【问题标题】:LINQ intersect, multiple lists, some emptyLINQ 相交,多个列表,一些为空
【发布时间】:2010-07-07 03:57:24
【问题描述】:

我正在尝试寻找与 LINQ 的交集。

示例:

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 };

想要返回:1,因为它存在于所有列表中。如果我运行:

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

它什么也不返回,因为 1 显然不在 int2 列表中。无论一个列表是否为空,我如何让它工作?

使用上面的例子或:

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>();

在这种情况下我如何返回 1 和 2.. 我不知道列表是否已填充...

【问题讨论】:

  • 考虑{1}, {2}, {3}的交集。在与前两个​​相交之后,您将尝试找到{}, {3} 的交集。你要求我们想出一些东西,在这种情况下会给{},但如果我们对{3}, {}执行相同的操作,{3}。如果您不想考虑空列表,请在尝试计算交集之前将它们从考虑中删除。
  • @Anon,有趣的观察。
  • 我将如何管理? if (int2.Count >0) var set = int1.Intersect(int2); if (int3.Count >0) var set2 = set.Intersect(set); if (int2.Count >0) var set3 = int2.Intersect(int2); .....似乎有点胡作非为......
  • 他的意思是{1}和{2}的交集是一个空列表,所以that和{3}的交集是{3} .这是允许 Intersect 方法忽略空集的一个陷阱,因为空的 results 会被忽略以及链接方法。
  • 那么问题来了……难道没有办法忽略空集而不检查它们是否为空吗?

标签: c# linq


【解决方案1】:

如果您需要一步完成,最简单的解决方案是过滤掉空列表:

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));
}

然后您可以在列表集合或其他IEnumerables 上使用它:

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

您可能更喜欢常规的静态方法:

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

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

【讨论】:

  • 只是一个小警告:感谢 Aggregate(),如果所有列表都为空,此扩展方法将抛出 InvalidOperationException 并带有“序列不包含元素”。
【解决方案2】:

您可以编写一个扩展方法来定义该行为。像

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;
    }
}

所以下面的代码会打印 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);

更新:

Anon 在 cmets 中提出了一个很好的问题。如果list 本身为空,则上述函数 生成一个空集,这应该是可取的。这意味着如果方法链中的第一个列表任何交集的结果集为空,则最终结果将为空。

要允许空的第一个列表不允许空结果集,您可以采用不同的方法。这是一种方法,它不是扩展方法,而是采用 IEnumerables 的 params 数组并首先过滤掉空集,然后尝试与其余的相交。

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;
}

你会这样使用它

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);
}

【讨论】:

  • 我应该在哪里声明扩展方法?给我:扩​​展方法只能在非泛型、非嵌套静态类中贴花,只是试图在方法中做到这一点..
  • 你可以把它放在一个静态类中。我已将类定义添加到我的答案中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-19
  • 1970-01-01
  • 1970-01-01
  • 2021-10-29
相关资源
最近更新 更多