コレクションを比較するための組み込みメソッドはありますか?
-
09-06-2019 - |
質問
Equals メソッドでいくつかのコレクションの内容を比較したいと思います。Dictionary と IList があります。これを行うための組み込みメソッドはありますか?
編集:2 つの辞書と 2 つの IList を比較したいので、同等が何を意味するかは明らかだと思います。2 つの辞書に、同じ値にマップされた同じキーが含まれている場合、それらは等しいです。
解決
指定された 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))
を見てください。 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);
}
順序付けされたコレクション (リスト、配列) の場合は、次を使用します。 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 サーバーが注意を喚起するのと同じことは、ページネーションを実行しようとするときに、並べ替えルールを提供する必要があることです。
さらに 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 です