Linq intersect to filter multiple criteria against list

1.2k Views Asked by At

I'm trying to filter users by department. The filter may contain multiple departments, the users may belong to multiple departments (n:m). I'm fiddling around with LINQ, but can't find the solution. Following example code uses simplified Tuples just to make it runnable, of course there are some real user objects.

Also on CSSharpPad, so you have some runnable code: http://csharppad.com/gist/34be3e2dd121ffc161c4

string Filter = "Dep1"; //can also contain multiple filters
var users = new List<Tuple<string, string>>
{
    Tuple.Create("Meyer", "Dep1"),
    Tuple.Create("Jackson", "Dep2"),
    Tuple.Create("Green", "Dep1;Dep2"),
    Tuple.Create("Brown", "Dep1")
};

//this is the line I can't get to work like I want to
var tuplets = users.Where(u => u.Item2.Intersect(Filter).Any());


if (tuplets.Distinct().ToList().Count > 0)
{
    foreach (var item in tuplets) Console.WriteLine(item.ToString());
}
else
{
    Console.WriteLine("No results");
}

Right now it returns:

(Meyer, Dep1)
(Jackson, Dep2)
(Green, Dep1;Dep2)
(Brown, Dep1)

What I would want it to return is: Meyer,Green,Brown. If Filter would be set to "Dep1;Dep2" I would want to do an or-comparison and find *Meyer,Jackson,Green,Brown" (as well as distinct, as I don't want Green twice). If Filter would be set to "Dep2" I would only want to have Jackson, Green. I also played around with .Split(';'), but it got me nowhere.

Am I making sense? I have Users with single/multiple departments and want filtering for those departments. In my output I want to have all users from the specified department(s). The LINQ-magic is not so strong on me.

3

There are 3 best solutions below

1
On BEST ANSWER

Since string implements IEnumerable, what you're doing right now is an Intersect on a IEnumerable<char> (i.e. you're checking each letter in the string). You need to split on ; both on Item2 and Filter and intersect those.

var tuplets = users.Where(u => 
    u.Item2.Split(new []{';'})
    .Intersect(Filter.Split(new []{';'}))
    .Any());
0
On
        string[] Filter = {"Dep1","Dep2"}; //Easier if this is an enumerable
        var users = new List<Tuple<string, string>>
        {
            Tuple.Create("Meyer", "Dep1"),
            Tuple.Create("Jackson", "Dep2"),
            Tuple.Create("Green", "Dep1;Dep2"),
            Tuple.Create("Brown", "Dep1")
        };

        //I would use Any/Split/Contains
        var tuplets = users.Where(u => Filter.Any(y=> u.Item2.Split(';').Contains(y)));


        if (tuplets.Distinct().ToList().Count > 0)
        {
            foreach (var item in tuplets) Console.WriteLine(item.ToString());
        }
        else
        {
            Console.WriteLine("No results");
        }
0
On

In addition to the other answers, the Contains extension method may also be a good fit for what you're trying to do if you're matching on a value:

var result = list.Where(x => filter.Contains(x.Value));

Otherwise, the Any method will accept a delegate:

var result = list.Where(x => filter.Any(y => y.Value == x.Value));