コレクションを比較する組み込みの方法はありますか?


178

Equalsメソッドでいくつかのコレクションの内容を比較したいと思います。辞書とIListがあります。これを行うための組み込みの方法はありますか?

編集:2つの辞書と2つのIListを比較したいので、等しいことの意味は明らかだと思います。2つの辞書に同じ値にマップされた同じキーが含まれている場合、それらは等しくなります。


注文の有無に関係なくIList?質問があいまいです。
nawfal

Enumerable.SequenceEqualそしてISet.SetEquals、この機能のバージョンを提供します。順序にとらわれず、重複しているコレクションを操作したい場合は、独自にロールする必要があります。この投稿で
ChaseMedallion

回答:


185

Enumerable.SequenceEqual

指定されたIEqualityComparer(T)を使用して要素を比較することにより、2つのシーケンスが等しいかどうかを判断します。

リストと辞書を直接比較することはできませんが、辞書の値のリストとリストを比較することはできます


52
問題は、SequenceEqualが要素の順序が同じであることを期待していることです。辞書クラスは列挙時にキーまたは値の順序を保証しないため、SequenceEqualを使用する場合は、最初に.Keysと.Valuesをソートする必要があります。
オリオンエドワーズ

3
@Orion:...もちろん、順序の違いを検出したくない場合を除きます:-)
schoetbi 2010年

30
@schoetbi:順序を保証しないコンテナで順序の違いを検出したいのはなぜですか?
Matti Virkkunen、2011

4
@schoetbi:IEnumerableから特定の要素を取り出すためのものです。しかし、辞書にはそうではなく、保証の順序を行い.Keysそして.Values、彼らはのように感じる任意の順序でキーと値を返すことが、辞書がよくように修正されるように、その順序が変更される可能性があります。辞書とは何か、そうでないものについて読んでおくことをお勧めします。
Matti Virkkunen、2011年

5
MSのTestToolsとNUnitはCollectionAssert.AreEquivalentを提供します
tymtam

44

他の人が示唆し、指摘したように、SequenceEqual順序に依存します。これを解決するには、キーによって辞書をソートします(一意であり、ソートは常に安定しています)を使用しますSequenceEqual。次の式は、2つの辞書が内部の順序に関係なく等しいかどうかを確認します。

dictionary1.OrderBy(kvp => kvp.Key).SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key))

編集: Jeppe Stig Nielsenによって指摘されたように、一部のオブジェクトにはIComparer<T>と互換性のないがありIEqualityComparer<T>、不正な結果をもたらします。このようなオブジェクトでキーを使用する場合はIComparer<T>、それらのキーの正しいものを指定する必要があります。たとえば、文字列キー(この問題が発生する)では、正しい結果を得るために次の操作を行う必要があります。

dictionary1.OrderBy(kvp => kvp.Key, StringComparer.Ordinal).SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))

キーの種類が異なる場合はどうなりますCompareToか?その後、ソリューションは爆発します。キータイプにデフォルトの等値比較子と互換性のないデフォルトの比較子がある場合はどうなりますか?これはの場合ですstring。例として(暗黙のデフォルト等式する比較器と)これらの辞書は、(私が知っ全ての文化に関する情報の下で)あなたのテストを失敗します:var dictionary1 = new Dictionary<string, int> { { "Strasse", 10 }, { "Straße", 20 }, }; var dictionary2 = new Dictionary<string, int> { { "Straße", 20 }, { "Strasse", 10 }, };
ジャップスティグ・ニールセン

@JeppeStigNielsen:IComparerとの間の非互換性についてはIEqualityComparer-この問題は認識していませんでしたが、非常に興味深いです!可能な解決策で答えを更新しました。の欠如について、CompareTo開発者はOrderBy()メソッドに提供されたデリゲートが比較可能なものを返すことを確認する必要があると思います。これはOrderBy()、辞書の比較以外でも、あらゆる用途に当てはまると思います。
Allon Guralnek 16

15

上述に加えてSequenceEqual、その

2つのリストが同じ長さで、対応する要素が比較子に従って等しい場合、trueです。

(これはデフォルトの比較子、つまりオーバーライドされる可能性がありますEquals()

.Net4ではISetオブジェクトにSetEqualsがあり、

要素と重複する要素の順序を無視します。

そのため、オブジェクトのリストが必要であるが、それらが特定の順序である必要はない場合は、ISet(のようなHashSet)が正しい選択である可能性があることを考慮してください。


7

Enumerable.SequenceEqual メソッドを見てください。

var dictionary = new Dictionary<int, string>() {{1, "a"}, {2, "b"}};
var intList = new List<int> {1, 2};
var stringList = new List<string> {"a", "b"};
var test1 = dictionary.Keys.SequenceEqual(intList);
var test2 = dictionary.Values.SequenceEqual(stringList);

13
SequenceEqualは値が信頼できる順序でディクショナリから出力されることを期待しているため、これは信頼できません-ディクショナリは順序やディクショナリについてそのような保証を行いません。キーは[1、2]ではなく[2、1]として出力される可能性があります。テストが失敗する
Orion Edwards

5

.NETには、コレクションを比較するための強力なツールがありません。以下のリンクで見つけることができる簡単なソリューションを開発しました。

http://robertbouillon.com/2010/04/29/comparing-collections-in-net/

これにより、順序に関係なく等価比較が実行されます。

var list1 = new[] { "Bill", "Bob", "Sally" };
var list2 = new[] { "Bob", "Bill", "Sally" };
bool isequal = list1.Compare(list2).IsSame;

これにより、アイテムが追加または削除されたかどうかが確認されます。

var list1 = new[] { "Billy", "Bob" };
var list2 = new[] { "Bob", "Sally" };
var diff = list1.Compare(list2);
var onlyinlist1 = diff.Removed; //Billy
var onlyinlist2 = diff.Added;   //Sally
var inbothlists = diff.Equal;   //Bob

これにより、ディクショナリのどのアイテムが変更されたかがわかります。

var original = new Dictionary<int, string>() { { 1, "a" }, { 2, "b" } };
var changed = new Dictionary<int, string>() { { 1, "aaa" }, { 2, "b" } };
var diff = original.Compare(changed, (x, y) => x.Value == y.Value, (x, y) => x.Value == y.Value);
foreach (var item in diff.Different)
  Console.Write("{0} changed to {1}", item.Key.Value, item.Value.Value);
//Will output: a changed to aaa

10
もちろん.NETには、コレクションを比較するための強力なツールがあります(これらはセットベースの操作です)。.Removed同じであるlist1.Except(list2).Addedlist2.Except(list1).Equalであるlist1.Intersect(list2).Differentされますoriginal.Join(changed, left => left.Key, right => right.Key, (left, right) => left.Value == right.Value)。LINQとのほとんどすべての比較を行うことができます。
Allon Guralnek、2011年

3
訂正:.Differentですoriginal.Join(changed, left => left.Key, right => right.Key, (left, right) => new { Key = left.Key, NewValue = right.Value, Different = left.Value == right.Value).Where(d => d.Different)。またOldValue = left.Value、古い値も必要な場合は追加することもできます。
Allon Guralnek、2011年

3
@AllonGuralnekあなたの提案は良いですが、リストが真のセットではない場合-リストは同じオブジェクトを複数回含んでいる場合は処理しません。{1、2}と{1、2、2}を比較しても、追加/削除されたものは何も返されません。
Niall Connaughton 2013


4

Enumerable.SequenceEqualメソッドについては知りませんでしたが(毎日何かを学びます...)、拡張メソッドを使用することをお勧めします。このようなもの:

    public static bool IsEqual(this List<int> InternalList, List<int> ExternalList)
    {
        if (InternalList.Count != ExternalList.Count)
        {
            return false;
        }
        else
        {
            for (int i = 0; i < InternalList.Count; i++)
            {
                if (InternalList[i] != ExternalList[i])
                    return false;
            }
        }

        return true;

    }

興味深いことに、SequenceEqualについて2秒で読んだ後、Microsoftが私が説明した関数を作成したようです。


4

これはあなたの質問に直接答えるものではありませんが、MSのTestToolsとNUnitの両方が提供します

 CollectionAssert.AreEquivalent

それはあなたが望むことをほとんど行います。


私のNUnitテストのためにこれを探していました
ブレム

1

コレクションを比較するには、LINQを使用することもできます。Enumerable.Intersect等しいすべてのペアを返します。次のような2つの辞書を解析できます。

(dict1.Count == dict2.Count) && dict1.Intersect(dict2).Count() == dict1.Count

dict2からのすべてのキーを含むことができるため、最初の比較が必要ですdict1

を使用Enumerable.ExceptEnumerable.Unionてバリエーションを考えることもできます。これにより、同様の結果が得られます。ただし、セット間の正確な違いを判別するために使用できます。


1

この例はどうですか:

 static void Main()
{
    // Create a dictionary and add several elements to it.
    var dict = new Dictionary<string, int>();
    dict.Add("cat", 2);
    dict.Add("dog", 3);
    dict.Add("x", 4);

    // Create another dictionary.
    var dict2 = new Dictionary<string, int>();
    dict2.Add("cat", 2);
    dict2.Add("dog", 3);
    dict2.Add("x", 4);

    // Test for equality.
    bool equal = false;
    if (dict.Count == dict2.Count) // Require equal count.
    {
        equal = true;
        foreach (var pair in dict)
        {
            int value;
            if (dict2.TryGetValue(pair.Key, out value))
            {
                // Require value be equal.
                if (value != pair.Value)
                {
                    equal = false;
                    break;
                }
            }
            else
            {
                // Require key be present.
                equal = false;
                break;
            }
        }
    }
    Console.WriteLine(equal);
}

礼儀:https : //www.dotnetperls.com/dictionary-equals


value!= pair.Valueは参照比較を行っています。代わりにEqualsを使用してください
kofifus

1

順序付けられたコレクション(リスト、配列)の使用 SequenceEqual

HashSet用 SetEquals

辞書でできること:

namespace System.Collections.Generic {
  public static class ExtensionMethods {
    public static bool DictionaryEquals<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> d1, IReadOnlyDictionary<TKey, TValue> d2) {
      if (object.ReferenceEquals(d1, d2)) return true; 
      if (d2 is null || d1.Count != d2.Count) return false;
      foreach (var (d1key, d1value) in d1) {
        if (!d2.TryGetValue(d1key, out TValue d2value)) return false;
        if (!d1value.Equals(d2value)) return false;
      }
      return true;
    }
  }
}

(より最適化されたソリューションはソートを使用しますが、これにはが必要になりますIComparable<TValue>


0

いいえ。コレクションフレームワークには、平等という概念はありません。あなたがそれについて考えるならば、主観的でないコレクションを比較する方法はありません。たとえば、IListをディクショナリと比較すると、すべてのキーがIListにある場合、すべての値がIListにある場合、または両方がIListにある場合、それらは等しくなりますか?これらの2つのコレクションを何のために使用するかを知らない限り、これらの2つのコレクションを比較する明白な方法はないため、汎用のequalsメソッドは意味がありません。


0

いいえ、フレームワークはリストの内容を比較する方法を認識していないためです。

これを見てください:

http://blogs.msdn.com/abhinaba/archive/2005/10/11/479537.aspx


3
要素を比較する方法をフレームワークに伝えるためのオプションはすでにいくつかありませんか?IComparer<T>、オーバーライドobject.EqualsIEquatable<T>IComparable<T>...
ステファンSteinegger

0
public bool CompareStringLists(List<string> list1, List<string> list2)
{
    if (list1.Count != list2.Count) return false;

    foreach(string item in list1)
    {
        if (!list2.Contains(item)) return false;
    }

    return true;
}

0

ありませんでしたし、そうでなかったかもしれませんが、少なくとも私はそう信じています。背後にある理由は、コレクションの同等性がおそらくユーザー定義の動作であるためです。

コレクション内の要素は特定の順序になっているとは限りませんが、自然に順序付けされているため、比較アルゴリズムがこれに依存するべきではありません。次の2つのコレクションがあるとします。

{1, 2, 3, 4}
{4, 3, 2, 1}

彼らは等しいかどうか?あなたは知っている必要がありますが、私はあなたの視点が何であるかわかりません。

アルゴリズムがソート規則を提供するまで、コレクションは概念的にデフォルトで順序付けされていません。SQLサーバーがあなたに注意を喚起するのと同じことは、ページネーションを行おうとするときに、ソート規則を提供する必要があります。

https://docs.microsoft.com/en-US/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-2017

さらに別の2つのコレクション:

{1, 2, 3, 4}
{1, 1, 1, 2, 2, 3, 4}

もう一度、彼らは等しいかどうか?教えてください ..

コレクションの要素の再現性は、さまざまなシナリオでその役割を果たし、一部のコレクションでDictionary<TKey, TValue>は、要素の繰り返しさえ許可していません。

これらの種類の平等はアプリケーションで定義されているため、フレームワークは可能な実装のすべてを提供しなかったと思います。

まあ、一般的なケースでEnumerable.SequenceEqualは十分ですが、次のケースではfalseを返します:

var a = new Dictionary<String, int> { { "2", 2 }, { "1", 1 }, };
var b = new Dictionary<String, int> { { "1", 1 }, { "2", 2 }, };
Debug.Print("{0}", a.SequenceEqual(b)); // false

私はこのような質問へのいくつかの回答(あなたはそれらのためにグーグルするかもしれません)と私が一般的に使用するものを読みました:

public static class CollectionExtensions {
    public static bool Represents<T>(this IEnumerable<T> first, IEnumerable<T> second) {
        if(object.ReferenceEquals(first, second)) {
            return true;
        }

        if(first is IOrderedEnumerable<T> && second is IOrderedEnumerable<T>) {
            return Enumerable.SequenceEqual(first, second);
        }

        if(first is ICollection<T> && second is ICollection<T>) {
            if(first.Count()!=second.Count()) {
                return false;
            }
        }

        first=first.OrderBy(x => x.GetHashCode());
        second=second.OrderBy(x => x.GetHashCode());
        return CollectionExtensions.Represents(first, second);
    }
}

つまり、元の順序を考慮せずに、1つのコレクションが繰り返し要素を含む要素で他のコレクションを表しています。実装に関する注意事項:

  • GetHashCode()順序付けのためであり、平等ではありません。この場合はそれで十分だと思います

  • Count() コレクションを実際には列挙せず、直接プロパティの実装に分類されます ICollection<T>.Count

  • 参照が等しい場合、それはボリスです

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