Finding Items in a Collection that Match one of...
Use LINQ to get the members of a collection that match the members of another collection
Introduction
Recently, I have answered a couple of Quick Answers where the user wanted a list of items that match a condition from another collection (similar to SQL's IN
operator. The trick to solving this is reversing the order of the operands in the Where
Linq extension from the norm.
Using the Code
The normal structue of a Where
predicate follows the pattern:
myCollection.Where(item=>item.Property matches value)
where Property
is the property you want to match to a particular value using whatever operator or method is appropriate to return the required result. However, what if value
is a collection of values? The trick to extracting your items is basically to reverse the conditional statement, and, depending on the way the match is made, optionally apply an Any,
All
, or Contains
method to the values collection. So the basic pattern becomes:
myCollection.Where(item=>values.Any(value=>value matches item.Property));
Some Examples
Problem
Get all items from List1
that are one of the items in List2
. Comparison should be case-insensitive. This is basically the same as a standard SQL IN
clause:
SELECT Field FROM TABLE WHERE Field IN (value1, value2,....valueN)
Solution
List<string> values = {value1, value2...valueN};
var results = table
.Select(rec=>rec.field)
.Where(field=>values.Contains(field, StringComparer.CurrentCultureIgnoreCase));
Problem
Get all items where the inventory_code
is prefixed with one of a set of standard prefixes(strings
).
Solution
string[] prefixes = {"N01", "N02", "M01", "M02" };
var results = inventoryList
.Where(item=>prefixes.Any(prefix=>item.inventory_code.StartWith(prefix)));
Problem
Get paragraphs that contain all of the pre-defined words.
Solution
This solution performs the trick twice. Upon separating out each paragraph, the Where
is issued with the SearchWords
array as the LHS of the condition. It then issues the All
condition for each word in SearchWords
with the words in the paragraph as the LHS of the condition.
string[] searchwords = {....};
string[] paragraphs = {
"The quick brown fox jumps over the lazy dog.",
"The quick brown fox jumps over the lazy cat.\nNotice the substitution of 'cat' for 'dog'.",
"The quick brown fox jumps over the lazy cat." };
string[] searchWords = { "FOX", "DOG" };
char[] wordDelimiters = { ' ', ',', '.', ';', ';','\n', '"', '\'' };
var results = paragraphs.Where(paragraph => searchWords
.All(word => paragraph.Split(wordDelimiters, StringSplitOptions.RemoveEmptyEntries)
.Contains(word, StringComparer.CurrentCultureIgnoreCase)));
As evidenced by these examples, the concept of using a pre-defined collection as the LHS operand in a Where
albeit seeming a little unnatural, can be a useful tool.