Better Linq Query for filtering Parent List with no Children
I have 2 Lists.
var adultList = new List<Dude>();
adultList.Add(new Dude() { ID = 2, Name = "Randy Marsh" });
adultList.Add(new Dude() { ID = 3, Name = "Jimbo Kern" }); // no kids
adultList.Add(new Dude() { ID = 4, Name = "Gerald Broflovski" });
adultList.Add(new Dude() { ID = 5, Name = "Stuart McCormick" });
adultList.Add(new Dude() { ID = 6, Name = "Liane Cartman" });
adultList.Add(new Dude() { ID = 7, Name = "Ned Gerblansky" }); // no kids
var childList = new List<Dude>();
childList.Add(new Dude() { ID = 8, Name = "Stan Marsh", ParentID = 2 });
childList.Add(new Dude() { ID = 9, Name = "Kyle Broflovski", ParentID = 4 });
childList.Add(new Dude() { ID = 10, Name = "Ike Broflovski", ParentID = 4 });
childList.Add(new Dude() { ID = 11, Name = "Kenny McCormick", ParentID = 5 });
childList.Add(new Dude() { ID = 12, Name = "Eric Cartman", ParentID = 6 });
I want a Linq query to return that returns any Dudes in the adultList that do NOT have any kids. The result list should also have no null entries (in sample above, should have a Count() of 2 and return 开发者_如何学Conly Jimbo and Ned).
var nullList = new List<Dude>();
nullList.Add(null);
var adultsWithNoChildren = adultList.GroupJoin(
childList,
p => p.ID,
c => c.ParentID,
(p, c) =>
{
if (c.FirstOrDefault() == null) return p;
return null;
})
.Except(nullList);
Is this the best way to accomplish this? is there another Linq function or something else? I don't like the idea of having the nullList created, but that is the only ensure that the result list has an accurate count.
Thanks
My approach would be like this:
var adultNoChildren = (from adult in adultList
where !childList.Any(child => child.ParentID == adult.ID)
select adult).ToList();
This can also be done using the other LINQ Syntax, but I can never remember that :) (The nice thing about .Any is that it stops as soon as it finds a result, so the entire child list is only traversed for adults with no children)
If the lists have any size at all, I have to recommend a solution involving GroupJoin, due to hashjoin costing n+m, versus where!any costing n*m
IEnumerable<Dude> noKids =
from adult in adultList
join child in childList
on adult.ID equals child.ParentID into kids
where !kids.Any()
select adult;
Or in method form
IEnumerable<Dude> noKids = adultList.GroupJoin(
childList,
adult => adult.ID,
child => child.ParentID,
(adult, kids) => new {Dude = adult, AnyKids = kids.Any() })
.Where(x => !x.AnyKids)
.Select(x => x.Dude);
Lastly, is Liane really a dude?
adultList.Where(x => !childList.Select(y => y.ParentID).Contains(x.ID));
var noKids = from adult in adultList
join child in childList
on adult.ID equals child.ParentID into g
from item in g.DefaultIfEmpty()
where item == null
select adult;
var noKids = adultList.Where(a => !childList.Any(c => c.ParentID == a.ID));
精彩评论