How to filter results based on selected results in a LINQ query?

I have a list of Foos that I would like to filter according to foo.HasBar .
Foo also have the Baz property.
When Foo is selected, all filters with the same Baz object must be filtered.
Is it possible to achieve this with a LINQ query or use foreach instead?

Edit: Here is an example dataset:

 Foo.HasBar = true; Foo.Baz = 1; Foo.HasBar = true; Foo.Baz = 1; Foo.HasBar = false; Foo.Baz = 1; Foo.HasBar = true; Foo.Baz = 2; Foo.HasBar = false; Foo.Baz = 2; 

I am trying to ensure that no other iteration on another Foo.HasBar = true; Foo.Baz = 1; Foo.HasBar = true; Foo.Baz = 1; will not be executed, otherwise no other iteration on Foo.HasBar = false; Foo.Baz = 2; Foo.HasBar = false; Foo.Baz = 2; will Foo.HasBar = true; Foo.Baz = 2; if Foo.HasBar = true; Foo.Baz = 2; Foo.HasBar = true; Foo.Baz = 2; already selected.

Here is how I would do it with the foreach loop:

 var selectedFoos = new List<Foo>(); foreach(var foo in foos) { if (selectedFoos.Exists(f => f.Baz == foo.Baz)) continue; if (foo.HasBar) selectedFoos.Add(foo); } 
+4
source share
5 answers

Use IEnumerable<Foo>.Distinct and implement your equality operator with the operator where the Baz property is checked, and the HasBar property HasBar ignored if Baz not equal. You can do this with && because if the left expression is false, the right expression is not evaluated.

Then a HasBar based HasBar with IEnumerable<Foo>.Where .

If you do not want to clutter your Foo object with the Equals operator, or you need different Equals implementations for different cases, then run a separate IEqualityComparer<Foo> .

This also has the advantage that you can completely not check the HasBar property when getting different values. If you miss a check in the class itself, it can cause subtle mistakes, because people can expect them to be equal. But with a well-known user mapping, it is unlikely that people will believe that it will ensure absolute equality.

Here is a sample code:

 IEnumerable<Foo> selectedFoos = sampleDataSet .Distinct(new PerformantFooComparer()) .Where(f => f.HasBar); // ... private class PerformantFooComparer : IEqualityComparer<Foo> { public bool Equals(Foo x, Foo y) { bool isXNull = x == null; bool isYNull = y == null; return isXNull == isYNull && isXNull || ( x.Baz == y.Baz // && x.HasBar == y.HasBar // HasBar commented out to avoid performance overhead. // It is handled by a Where(foo => foo.HasBar) filter ); } public int GetHashCode(Foo obj) { if (obj == null) return 0; // See: http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode int hash = 17; hash = hash * 23 + obj.Baz.GetHashCode(); // HasBar intentionally not hashed return hash; } } 
+3
source
 var results = from f in Foos where (foo.HasBar) && (foo.Baz equals SelectedBaz) select f; 
+1
source
 var q = from f in foos group f by f.Baz into g let tempFoo = g.FirstOrDefault(foo => foo.HasBar) where tempFoo != null select tempFoo; 

The best I could come up with (for now).

Using let should avoid multiple FirstOrDefault calls, so your intensive-intensive HasBar will not be called more than necessary if you assume that the FirstOrDefault implementation will not iterate after it finds the result. If let was not used, FirstOrDefault should be used in the where clause.

+1
source

From your code, you just need to:

 foos.Where(f => !foos.Where(f2 => f2.HasBar).Any(s => s.Baz == f.Baz)); 
0
source

you can just go with

 var filtered = from f in foos where foo.HasBar == true select f; 
0
source

All Articles