Use LINQ to search items in a SortedDictionary - c#

I have a SortedDictionary of the type:
SortedDictionary<PriorityType, List<T>> dictionary;
where PriorityType is an Enum class, and the List contains various string values.
I want to use LINQ to search for the string items in the list, that have an even length.
As in:
IEnumerable<T> filteredList = new List<T>();
// Stores items in list whose string length is even
filteredList = //LINQ code;
I have tried a lot of implementations of LINQ but, it seems tough to traverse a List in a SortedDictionary using LINQ (taking into account I'm relatively new to LINQ).
Please help me with the LINQ code. Thanks!

If I understand you correctly, then you need items from lists which have even count of items:
filteredList = dictionary.Select(kvp => kvp.Value)
.Where(l => l != null && l.Count % 2 == 0)
.SelectMany(l => l)
.ToList();
UPDATE: If you want to select strings with even length, then you should use List<string> instead of generic list of T:
SortedDictionary<PriorityType, List<string>> dictionary;
filteredList = dictionary.SelectMany(kvp => kvp.Value)
.Where(s => s.ToString().Length % 2 == 0)
.ToList();

The solution provided by #Sergey is correct & in conformance to my requirements.
Also I found another easy solution using the select statement.
filteredList = from list in dictionary.Values from item in list where item.ToString().Length % 2 == 0 select item;
Hope this helps!

Related

Remove Duplicate item from datatable that starts with alphabet

I'm trying to remove duplicate data from datatable but not just keeping the first data entry and removed the second duplicate entry onward. I need to set a condition such that it will be able to removed the incorrect entry.
For example:
ID Value
111 A
222 B
333 C
444 A
I want to remove 111 data and keep 444 because they have duplicate data A. The other solution I found will remove 444 instead.
The closest thing I can find that relates to my question is this.
Remove Duplicate item from list based on condition
The answer is using linq, which I'm not familiar with. I was thinking to use "StartsWith" to filter the correct data I want and I have no idea how to implement into it.
var result = items
.GroupBy(item => item.Name)
.SelectMany(g => g.Count() > 1 ? g.Where(x => x.Price != 500) : g); <-- I want to apply StartsWith here
Really appreciate if someone could help me with this.
I think you need something like
var result = items
.GroupBy(item => item.Name)
.SelectMany(g =>
{
if (g.Count() > 1 && g.Key == "A") //g.Key.StartsWith("A")
return g;
});
This will return u an array where will be all "A" elements and then u could decide which u'd like to delete
To delete all duplicates and leave only the last inserted element:
var result = items
.GroupBy(item => item.Name)
.SelectMany(g =>
{
if (g.Count() > 1)
{
var mainElement = g.OrderByDescending(x => x.ID).First();
return g.Where(x => x.ID != mainElement.ID).ToArray();
}
});
You forgot to say why you want to keep item 444 and not item 111 instead of the other way around.
LINQ is developed to query data. LINQ will never change the original source sequence.
You can use LINQ to query the items that you want to remove, and then use a foreach to remove the items one by one.
To query the items with duplicates is easy. If you need this function more often, consider creating an extension function for this:
static IEnumerable<IGrouping<TSource, TKey>> GetDuplicates<TSource>(
this IEnumerable<TSource> source,
Func<TSource, TKey> propertySelector)
{
// TODO: check source and propertySelector not null
// make groups of source items that have the same value for property:
return source.GroupBy(item => propertySelector(item))
// keep only the groups that have more than one element
// it would be a waste to Coun(), just stop after counting more than one
.Where(group => group.Skip(1).Any());
}
This will give you groups of all source items that have duplicate values for the selected property.
In your case:
var itemsWithDuplicateValues = mySourceItems.GetDuplicates(item => item.Value);
This will give you all your source items that have duplicate values for item.Value, grouped by same item.Value
Now that you've got time to find out why you want to keep item with Id 444 and not 111, you can write a function that takes a group of duplicates and returns the elements that you want to remove.
static IEnumerable<TSource> SelectItemsIWantToRemove<TSource>(
IEnumerable<TSource> source)
{
// TODO: check source not null
// select the items that you want to remove:
foreach (var item in source)
{
if (I want to remove this item)
yield return item;
}
// TODO: make sure there is always one item that you want to keep
// or decide what to do if there isn't any item that you want to keep
}
Now that you've got a function that selects the items that you want to remove it is easy to create a LINQ that will select from your sequence of duplicates the item that you want to remove:
static IEnumerable<TSource> WhereIWantToRemove<TSource>(
this IEnumerable<IGrouping<TSource>> duplicateGroups)
{
foreach (var group in duplicateGroups)
{
foreach (var sourceItem in group.WhereIWantToRemove())
{
yield return sourceItem;
}
}
}
You could also use a SelectMany for this.
Now put everything together:
static IEnumerable<TSource> WhereIWantToRemove<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> propertySelector)
{
return source.GetDuplicates(propertySelector)
.WhereIWantToRemove();
}
Usage:
var itemsToRemove = mySourceItems.WhereIWantToRemove(item => item.Value);
You can see that I chose to create several fairly small and easy to understand extension functions. Of course you can put them all together in one big LINQ statement. However, I'm not sure if you can convince your project leader that this would make your code better readable, testable, maintainable and re-usable. So my advice would be to stick to the small extension functions.
You can group the DataRows by Value and then select all the rows that don't match your conditions, and then delete all those rows:
var result = items.AsEnumerable()
.GroupBy(item => item.Field<string>("Value"))
.Where(g => g.Count() > 1)
.SelectMany(g => g.Where(x => !x.Field<string>("ID").StartsWith("4")));
foreach (var r in result) {
r.Delete();
}

How Enumerable.Zip works with LinQ

I would like to Zip multiple List of integer and don't know how to do that in LinQ.
Here's my List :
List<KeyValuePair<Guid, List<int>>> totals = Totals.Where(x => x.Key == myGuid).ToList();
//where Totals is a List<KeyValuePair<Guid, List<int>>>
List<List<int>> totalsValue = totals.Select(s => s.Value).ToList();
//I want to Zip all my List<int> in totalsValue and put it into listToReturn
List<int> listToReturn = new List<int>();
I want something like that : http://linqsamples.com/linq-to-objects/other/Zip-lambda-csharp but in this exemple, lists are separeted.
Mine is a List>
Where listToReturn represents the list to return.
Can someone help me ?
Result :
Where adventest is a List and agiltest also
enter image description here
I think you are looking for SelectMany:
List<int> listToReturn= totals = Totals.Where(x => x.Key == myGuid)
.SelectMany(s => s.Value)
.ToList();
What you are trying to do is flatten all the lists into one sequence and that is what SelectMany extension method do. Zip, in the other side, merges each element of the first sequence with an element that has the same index in the second sequence, in other words, it allows you to project from two sequences of the same length.
Update
Now I understand what you are trying to achieve:
var lists= Totals.Where(x => x.Key == myGuid).Select(e=>Value).ToList();// Select the lists
if(lists.Count>0)
var result= Enumerable.Range(0, lists[0].Count).Select(i=> lists.Sum(l=>l[i]));
The thing with this solution you need to make sure that all the selected lists have the same size. I'm pretty sure there is a more elegant solution but this was that come to my mind now. If I think in something better I will update my answer

Linq query in C# to retrieve the set of elements with respect to list

We have the list A which contain the random indexes in it. I have another list B which contains the class objects in it. I want to parse list B with the indexes present in list A and find the objects which have name Amar in it using Linq.
For example:
List<int> Indexes = new List<int>(); // This contains the random indexes
List<Student> StuObj = new List<Student>();
Class Student
{
String name;
}
Now I want to parse the list StuObj with the respect to the indexes present in the list Indexes and get the Student object indexes present in the list StuObj where the name is Amar.
You can do that using Linq. The Where has an overload that provides the index of the element:
List<int> indexes = new List<int>() { 5, 1 , 10, 30 };
var results = listB.Where((item, index)=> indexes.Contains(index)
&& item.Name == "Amar")
.Select(x => listB.IndexOf(x)).To‌​List();
Edit: to get the index of the element in the original listB, you can make use of the IndexOf(T) method.
This should work:
var result = Indexes
.Select(i => StuObj[i])
.Where(s => s.name = "Amar").ToList()
It performs fast index lookup to fetch only required objects. If you know there is only one record, you can use First or FirstOrDefault instead of ToList.
Assuming you have two lists, List indexList and List dataList, you can use select as follows:
indexList.Select(i => dataList[i]);
You should consider what you wish to happen if indexList contains an integer < 0 or > the size of dataList. For example, you could replace invalid entries with null, like:
indexList.Select(i => i < 0 || i >= dataList.Count ? null : dataList[i]);
Or you could filter them out like:
indexList.Where(i => i>=0 && i < dataList.Count).Select(i => dataList[i]);
Or you may know via preconditions that you will never have items in the index list that are out of the range of valid values.
EDIT
Based on the updated question, try this:
dataList.Where((item, index) => indexList.Contains(index) && item.Name == "Amar")
.Select(item => dataList.IndexOf(item));
This takes advantage of the Select and Where overloads that take the index of the item. The Where clause selects the item where the item's index in dataList is in the indexList, and also where the item's Name is Amar. The Select clause then returns the index of the item in the original data list.
Something like this:
var result = listA
.Where(i => i >= 0 && i < listB.Count)
.Select(i => listB[i])
.FirstOrDefault(b => b.Name == "Amar");
Basically you use the value from listA as index of an element of the listB. If you are sure that listA contains only valid indexes, then Where call can be removed.
EDIT: As per updated question, the answer is even easier:
var result = listA
.Where(i => i >= 0 && i < listB.Count && listB[i].Name == "Amar")
.ToList();
I see absolutely no reason to use the linear searching (hence slow) methods Contains and IndexOf as suggested in some other answers.

Retrieving non-duplicates from 2 Collections using LINQ

Background: I have two Collections of different types of objects with different name properties (both strings). Objects in Collection1 have a field called Name, objects in Collection2 have a field called Field.
I needed to compare these 2 properties, and get items from Collection1 where there is not a match in Collection2 based on that string property (Collection1 will always have a greater or equal number of items. All items should have a matching item by Name/Field in Collection2 when finished).
The question: I've found answers using Lists and they have helped me a little(for what it's worth, I'm using Collections). I did find this answer which appears to be working for me, however I would like to convert what I've done from query syntax (if that's what it's called?) to a LINQ query. See below:
//Query for results. This code is what I'm specifically trying to convert.
var result = (from item in Collection1
where !Collection2.Any(x => x.ColumnName == item.FieldName)
select item).ToList();
//** Remove items in result from Collection1**
//...
I'm really not at all familiar with either syntax (working on it), but I think I generally understand what this is doing. I'm struggling trying to convert this to LINQ syntax though and I'd like to learn both of these options rather than some sort of nested loop.
End goal after I remove the query results from Collection1: Collection1.Count == Collection2 and the following is true for each item in the collection: ItemFromCollection1.Name == SomeItemFromCollection2.Field (if that makes sense...)
You can convert this to LINQ methods like this:
var result = Collection1.Where(item => !Collection2.Any(x => x.ColumnName == item.FieldName))
.ToList();
Your first query is the opposite of what you asked for. It's finding records that don't have an equivalent. The following will return all records in Collection1 where there is an equivalent:
var results=Collection1.Where(c1=>!Collection2.Any(c2=>c2.Field==c1.Name));
Please note that this isn't the fastest approach, especially if there is a large number of records in collection2. You can find ways of speeding it up through HashSets or Lookups.
if you want to get a list of non duplicate values to be retained then do the following.
List<string> listNonDup = new List<String>{"6","1","2","4","6","5","1"};
var singles = listNonDup.GroupBy(n => n)
.Where(g => g.Count() == 1)
.Select(g => g.Key).ToList();
Yields: 2, 4, 5
if you want a list of all the duplicate values then you can do the opposite
var duplicatesxx = listNonDup.GroupBy(s => s)
.SelectMany(g => g.Skip(1)).ToList();

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;

Categories

Resources