need help to only select/get distinct entries based on i.Code.
There are duplicates and thus I'm getting an error in my expression "An item with the same key has already been added."
var myDictionary = dbContext.myDbTable
.Where(i => i.shoesize>= 4)
.OrderBy(i => i.Code)
.ToDictionary(i => i.Code, i => i);
Have tried to use Select and/or Distinct in different combinations and also by themselves but am still getting the same error
var myDictionary= dbContext.myDbTable
.Where(i => i.shoesize>= 4)
.OrderBy(i => i.Code)
//.Select(i => i)
//.Distinct()
.ToDictionary(i => i.Code, i => i);
Can anybody help? C#
UPDATE: If there are multiple objects with the same code I only want to add the first object(with that particular code) to myDictionary.
You can group by Code and select the first item from each group (which is equivalent to distinct):
var myDictionary = dbContext.myDbTable
.Where(i => i.shoesize >= 4) // filter
.GroupBy(x => x.Code) // group by Code
.Select(g => g.First()) // select 1st item from each group
.ToDictionary(i => i.Code, i => i);
You don't need the OrderBy since Dictionarys represent an unordered collection. If you need an ordered dictionary you could use SortedDictionary.
It sounds to me that what you are looking for is .DistinctBy() (available in .NET 6), which lets you specify which property to distinct the elements in your collection by:
var myDictionary= dbContext.myDbTable
.Where(i => i.shoesize>= 4)
.DistinctBy(i => i.Code)
.ToDictionary(i => i.Code, i => i);
By dividing it and creating a list first it worked as compared to when it was all bundled up into one linq, guess the First() needed it to be in a list before being able to make it into a dict.
var firstLinq = dbContext.myDbTable
.Where(i => i.shoesize>= 4)
.ToList();
then
var finalLinq = fromConcurWithDuplicates
.GroupBy(i => i.Code)
.Select(i => i.First())
.ToList()
.ToDictionary(i => i.Code, i => i);
Related
Is there a elegant way of doing following in LINQ or should I write an extension for this
i have a list of objects that need to be grouped by startdate
lets say
09.00,
13.00,
13.00,
13.00,
15.00,
var groupedStartDates = startdate.groupby(x => x.StartDate);
I need to have maximum size of group to be 2.
Expected result is
var groupedStartDates = startDate.GroupBy(x => x.StartDate);
List
list1 {09.00}
list2 {13.00; 13.00}
list3 {13.00}
list4 {15.00}
After the initial grouping you can then group by the index (in the groups) divided by 2 to do a further grouping, then use SelectMany to flatten that back out.
var result = startDate.GroupBy(x => x.StartDate)
.SelectMany(grp => grp.Select((x,i) => new{x,i})
.GroupBy(a => a.i / 2)
.Select(sgrp => sgrp.Select(a => a.x)));
Here's a break down of what's going on. Note curly brackets will represent collections and square will represent object with multiple properties.
Initial data
09.00, 13.00, 13.00, 13.00, 15.00
After GroupBy(x => x.StartDate)
[Key:09.00, {09.00}], [Key:13.00, {13.00, 13.00, 13.00}], [Key:15.00, {15.00}]
Now it's going to operate on each group, but I'll show the results for all of them at each step.
After the Select((x,i) => new{x,i})
{[x:09.00, i:0]}, {[x:13.00, i:0], [x:13.00, i:1], [x:13.00, i:2]}, {[x:15.00, i:0]}
After the GroupBy(a => a.i / 2)
{[Key:0, {[x:09.00, i:0]}]}, {[Key:0, {[x:13.00, i:0], [x:13.00, i:1]}], [Key:1, {[x:13.00, i:2]}}, {[Key:0, {[x:15.00, i:0]}}
After the .Select(sgrp => sgrp.Select(a => a.x))
{{09.00}}, {{13.00, 13.00}, {13.00}}, {{15.00}}
And finally the SelectMany will flatten that to.
{09.00}, {13.00, 13.00}, {13.00}, {15.00}
Note that each line represents a collection, but I didn't put curly braces around them as I felt it made it even harder to read.
Or with an extension method
public static IEnumerable<IEnumerable<T>> Bin<T>(this IEnumerable<T> items, int binSize)
{
return items
.Select((x,i) => new{x,i})
.GroupBy(a => a.i / binSize)
.Select(grp => grp.Select(a => a.x));
}
You can make it a little nicer.
var result = startDate
.GroupBy(x => x.StartDate)
.SelectMany(grp => grp.Bin(2));
Update: As of .Net 6 they have added the new Linq method Chuck that does the same thing as my Bin method above. So now you can do
var result = startDate
.GroupBy(x => x.StartDate)
.SelectMany(grp => grp.Chunk(2));
If I understand your question correctly, you can use Take:
var result= startDate.GroupBy(x => x.StartDate)
.Select(x => x.Take(2))
.ToList();
Each group will contains at most 2 members and additional items of groups will not return.
It is easy to select the first of each group:
var firstOfEachGroup = dbContext.Measurements
.OrderByDescending(m => m.MeasurementId)
.GroupBy(m => new { m.SomeColumn })
.Where(g => g.Count() > 1)
.Select(g => g.First());
But...
Question: how can I select all from each group except the first item?
var everythingButFirstOfEachGroup = dbContext.Measurements
.OrderByDescending(m => m.MeasurementId)
.GroupBy(m => new { m.SomeColumn })
.Where(g => g.Count() > 1)
.Select( ...? );
Additional information:
My real goal is to delete all duplicates except the last (in a bulk way, ie: not using an in-memory foreach), so after the previous query I want to use RemoveRange:
dbContext.Measurements.RemoveRange(everythingButFirstOfEachGroup);
So, if my question has no sense, this information might be handy.
Use Skip(1) to skip the first record and select the rest.
Something like:
var firstOfEachGroup = dbContext.Measurements
.OrderByDescending(m => m.MeasurementId)
.GroupBy(m => new { m.SomeColumn })
.Where(g => g.Count() > 1)
.SelectMany(g => g.OrderByDescending(r => r.SomeColumn).Skip(1));
See: Enumerable.Skip
If you do not need a flattened collection then replace SelectMany with Select in code snippet.
IGrouping<K, V> implements IEnumerable<V>; you simply need to skip inside the select clause to apply it to each group:
.Select(g => g.Skip(1))
You can always use .Distinct() to remove duplicates; presumably sorting or reverse-sorting and then applying .distinct() would give you what you want.
I got a LINQ query with Entity Framework (EF) and getting a list of items. Now I want to create a dictionary with the incrementing index of the item and the item itself.
I have it like this:
result = context
.Items
.Where(b => !b.Deleted)
.OrderBy(b => b.Name)
.ToDictionary(COUNTHERE, b => b.Name)
So the dictionary have to look like this:
1: "item1"
2: "item2"
3: "item5"
4: "item10"
5: "item100"
I think what you need is to have the item name as the key instead the count as the key, because if there is two items that have the same count, it will throw exception that the key has been added.
Then you can Use GroupBy before ToDictionary so that you can count it.
result = context
.Items
.Where(b => !b.Deleted)
.GroupBy(x => x.Name)
.ToDictionary(g => g.Key, g => g.Count())
.OrderBy(g => g.Key);
based on your updated comment, then what you need is
result = context
.Items
.Where(b => !b.Deleted)
.OrderBy(b => b.Name)
.AsEnumerable()
.Select((v,i) => new { i, v.Name })
.ToDictionary(g => g.i + 1, g => g.Name);
Note that you need to add AsEnumerable so that the Select clause works as linq to object (Select that accept index is not supported in L2S).
Just use:
int count = 0;
var result = context
.Items
.Where(b => !b.Deleted)
.OrderBy(b => b.Name)
.ToDictionary(b => ++count, b => b.Name);
An alternative way of achieving this is:
var sortedNames = context
.Items
.Where(b => !b.Deleted)
.Select(b => b.Name)
.OrderBy(b => b.Name)
.ToArray();
result = Enumerable.Range(1, sortedNames.Length)
.ToDictionary(i => i, i => sortedNames[i - 1]);
To get zero-based numbering, use Enumerable.Range(0, sortedNames.Length) and sortedNames[i] instead.
I have the following LINQ query to receive indexes:
fieldIndexes = this.record.Fields.Where(a => !a.IsCodeField)
.OrderBy(a => a.DatabaseIndex)
.Select(a => a.DatabaseIndex - 1)
.ToArray();
But I want to replace the a.DatabaseIndex with the actual index of the search. I am aware of the syntax .Select((a, index) => new (index, a))... but I am not sure how to cast the a here to be of my type which in this case is Field. I have tried:
fieldIndexes = this.record.Fields.Select((a, index) => new {index, a})
.Where(a => !a.IsCodeField) // <- Invalid Cast.
.OrderBy(a => a.DatabaseIndex)
.Select(a => a.DatabaseIndex - 1)
.ToArray();
How can I cast a to my type within the LINQ statement?
Thanks for your time.
In the Where clause you are working with your newly created anonymous objects with properties a and index, which you can use:
.Where(a => !a.a.IsCodeField)
Of course this can be done in more readable fasion:
fieldIndexes = this.record.Fields.Select((a, index) => new {Index = index, Field = a})
.Where(a => !a.Field.IsCodeField)
...
You are projecting sequence items to anonymous objects with properties index and a. Original item will be accessible via property a:
fieldIndexes = this.record.Fields.Select((a, index) => new {index, a})
.Where(x => !x.a.IsCodeField)
I need to delete a specific item from a dictonary..
The dictonary is like
dict["Key1"]="Value1"
dict["Key2"]="Value2"
dict["Key3"]="Value3"
dict["Key4"]="Value2"
How to delete the item if another item has the same value using LINQ
Thanks in advance
Here my tested solution:
dict.GroupBy(x => x.Value, x => x.Key)
.Where(x => x.Count() > 1)
.SelectMany(x => x.Skip(1))
.ToList().ForEach(x => dict.Remove(x))
check orginal answer by #Jon Skeet : C#: Remove duplicate values from dictionary?
var uniqueValues = myDict.GroupBy(pair => pair.Value)
.Select(group => group.First())
.ToDictionary(pair => pair.Key, pair => pair.Value);
var dupKeys = dict.GroupBy(innerD => innerD.Value)
.Where(mergedByValue => mergedByValue.Count() > 1)
.Select(mergedByValue => mergedByValue.OrderByDescending(m => m.Key).First().Key);
dict.Where(d => dupKeys.Contains(d.Key)).ToList()
.ForEach(d => dict.Remove(d.Key));
This assumes you want the last duplicate removed, where last is defined as the last ordinal string value.
If you want all duplicates removed, change your dupKeys to this:
var dupKeys = dict.GroupBy(innerD => innerD.Value)
.Where(mergedByValue => mergedByValue.Count() > 1).Dump()
.SelectMany(mergedByValue => mergedByValue.Select(m => m.Key));
You need to use Distinct.