I need to perform a search in two different data structures in C#, and here's the deal:
I have one name (which is a string) and I want to perform a search. I have a function called Exists which will return a bool indicating whether it exists or not.
In case it exists, I increase the name (simply adding a 1 at the end of the string), and then I need to perform the search again (via method exists) to see if an object with the new name exists.
This would go on until there's an unused name, which I could use, BUT, in case it doesn't exist, now I should perform a search another data structure which contains the objects that were deleted, and if the string is found there, then I'd have to increase the name again, and start searching since the beginning.
This would all end in case there's no object with such name neither using Exists method nor in the data structure where all the deleted objects are.
How could I approach this problem?
I hope I expressed myself clearly :-)
Thanks a lot in advance!
string BuildNextName(string originalName)
{
string name = originalName;
while( Exists(name) || deletedNames.Contains(name))
{
name = Increment(name);
}
return name;
}
Or did I miss something?
Using a for loop:
string BuildNextName(string originalName)
{
for (string name=originalName;
Exists(name) || deletedNames.Contains(name);
name = Increment(name));
return name;
}
BTW, I guess your name incrementation algorithm is more complex than simply adding 1: name, name1, name2,... Basically, if the name doesn't end in a number, you append "1". If it does, you increment that number. right?
a non recursive and simple solution could be something like this ( I don't see any need of recursion in this case)
//pseudocode
String name;
bool condition = true;
while(condition)
{
if(ExistInFirstDataStructure(name))
{
//increment name
}
else
{
if(ExistInDeletedDataStructure(String name))
{
//increment name
}
else
{
condition = false;
}
}
}
bool ExistInFirstDataStructure(String name)
{
}
bool ExistInDeletedDataStructure(String name)
{
}
Why use a loop at all?? (I know LINQ will under the hood)
var LastUsedObjectName =
MyObjects.Select(mo => mo.Name)
.Union( MyDeletedObjects.Select(mo => mo.Name))
.OrderByDescending(name => /*Function to order by integer part of name*/).First();
// Now add 1 to LastUseObjectName and use that.
How about this one:
var listOfExistingNames = new List<string> { "MyName", "MyName1", "MyName3" };
var listOfDeletedNames = new List<string> { "MyName2", "MyName5" };
int counter = 0;
string baseToFindFreePlace = "MyName";
string newName = baseToFindFreePlace;
var allNames = listOfExistingNames.Concat(listOfDeletedNames);
while (allNames.Contains(newName))
{
counter++;
newName = baseToFindFreePlace + counter;
}
listOfExistingNames.Add(newName);
if you create Exists methods for both data structures, you can search with recursion like this:
pseudo code:
string resultName;
void Search(string name)
{
if(ExistsInFirstStructure(name)) //name is in first data structure
Search(name + "1"); //add 1 and try again
else
if(ExistsInSecondStructure(name)) //name exists in second data structure
Search(name + "1"); //perform search again
else
resultName = name; //current name wasn't found in first and second data structures - we have result
}
Related
this is quite the nooby questions, it's just that I havn't been doing this for a long time and would need some help.
So here is the problem. I have this debug info on my list "lstfriendlist":
I simply put up a breakpoint in my activity, then clicked on my list and saw, that all my "friends" are brought to me on this list under "friendUsername".
I was able to retrieve a certain username via:
string temp = lstfriendList[11].friendUsername.ToString();
This returns "torben" on my string "temp".
Now I just forgot how to use the foreach loop to retrieve all objects in order from my list and then write them down. I'm sorry to bother you with this, but I simply forgot :(
I hope you can help me.
Thank you :)
You already have declared a variable of the type "Friend" in the foreach loop's head. Now you can access the properties of the current object by typing
foreach (Friend f in lstfriendList)
{
string temp = f.friendUsername;
}
To complete Sebastian Hofmann answer, you can use .OrderBy or .OrderByDescending to order on name or username
foreach (Friend f in lstfriendList.OrderBy(list => list.friendUsername))
{
string temp = f.friendUsername;
}
Will return user name from a to z
foreach (Friend f in lstfriendList.OrderByDescending(list => list.friendUsername))
{
string temp = f.friendUsername;
}
Will return username z to a
I think there is no way to stuck there, since you are now having the Friend object(f) with you inside the loop, just place a . after f and see what intellisense suggests, Anyway It is pretty good if you modify the class like the following, with override ToString():
class Friend
{
public string friendUsername { get; set; }
public int friendId { get; set; }
// Add rest of properties here
public override string ToString()
{
return "ID :" + friendId + "\n Friend Name: " + friendUsername;
// Append rest of properties here
}
}
And then use like this:
foreach (Friend f in lstfriendList)
{
string friendDetails = f.ToString();
Console.WriteLine(friendDetails);
}
I'm really new to programming, so take this with a grain of salt.
I've made 2 arrays that correspond to eachother; One is a Name array and one is a Phone Number array. The idea is that the spot [1] in NameArray corresponds to spot [1] in the PhoneArray. In other words, I need to keep these 'pairings' in tact.
I'm trying to make a function that deletes one of the spots in the array, and shifts everything down one, as to fill the space left empty by the deleted element.
namearray = namearray.Where(f => f != iNum).ToArray();
is what I've tried, with iNum being the number corresponding to the element marked for deletion in the array.
I've also tried converting it to a list, removing the item, then array-ing it again.
var namelist = namearray.ToList();
var phonelist = phonearray.ToList();
namelist.Remove(txtName.Text);
phonelist.Remove(txtPhone.Text);
namearray = namelist.ToArray();
phonearray = phonelist.ToArray();
lbName.Items.Clear();
lbPhone.Items.Clear();
lbName.Items.AddRange(namearray);
lbPhone.Items.AddRange(phonearray);
with txtName.Text and txtPhone.Text being the strings for deletion in the corresponding list boxes.
Can someone suggest a better way to do it / What I'm doing wrong / How to fix?
Thanks guys
-Zack
A better way would be to have an array of a class that contains a Name and Phone Number object:
public class PersonData
{
public string Name { get; set; }
public string Phone { get; set; }
}
public PersonData[] data;
That way, instead of keeping two arrays in sync, it's one array with all the appropriate data.
Try a loop through both arrays, moving the values of each down an index each time.
Start the loop at the index of the value you want to delete. So you would find the IndexOf(T) the value you want, storing it as deleteIndex and run the loop starting from that index.
When you hit the end of the array, set the last value as null or string.Empty (depending what value type the array holds).
A bit like this:
var deleteIndex = namearray.IndexOf("TheStringYouWantToDelete");
for (int i = deleteIndex; i < namearray.Length; i++)
{
if (i == namearray.Length - 1) // The "last" item in the array.
{
namearray[i] = string.Empty; // Or null, or your chosen "empty" value.
phonearray[i] = string.Empty; // Or null, or your chosen "empty" value.
}
else
{
namearray[i] = namearray[i+1];
phonearray[i] = phonearray[i+1];
}
}
This will work for deleting and moving values 'down' in index.
You could also rewrite the code for moving them the other way, as it would work similarly.
Reordering them completely? Different ball game...
Hope this helps.
If the namearray and phonearray contain strings and you know the index of the element to remove (iNum) then you need to use the overload of the Where extension that takes a second parameter, the index of the current element in the evaluation
namearray = namearray.Where((x, y) => y != iNum).ToArray();
However the suggestion to use classes for your task is the correct one. Namearray and Phonearray (and whatever else you need to handle in future) are to be thought as properties of a Person class and instead of using arrays use a List<Person>
public class Person
{
public string FirstName {get; set;}
public string LastName {get; set;}
public string Phone {get; set;}
}
List<Person> people = new List<Person>()
{
{new Person() {FirstName="Steve", LastName="OHara", Phone="123456"}},
{new Person() {FirstName="Mark", LastName="Noname", Phone="789012"}}
};
In this scenarion removing an item knowing the LastName could be written as
people = people.Where(x => x.LastName != "OHara").ToList();
(or as before using the index in the list of the element to remove)
people = people.Where((x, y) => y != iNum).ToArray();
The other answers provide some better design suggestions, but if you're using ListBoxes and want to stick with arrays, you can do this to synchronize them:
int idx = lbName.Items.IndexOf(txtName.Text);
if (idx > -1)
{
lbName.Items.RemoveAt(idx);
lbPhone.Items.RemoveAt(idx);
}
namearray = lbName.Items.Cast<string>().ToArray<string>();
phonearray = lbPhone.Items.Cast<string>().ToArray<string>();
Use a dictionary instead.
Dictionary<string, string> phoneBook = new Dictionary<string, string>();
phoneBook["Foo"] = "1234567890";
phoneBook["Bar"] = "0987654321";
phoneBook.Remove("Bar");
I'm trying to parse out this single method which has over 8,000 lines of hardcoded data validation. Most of which is identical, duplicated logic for different fields in the data source.
A lot of it looks something like this (C++):
temp_str = _enrollment->Fields->FieldByName("ID")->AsString.SubString(1,2);
if (temp_str.IsEmpty())
{ /* do stuff */ }
else
{
if (!IsDigitsOnly(temp_str))
{ /* do different stuff */ }
else
{ /* do other stuff */ }
}
temp_str = _enrollment->Fields->FieldByName("OtherField");
if (temp_str.IsEmpty())
/* do more stuff */
So basically, I just need to parse out values between each pair of temp_str = ... and then get each unique validation "chunk".
The problem I'm having currently is identifying where each "chunk" begins and ends.
This is the code I have:
static void Main(string[] args)
{
string file = #"C:\somePathToFile.h";
string validationHeader = "temp_str = _enrollment->Fields->FieldByName(";
string outputHeader = "=====================================================";
int startOfNextValidation;
List<string> lines = File.ReadAllLines(file).ToList<string>();
List<string> validations = new List<string>();
while (lines.Contains(validationHeader))
{
//lines[0] should be the "beginning" temp_str assignment of the validation
//lines[startOfNextValidation] should be the next temp_str assignment
startOfNextValidation = lines.IndexOf(validationHeader, lines.IndexOf(validationHeader) + 1);
//add the lines within that range to another collection
// to be iterated over and written to a textfile later
validations.Add((lines[0] + lines[startOfNextValidation]).ToString());
//remove everything up to startOfNextValidation so we can eventually exit
lines.RemoveRange(0, startOfNextValidation);
}
StreamWriter sw = File.CreateText(#"C:\someOtherPathToFile.txt");
foreach (var v in validations.Distinct())
{
sw.WriteLine(v);
sw.WriteLine(outputHeader);
}
sw.Close();
}
My while statement is never hit and it just immediately jumps to the StreamWriter creation, which creates an empty text file since validations is empty.
So I guess my first question is, how do you loop over a List while checking to make sure there are still items containing a particular "subvalue" within those items?
Edit:
I tried this as well;
while (lines.Where(stringToCheck => stringToCheck.Contains(validationHeader)))
Per this answer; https://stackoverflow.com/a/18767402/1189566
But it says it can't convert from a string to a bool?
You mean something like this?
while (lines.Any(x => x.Contains(validationHeader)))
This checks whether the validationHeader is a part of any string in the list.
I tried this as well;
while (lines.Where(stringToCheck => stringToCheck.Contains(validationHeader)))
This won't work, because LINQ's Where method will return an IEnumerable<string> in your case. And the while loop needs a boolean predicate. An IEnumerable cannot be true or false, thus the compiler is complaining.
simple one to check if it has the needed value or not
foreach (var setting in FullList)
{
if(cleanList.Exists(x => x.ProcedureName == setting.ProcedureName))
setting.IsActive = true; // do you business logic here
else
setting.IsActive = false;
updateList.Add(setting);
}
Below is a snippet of code i am trying to use to search an arraylist for. I have used code very similar to this in a different form and it has worked. However here it has not. It is meant to search for the Customer Number within the Arraylist on frmMain, it then searches for that customers Account Number typed in by the user. But for some reason it is almost as if it skips the second if statement and go to the error message at the bottom.
The Arraylist is on the frmMain, then there are classes for Customer Account and Transaction. In Customer there is another ArrayList storing the Accounts, then in the Account Class there is an ArrayList storing the transactions of the customer account.
foreach (Customer a in frmMain.bankDetails)
{
if (a.getCustomerNumber().ToUpper().Equals(custSearch))
{
foreach (Account b in a.Accounts)
{
if (b.getAccNumber().Equals(searchString))
{
txtSearch.BackColor = Color.PaleGreen;
txtAccSortCode.Text = b.getAccSort();
txtAccNumber.Text = Convert.ToString(b.getAccNumber());
txtAccNickName.Text = b.getAccNick();
txtAccDate.Text = b.getAccDate();
txtAccCurBal.Text = Convert.ToString(b.getAccCurBal());
txtAccOverDraft.Text = Convert.ToString(b.getAccOverDraft());
txtNumTrans.Text = Convert.ToString(b.getAccNumTrans());
found = true;
break;
}
}
}
}
Any help appreciated!
If b.getAccNumber() is returning an int and searchString is a string, then it will always return false. For example:
int int_four = 4;
string string_four = "4";
bool eq1 = int_four.Equals(string_four); // false
bool eq2 = int_four.ToString().Equals(string_four); // true
is the "getCustomerNumber" property an actual number (an integer) or not? Which I think its not, because you else wouldn`t use ToUpper() method. There are letter as well (I think).
Anyway, would you mind showing me your Customer class? And paste some customer number example.
Mitja
I am programming a program to search the name from the list and I need to find them even if the keyword is not in front of the names (that's what I mean non-prefix)
e.g. if I my list is the music instruments and I type "guit" to the search textbox.
It should find the names "Guitar, GuitarrĂ³n, Acoustic Guitar, Bass Guitar, ..."
or something like this Longdo Dictionary's search suggestion.
here is my simple and stupid algorithm (that's all I can do)
const int SEARCHROWLIMIT = 30;
private string[] DoSearch(string Input, string[] ListToSearch)
{
List<string> FoundNames = new List<string>();
int max = 0;
bool over = false;
for (int k = 0; !over; k++)
{
foreach (string item in ListToSearch)
{
max = (max > item.Length) ? max : item.Length;
if (k > item.Length) continue;
if (k >= max) { over = true; break; }
if (!Input.Equals("Search")
&& item.Substring(k, item.Length - k).StartsWith(Input, StringComparison.OrdinalIgnoreCase))
{
bool exist = false;
int i = 0;
while (!exist && i < FoundNames.Count)
{
if (item.Equals(FoundNames[i]))
{
exist = true;
break;
}
i++;
}
if (!exist && FoundNames.Count < SEARCHROWLIMIT)
FoundNames.Add(item);
else if (FoundNames.Count >= SEARCHROWLIMIT) over = true;
}
}
}
return FoundNames.ToArray();
}
I think this algorithm is too slow for a large number of names and after several trial-and-error, I decided to add SEARCHROWLIMIT to breaks the operation
And I also think there're some readymade methods that can do that.
And another problem is I need to search music instruments by a category like strings, percussions, ... and by the country of origins. So I need to search them with filter by type and country.
How can I achieve this?
Using LINQ you could write code like this:
var resultSet = products
// filter products by category
.Where(product => product.Category == "strings")
// filter products by origin
.Where(product => product.Origin == "italy")
// filter products whose name contains a word starting with "guit"
.Where(product => (" " + product.Name).Contains(" guit"))
// limit the result set to the first 30 matching products
.Take(30);
If your sets of products is reasonably small, you can use LINQ-to-Objects. Otherwise you should use a database and have a look at LINQ-to-SQL.
One word. Database!
Seriously, if you want to do all these different searches, consider placing your data into a database with a schema that simplifies the categorization issues you are having. Sql Server Express now supports full text search which would be very useful for the kind of search you are trying to perform.
There's a nice blog post here about using FTS with Linq-to-Sql.
static List<string> GetItemsWithWordsStartingWithSubstring(List<string> list, string substring)
{
var query = from str in list
from item in str.Split(' ')
where item.StartsWith(substring, StringComparison.InvariantCultureIgnoreCase)
select str;
return query.ToList();
}
I hope I have read your intiial question properly. This function will return any item from the list that contains a word starting with your substring. More punctuation could be added to the split parameters. Given a list with the following contents:
"abcdef","defabc","def abc","xyz"
A search on "abc" will find "abcdef" and "def abc", but not "defabc".