检查一个IEnumerable是否包含另一个IEnumerable的所有元素

时间:2010-07-18 06:17:34

标签: c# .net linq ienumerable

在比较两个集合中每个元素的字段/属性时,确定一个IEnumerable是否包含另一个IEnumerable的所有元素的最快方法是什么?


public class Item
{
    public string Value;

    public Item(string value)
    {
        Value = value;
    }
}

//example usage

Item[] List1 = {new Item("1"),new Item("a")};
Item[] List2 = {new Item("a"),new Item("b"),new Item("c"),new Item("1")};

bool Contains(IEnumerable<Item> list1, IEnumerable<Item>, list2)
{
    var list1Values = list1.Select(item => item.Value);
    var list2Values = list2.Select(item => item.Value);

    return //are ALL of list1Values in list2Values?
}

Contains(List1,List2) // should return true
Contains(List2,List1) // should return false

8 个答案:

答案 0 :(得分:118)

除非您跟踪并维护一些确定一个集合中的所有值是否包含在另一个集合中的状态,否则没有“快速方法”。如果您只有IEnumerable<T>可以使用,我会使用Intersect

var allOfList1IsInList2 = list1.Intersect(list2).Count() == list1.Count();

这个的表现应该非常合理,因为Intersect()只会枚举每个列表一次。此外,如果基础类型为Count()而不仅仅是ICollection<T>,则对IEnumerable<T>的第二次调用将是最佳的。

答案 1 :(得分:44)

您还可以使用Except从第一个列表中删除第二个列表中存在的所有值,然后检查是否已删除所有值:

var allOfList1IsInList2 = !list1.Except(list2).Any();

此方法的优点是不需要两次调用Count()。

答案 2 :(得分:20)

C#3.5 +

使用Enumerable.All<TSource>确定List1中是否包含所有List2项目:

bool hasAll = list2Uris.All(itm2 => list1Uris.Contains(itm2));

当list1包含的内容甚至超过list2的所有项目时,这也可以。

答案 3 :(得分:4)

在重复的情况下,标记为答案的解决方案将失败。如果您的IEnumerable只包含不同的值,那么它将通过。

以下答案适用于重复的2个列表:

        int aCount = a.Distinct().Count();
        int bCount = b.Distinct().Count();

        return aCount == bCount &&
               a.Intersect(b).Count() == aCount;

答案 4 :(得分:3)

Kent的答案很好而且简短,但他提供的解决方案总是需要迭代整个第一个集合。这是源代码:

public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
    if (first == null)
        throw Error.ArgumentNull("first");
    if (second == null)
        throw Error.ArgumentNull("second");
    return Enumerable.IntersectIterator<TSource>(first, second, comparer);
}

private static IEnumerable<TSource> IntersectIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
    Set<TSource> set = new Set<TSource>(comparer);
    foreach (TSource source in second)
        set.Add(source);
    foreach (TSource source in first)
    {
        if (set.Remove(source))
            yield return source;
    }
}

并非总是需要这样做。所以,这是我的解决方案:

public static bool Contains<T>(this IEnumerable<T> source, IEnumerable<T> subset, IEqualityComparer<T> comparer)
{
    var hashSet = new HashSet<T>(subset, comparer);
    if (hashSet.Count == 0)
    {
        return true;
    }

    foreach (var item in source)
    {
        hashSet.Remove(item);
        if (hashSet.Count == 0)
        {
            break;
        }
    }

    return hashSet.Count == 0;
}

实际上,您应该考虑使用ISet<T>HashSet<T>)。它包含所有必需的设置方法。在你的情况下IsSubsetOf

答案 5 :(得分:2)

Linq运算符SequenceEqual也可以工作(但对可枚举项的顺序相同)

return list1Uris.SequenceEqual(list2Uris);

答案 6 :(得分:1)

您应该使用HashSet而不是Array。

示例:

{{1}}

Reference

唯一的HasSet限制是我们不能像List那样按索引获取项目,也不能像Dictionaries那样通过Key获取项目。你所能做的就是枚举它们(每个,等等)

如果适合你,请告诉我

答案 7 :(得分:-1)

您可以使用此方法比较两个列表

Tree::Trie