1つの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
あなたのリストはどの方向ですか?リスト1のすべてのアイテムがリスト2にあるか、またはリスト2のすべてのアイテムがリスト1にあるかを確認しますか?
Mark Byers、2010

回答:


138

あるコレクションのすべての値が別のコレクションに含まれているかどうかを判断する状態を追跡および維持しない限り、これを行う「高速な方法」はありません。あなたがIEnumerable<T>反対する必要があるだけなら、私は使用しますIntersect

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

Intersect()各リストを1回だけ列挙するため、このパフォーマンスは非常に妥当です。また、Count()基礎となる型がICollection<T>単なるではなくである場合、2番目の呼び出しは最適になりますIEnumerable<T>


私はいくつかのテストを行いましたが、この方法は他のものよりも速く実行されているようです。先端をありがとう。
Brandon Zacharie、2010

2
リストに重複がある場合、これは機能しません。たとえば、441と414のchar配列を比較すると41が返されるため、カウントは失敗します。
John

69

Exceptを使用して、最初のリストから2番目のリストに存在するすべての値を削除し、すべての値が削除されているかどうかを確認することもできます。

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

このメソッドには、Count()を2回呼び出す必要がないという利点がありました。


これは、List1にはあるがList2にはないものを見つけるのにも役立ちます。
ホーマー

16
これは、list1の値が重複している状況で機能します。受け入れられた答えはしません。
dbc 2016

23

C#3.5以降

を使用Enumerable.All<TSource>して、すべてのList2アイテムがList1に含まれているかどうかを判断します。

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

これは、list1にlist2のすべてのアイテムより多くのアイテムが含まれている場合にも機能します。


10
Contains()コール内でのコールのパフォーマンスへの影響が重要All()です。
ケントブーガート、

また、グループメソッドに移動することもできます。bool hasAll = list2Uris.All(list1Uris.Contains);
jimpanzer、2014年

IEnumerable <T>タイプの場合、このソリューションはn * mのパフォーマンスを提供します。
Dmitriy Dokshin

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;

すべての重複を削除し、実際にはそれらを比較しないため、これは良い解決策ではありません。
John

2

Arrayの代わりにHashSetを使用する必要があります。

例:

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

参照

HasSetの唯一の制限は、Listのようにインデックスでアイテムを取得できず、Dictionariesのようにキーでアイテムを取得できないことです。あなたができることはそれらを列挙することです(それぞれのために、ながら、など)

それがあなたのために働くかどうか私に知らせてください


-2

このメソッドを使用して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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.