简体   繁体   中英

LINQ intersect, multiple lists, some empty

I'm trying to find an intersect with LINQ.

Sample:

List<int> int1 = new List<int>() { 1,2 };
List<int> int2 = new List<int>();
List<int> int3 = new List<int>() { 1 };
List<int> int4 = new List<int>() { 1, 2 };
List<int> int5 = new List<int>() { 1 };

Want to return: 1 as it exists in all lists.. If I run:

var intResult= int1
            .Intersect(int2)
            .Intersect(int3)
            .Intersect(int4)
            .Intersect(int5).ToList();

It returns nothing as 1 obviously isn't in the int2 list. How do I get this to work regardless if one list is empty or not ?

Use the above example or:

List<int> int1 = new List<int>() { 1,2 };
List<int> int2 = new List<int>();
List<int> int3 = new List<int>();
List<int> int4 = new List<int>();
List<int> int5 = new List<int>();

How do I return 1 & 2 in this case.. I don't know ahead of time if the lists are populated...

If you need it in a single step, the simplest solution is to filter out empty lists:

public static IEnumerable<T> IntersectNonEmpty<T>(this IEnumerable<IEnumerable<T>> lists)
{
    var nonEmptyLists = lists.Where(l => l.Any());
    return nonEmptyLists.Aggregate((l1, l2) => l1.Intersect(l2));
}

You can then use it on a collection of lists or other IEnumerable s:

IEnumerable<int>[] lists = new[] { l1, l2, l3, l4, l5 };
var intersect = lists.IntersectNonEmpty();

You may prefer a regular static method:

public static IEnumerable<T> IntersectNonEmpty<T>(params IEnumerable<T>[] lists)
{
    return lists.IntersectNonEmpty();
}

var intersect = ListsExtensionMethods.IntersectNonEmpty(l1, l2, l3, l4, l5);

You could write an extension method to define that behaviour. Something like

static class MyExtensions
{
    public static IEnumerable<T> IntersectAllIfEmpty<T>(this IEnumerable<T> list, IEnumerable<T> other)
    {
        if (other.Any())
            return list.Intersect(other);
        else
            return list;
    }
}

So the code below would print 1.

List<int> list1 = new List<int>() { 1, 2 };
List<int> list2 = new List<int>();
List<int> list3 = new List<int>() { 1 };

foreach (int i in list1.IntersectAllIfEmpty(list2).IntersectAllIfEmpty(list3))
    Console.WriteLine(i);

Update:

Anon brings up a good point in the comments to the question. The above function will result in an empty set if list itself is empty, which should be desirable. This means if the first list in the method chain or the result set of any intersection is empty, the final result will be empty.

To allow for an empty first list but not for empty result sets, you could take a different approach. This is a method which is not an extension method, but rather takes a params array of IEnumerables and first filters out the empty sets and then attempts to intersect the rest.

public static IEnumerable<T> IntersectAllIfEmpty<T>(params IEnumerable<T>[] lists)
{
    IEnumerable<T> results = null;

    lists = lists.Where(l => l.Any()).ToArray();

    if (lists.Length > 0)
    {
        results = lists[0];

        for (int i = 1; i < lists.Length; i++)
            results = results.Intersect(lists[i]);
    }
    else
    {
        results = new T[0];
    }

    return results;
}

You would use it like this

List<int> list0 = new List<int>();
List<int> list1 = new List<int>() { 1, 2 };
List<int> list2 = new List<int>() { 1 };
List<int> list3 = new List<int>() { 1,2,3 };

foreach (int i in IntersectAllIfEmpty(list0, list1, list2, list3))
{
    Console.WriteLine(i);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM