public class Item
{
public int Id {get; set;}
public bool Selected {get; set;}
}
List<Item> itemList = new List<Item>(){ /* fill with items */ };
I need to create a list of Items that meet the following criteria. From itemList, I need to group the items by Id and then choose a single item from each group. The chosen item must be one in which Selected == true. If no item in the group is selected, then any item can be chosen, it doesn't matter, but one must be chosen.
Based on this question: How to get distinct instance from a list by Lambda or LINQ
I was able to put together the following, which seems to work:
var distinctList = itemList.GroupBy(x => x.Id,
(key, group) => group.Any(x => x.Selected) ?
group.First(x => x.Selected) : group.First());
Is there a more efficient or simpler way to achieve this? I tried FirstOrDefault() but couldn't seem to make it do what I needed. My concern with the efficiency in the above code, is the call to Any().
You can indeed use the FirstOrDefault extension method, but use the version that takes a predicate, and combine it with the coalesce operator (??) like so (I've used query syntax here to make it easier):
var distinctList =
from item in itemList
group item by item.Id into g
select g.FirstOrDefault(i => i.Selected) ?? g.First();
Using the predicate in FirstOrDefault, you are picking the first item where the Selected property is true. If there is none, then assuming your type is a reference type (and this is important this to work with FirstOrDefault), it will return null.
If null is returned, then you'll just return the first item in the group (through the call to First), since any item can be returned and a group can't exist without items in it (so the call to First is always guaranteed to succeed).
Basically, you are applying a selector to the result of the grouping, not while grouping.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With