Getting index of duplicate items in a list in c# - c#

I am looking for a way to get the index of all elements in a list from a keyword search within the list. So for example my list has:
Hello World
Programming Rocks
Hello
Hello World
I love C#
Hello
Now from this list of strings, i want to get all the indices of elements that say Hello World. I have tried the following but it only returns the first index it finds that has my search criteria:
for (int i = 0; i< searchInList.Count; i++)
foundHelloWorld[i] = searchInList.IndexOf("Hello World");
Anyone know of a way to do this?
Thanks

searchInList.Select((value, index) => new {value, index})
.Where(a => string.Equals(a.value, "Hello World"))
.Select(a => a.index)
If you're trying to search for more than just "Hello World", you could do
searchInList.Select((value, index) => new {value, index})
.Where(a => stringsToSearchFor.Any(s => string.Equals(a.value, s)))
.Select(a => a.index)

Since you know you're looking for ALL occurrences and therefore you must traverse the entire list anyway, you'll gain a lot of readability over using IndexOf by simply examining each element yourself:
var i=0;
foreach(var value in searchInList)
{
if(value == "Hello World")
foundHelloWorld.Add(i); //foundHelloWorld must be an IList
i++;
}
You can also use an overload of the Linq Select method that incorporate's the element's index in the source collection; this should be highly readable (and thus maintainable) to Linq-experienced programmers:
foundHelloWorld = searchInList
.Select((v,i)=>new {Index = i, Value = v})
.Where(x=>x.Value == "Hello World")
.Select(x=>x.Index)
.ToList();
The above code takes the list and transforms the string into a simple anonymous type incorporating each item's place in the original list. Then, it filters down to only matching elements, and then it projects out the Index (which didn't change through the filtering) into a new List object. However, all this transformation will make this solution perform slower, because this statement will traverse the entire list multiple times.

A little ugly but will work:
var searchInList = new List<string>();
//Populate your list
string stringToLookUp= "Hello world";
var foundHelloWorldIndexes = new List<int>();
for (int i = 0; i < searchInList.Count; i++)
if (searchInList[i].Equals(stringToLookUp))
foundHelloWorldIndexes.Add(i);

The FindAll method for lists is here. The list extension method Where is here.
Both of these will do pretty much exactly what you want and they are pretty easy to use. There are abundant examples of each on the internet but if you need help using them just let me know.

Related

LINQ Compare two lists where property value is not equal

I have been over a few StackOverflow articles about this (this in particular)
and for some reason my case is different. I've used Tony the Lion's answer to attempt to get a list of objects that have different property values, without success. This, however does work:
List<Task> changedTasksWorking = new List<Task>();
for (int x = 0; x < originalTaskList.Count; x++)
{
if (originalTaskList[x].ActiveFlag != newTaskList[x].ActiveFlag)
{
changedTasksWorking.Add(newTaskList[x]);
}
}
The following is what I thought would provide me the same result. But where the returned list should equal 1, it instead equals zero. When I flip the property comparison to != and remove the nor condition on the inner list, I get ALL the objects of the list instead:
List<Task> notWork = oL.Where(o => newL.Any(n => o.ActiveFlag != n.ActiveFlag)).ToList();
I feel like I'm taking crazy pills. Looking at the above one-liner that should give me what I'm asking for. Perhaps I have misunderstood how the LINQ methods Where and Any are interacting.
Your proposed LINQ approach is completely different from what you seem to actually be trying to do. In particular, according to your original example, you have two lists that are exactly in sync with each other. I.e. they have the same number of elements, and each element from one list corresponds exactly to the same element in the same position in the other list.
Your LINQ code, on the other hand, looks at each element in one list at a time, and for each of those elements, searches the other list for one that has a property value that doesn't match. In other words, if the newL list has elements of all possible values of ActiveFlag then of course it will return all elements of oL, because for each element in oL, LINQ is able to find an element in newL where the property value doesn't match.
There are at least a couple of obvious alternatives using LINQ that will actually work:
Use the overload for Where() that passes the index to the predicate delegate:
List<Task> changedTasks = newTaskList
.Where((n, i) => n.ActiveFlag != originalTaskList[i].ActiveFlag).ToList();
Use Enumerable.Zip() to pair up elements in a new sequence and filter that:
List<Task> changedTasks = originalTaskList
.Zip(newTaskList, (o, n) => o.ActiveFlag != n.ActiveFlag ? n : null)
.Where(n => n != null).ToList();
Either of those should work fine.

convert the for loop with index using LINQ

I am new to C# language, I have started learning LINQ in that
So I just want to convert the code using linq. Is there any way to do. The current implementation is not a stylish one.
var list = new List<int>();
for (int index = 0; index < contentList.Count; index++)
{
list.Add(MyClass.GetCorrespondence(module, index));
}
return list;
You could write it like this:
var list = contentList.Select((_, i) => MyClass.GetCorrespondence(module, i)).ToList();
or like this
var list = Enumerable.Range(0,contentList.Count).Select(i => MyClass.GetCorrespondence(module, i)).ToList();
But, honestly, dont do either! Your code is perfectly readable as it is.
If you must use LINQ for this then you can use the overload for Select which "projects each element of a sequence into a new form by incorporating the element's index." e.g:
list.AddRange(contentList.Select((c, index) => MyClass.GetCorrespondence(c, index));
https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.select?view=netframework-4.8
this method work:
var result= contentList.Select((paramter,index)=>MyClass.GetCorrespondence(module,index)).ToList();
If you're desperate to do it with Linq then you could try:
list.AddRange(Enumerable.Range(0, contentList.Count).Select(index => MyClass.GetCorrespondence(module, index)))
or:
list = Enumerable.Range(0, contentList.Count).Select(index => MyClass.GetCorrespondence(module, index)).ToList();
You could also use the ForEach LINQ statement.
contentList.ForEach(x => list.Add(MyClass.GetCorrespondence(module, x)));
EDIT: Any reason why this was down voted?

Linq Select Where IN

Cannot find the lambda linq equivalent to SELECT * FROM [Source] WHERE [Field] IN [String Array]. I need to select all data from a data table that contains zip codes from a string array. I would like a faster way than just iterating through every row comparing them as I believe this would be fairly inefficient (or I believe it would anyway). I can't seem to find an adequate answer on Google of how to perform a lambda LINQ IN query on a data table. Any assistance would be great! Here is what I have currently:
List<string> lst = dtEtechZipCodeEmailRecipients.AsEnumerable()
.Select(o => o.Field<string>("Email")).Distinct().ToList();
for (int i = 0; i < lst.Count - 1; ++i)
{
string email = lst[i].ToString().ToUpper();
string[] zipCodes = dtEtechZipCodeEmailRecipients.AsEnumerable()
.Where(zip => (zip.Field<string>("Email") ?? (object)String.Empty).ToString().ToUpper() == email)
.Select(zip => zip.Field<string>("ZipCode")).ToArray();
Console.WriteLine(" - " + email);
dtEtechModelRequests.AsEnumerable().Where(mod => mod.Field<string>("ZipCode").Contains(zipCodes)).Select(mod => mod);
}
That does not work, everything but the .Contains does do exactly what I need though. I left the .Contains to try and demonstrate my point.
You should do opposite thing - check whether array of zip codes contains current zip code:
Where(mod => zipCodes.Contains(mod.Field<string>("ZipCode"))
That is same as verifying if current zip code IN array.
Simple answer to your question is,
[Source].Where(el => [String Array].Contains(el.Field<string>("Field"))
And if you need NOT IN then follow the following pattern
Adding ! infront of your [String Array]
[Source].Where(el => ![String Array].Contains(el.Field<string>("Field"))
alternative representation
[Source].Where(el => [String Array].Contains(el.Field<string>("Field")==false)

how to use linq to retrieve values from a 2 dimensional generic list

I have a generic List List[int, myClass], and I would like to find the smallest int value, and retrieve the items from the list that match this.
I am generating this from another LINQ statement
var traysWithExtraAisles = (from t in poolTrays
where t.TrayItems.Select(i=>i.Aisle)
.Any(a=> ! selectedAisles.Contains(a))
select new
{
count= t.TrayItems.Select(i=>i.Aisle)
.Count(a=> !selectedAisles.Contains(a)),
tray=t
}).ToList();
this gives me my anonymous List of [count, Tray], but now I want to figure out the smallest count, and return a sublist for all the counts that match this.
Can anyone help me out with this?
var smallestGroup = traysWithExtraAisles
.GroupBy(x => x.count)
.OrderBy(g => g.Key)
.First();
foreach(var x in smallestGroup)
{
var poolTray = x.tray;
}
You can use SelectMany to "flatten" your list. Meaning, combine all of the lists into one, then take the Min. So;
int minimum = poolTrays.SelectMany(x => x).Min(x => x.TheIntegerIWantMinOf);
Will give you the smallest value contained in the sub lists. I'm not entirely sure this is what you're asking for but if your goal is simply to find the smallest element in the collection then I would scrap the code you posted and use this instead.
Right, I now realise this is actually incredibly easy to do with a bit more fiddling around. I have gone with
int minCount = traysWithExtraAisles.Min(x=>x.count);
var minAislesList = (from t in trayswithExtraAisles
where t.count==mincount
select t).ToList()
I imagine it is probably possible to do this in one statement
You can use GroupBy as answered by Tim... or OrderBy as follow:
var result = traysWithExtraAisles.OrderBy(x=>x.count)
.TakeWhile((x,i)=> i == 0 || x.count == traysWithExtraAisles[i-1]).count;

How to search for any text in List<String>

I have list and I need to search for items something like:
if the user searches for smi it will bring all items that include smi?
Any idea?
Check the following example
string serachKeyword ="o";
List<string> states = new List<string>();
states.Add("Frederick");
states.Add("Germantown");
states.Add("Arlington");
states.Add("Burbank");
states.Add("Newton");
states.Add("Watertown");
states.Add("Pasadena");
states.Add("Maryland");
states.Add("Virginia");
states.Add("California");
states.Add("Nevada");
states.Add("Ohio");
List<string> searchResults = states.FindAll(s => s.Contains(serachKeyword));
This will find all results that start with 'smi' (mySearchString)
foreach(var result in myList.Where(s => s.IndexOf(mySearchString) == 0))
{
// Do whatever
}
This will find any that contains 'smi' (mySearchString)
foreach(var result in myList.Where(s => s.IndexOf(mySearchString,StringComparison.InvariantCultureIgnoreCase) != -1))
{
// Do whatever
}
This will search for your text (ignoring case), and return any strings that contain the text.
that should work - Don't have IDE close by, but hope it helps
To search items that Include 'smi'
var result = list.Where(s => s.Contains("smi"));
If you want to grab all of the items that contain "smi" anywhere, like "smith" and "vesmit":
var list = myList.Where(m => m.Contains("smi"));
If you want to grab all of the items that contain "smi" at the start of the string, like "smith", "smitten", and "smile":
var list = myList.Where(m => m.BeginsWith("smi"));
If you want more flexibility, you can use a Regex
var list = myList.Where(m => Regex.IsMatch(m, "regexPattern"));
If you use a version of C# without LINQ you can use the Find method of the List as described here (it's got quite big sample on that page too).

Categories

Resources