series0ne series0ne - 27 days ago 11
C# Question

LINQ - building AND vs OR logic for multiple result sets

I am trying to find the most elegant way of finding exact matches across multiple

IEnumerable<T>
collections, using AND logic, rather than OR logic.

Example

public class IceCream
{
public string Flavor { get; set; } // "Vanilla", "Choc Chip", "Mint"
public bool ContainsNuts { get; set; }
public ServingType { get; set; } // "Cup", "Cone", "Waffle Cone"
public SprinklesType { get; set; } // "Choc", "HundredsAndThousands", "EdibleBallBearings"
}


My first step is that I want to be able to get all available ice creams by individual search terms:

IEnumerable<IceCream> IceCreamsByFlavor { get; set; }
IEnumerable<IceCream> IceCreamsByContainsNuts { get; set; }
IEnumerable<IceCream> IceCreamsByServingType { get; set; }
IEnumerable<IceCream> IceCreamsBySprinklesType { get; set; }


Now, I can OR these together to get a collection of ice creams that have ANY of the properties of the term:

return Enumerable.Empty<IceCream>()
.Concat(IceCreamsByFlavor)
.Concat(IceCreamsByContainsNuts)
.Concat(IceCreamsByServingType)
.Concat(IceCreamsBySprinklesType)
.Distinct();


But now, say if my search term was:

Search(
flavor: "Vanilla",
containsNuts: true,
servingType: ServingType.WaffleCone,
sprinklesType: SprinkesType.HundredsAndThousands);


How do I get an
IEnumerable<IceCream>
that contains only ice creams that contain ALL of the terms, rather than ANY of them?

Answer

You can use Intersect instead of Concat:

return IceCreamsByFlavor
    .Intersect(IceCreamsByContainsNuts)
    .Intersect(IceCreamsByServingType)
    .Intersect(IceCreamsBySprinklesType);

It will return only items that are in all collections.

Keep in mind that depending on how your equality comparison is implemented you might need to use to use overload of Intersect with IEqualityComparer

Comments