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


102

比较两个集合中每个元素的字段/属性时,确定一个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

1
您的清单在哪个方向上?您是否要检查list1中的所有项目都在列表2中,还是list2中的所有项目都在列表1中?
Mark Byers 2010年

Answers:


138

除非您跟踪并维护确定一个集合中的所有值是否都包含在另一个集合中的某种状态,否则没有“快速方法”来执行此操作。如果您只需要IEnumerable<T>与之抗争,我会使用Intersect

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

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


我做了一些测试,这种方法似乎比其他方法运行得更快。谢谢你的提示。
Brandon Zacharie 2010年

2
如果列表中有重复项,则无法使用。例如,比较441和414的char数组将返回41,因此计数失败。
约翰

69

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

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

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


这也有助于找出List1中的内容,而不是List2中的内容。
荷马

16
这在list1具有重复值的情况下起作用。接受的答案不是。
dbc

23

C#3.5+

使用Enumerable.All<TSource>以确定是否所有列表2项都包含在列表1:

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

当list1包含的内容超过list2的所有项目时,这也将起作用。


10
很大程度地影响了Contains()呼叫中All()呼叫的性能。
肯特·布加亚特

您也可以将其移动到分组方法:bool hasAll = list2Uris.All(list1Uris.Contains);
jimpanzer

如果是IEnumerable <T>类型,此解决方案将提供n * m性能。
德米特里·多克辛

4
速记: bool hasAll = list2Uris.All(list1Uris.Contains);
照明器

3

肯特的答案很简短,但他提供的解决方案始终需要在整个第一个集合中进行迭代。这是源代码:

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>)。它包含所有必需的set方法。IsSubsetOf在你的情况下。


2

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

return list1Uris.SequenceEqual(list2Uris);

2

如果重复,则标记为答案的解决方案将失败。如果您的IEnumerable仅包含不同的值,则它将通过。

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

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

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

这不是一个好的解决方案,因为它会删除所有重复项,而不是实际比较它们。
约翰

2

您应该使用HashSet而不是Array。

例:

List1.SetEquals(List2); //returns true if the collections contains exactly same elements no matter the order they appear in the collection

参考

唯一的HasSet限制是,我们不能像List这样的索引来获取项目,也不能像Dictionary这样的键来获取项目。您所能做的就是枚举它们(每个,一会儿,等等)

请让我知道是否适合您


-2

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

    //Method to compare two list
    private bool Contains(IEnumerable<Item> list1, IEnumerable<Item> list2)
    {
        bool result;

        //Get the value
        var list1WithValue = list1.Select(s => s.Value).ToList();
        var list2WithValue = list2.Select(s => s.Value).ToList();

        result = !list1WithValue.Except(list2WithValue).Any();

        return result;
    }

3年前给出了几乎相同的答案:stackoverflow.com/a/16967827/5282087
Dragomok
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.