コレクションを比較するための組み込みメソッドはありますか?

StackOverflow https://stackoverflow.com/questions/43500

  •  09-06-2019
  •  | 
  •  

質問

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

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

役に立ちましたか?

解決

Enumerable.SequenceEqual

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

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

他のヒント

他の人が示唆し、指摘したように、 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))

言及されたものに加えて、 シーケンスが等しい, 、 どれの

2つのリストが等しい長さであり、対応する要素が比較に応じて等しいと等しい場合に真です

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

.Net4 には次のものがあることに言及する価値があります。 SetEquals の上 ISet オブジェクト、それ

要素の順序と重複する要素は無視されます。

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

を見てください。 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);

.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

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;

    }

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

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

 CollectionAssert.AreEquivalent

これでほぼ希望どおりのことができます。

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

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

最初の比較が必要になるのは、 dict2 からのすべてのキーを含めることができます dict1 もっと。

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

次の例はどうでしょうか。

 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

順序付けされたコレクション (リスト、配列) の場合は、次を使用します。 SequenceEqual

ハッシュセット用 SetEquals

Dictionary の場合、次のことができます。

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

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

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

これを見てください:

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

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

少なくとも私はそう信じます。その理由は、コレクションの平等性がおそらくユーザー定義の動作であるためです。

コレクション内の要素は、自然に順序付けされていますが、特定の順序であることは想定されていません。比較アルゴリズムが依存すべきものではありません。次の 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);
    }
}

つまり、一方のコレクションは、元の順序を考慮せずに、繰り返し回数を含む要素でもう一方のコレクションを表現します。実装に関するいくつかのメモ:

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

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

  • 参照が等しい場合、それは単に Boris です

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top