web-dev-qa-db-ja.com

順序を無視して、2つのList <T>オブジェクトが等しいかどうか比較します。

さらに別のリスト比較質問。

List<MyType> list1;
List<MyType> list2;

リスト内の位置に関係なく、両者が同じ要素を持っていることを確認する必要があります。各MyTypeオブジェクトはリストに複数回現れることがあります。これをチェックする組み込み関数はありますか?各要素がリストに一度だけ現れることを保証したらどうなりますか?

EDIT:答えてくれてありがとう。でも、何かを追加するのを忘れて、それぞれの要素の出現回数は両方のリストで同じであるべきです。

218
Bruno Teixeira

あなたがそれらを本当に等しくしたい(すなわち、同じアイテムと同じ数の各アイテム)ことを望むならば、私は最も簡単な解決策が比較する前にソートすることであると思います:

Enumerable.SequenceEqual(list1.OrderBy(t => t), list2.OrderBy(t => t))

編集する

これは少し良く(約10倍速く)実行し、IEquatableではなくIComparableのみを必要とするソリューションです。

public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2) {
  var cnt = new Dictionary<T, int>();
  foreach (T s in list1) {
    if (cnt.ContainsKey(s)) {
      cnt[s]++;
    } else {
      cnt.Add(s, 1);
    }
  }
  foreach (T s in list2) {
    if (cnt.ContainsKey(s)) {
      cnt[s]--;
    } else {
      return false;
    }
  }
  return cnt.Values.All(c => c == 0);
}

編集2:

任意のデータ型をキーとして扱うには(たとえば、Frank Tzanabetisが指摘したようにnull許容型)、辞書に対して comparer を取るバージョンを作ることができます。

public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer) {
  var cnt = new Dictionary<T, int>(comparer);
  ...
270
Guffa

書かれているように、この質問はあいまいです。ステートメント

...リスト内の位置に関係なく、両方とも同じ要素を持ちます。各MyTypeオブジェクトは、リストに複数回現れることがあります。

2つのリストが同じセットのオブジェクトまたは同じ個別セットを持っていることを確認するかどうかを示すものではありません。 .

順序に関係なく、コレクションに確実に同じメンバーのセットを確実に付けるには、次のようにします。

// lists should have same count of items, and set difference must be empty
var areEquivalent = (list1.Count == list2.Count) && !list1.Except(list2).Any();

2つのコレクションが同じメンバーのセットを持っていることを確認したい場合(どちらかの重複が無視される場合)は、次のようにします。

// check that [(A-B) Union (B-A)] is empty
var areEquivalent = !list1.Except(list2).Union( list2.Except(list1) ).Any();

集合演算(IntersectUnionExcept)を使用する方がContainsのようなメソッドを使用するよりも効率的です。私の意見では、それはまたあなたの質問の期待をよりよく表しています。

EDIT:質問を明確にしたので、最初の形式を使用したいと思うことがあります - 重複することが重要なので。これはあなたが望む結果を得ることを実証するための簡単な例です:

var a = new[] {1, 2, 3, 4, 4, 3, 1, 1, 2};
var b = new[] { 4, 3, 2, 3, 1, 1, 1, 4, 2 };

// result below should be true, since the two sets are equivalent...
var areEquivalent = (a.Count() == b.Count()) && !a.Except(b).Any(); 
45
LBushkin

あなたが発生の数を気にしないのであれば、私はこのようにしてアプローチするでしょう。ハッシュセットを使用すると、単純な反復よりも優れたパフォーマンスが得られます。

var set1 = new HashSet<MyType>(list1);
var set2 = new HashSet<MyType>(list2);
return set1.SetEquals(set2);

これには.GetHashCode()をオーバーライドしてMyTypeIEquatable<MyType>を実装する必要があります。

39
recursive

Guffaの答えに加えて、この変形を使ってもっと短い表記をすることもできます。

public static bool ScrambledEquals<T>(this IEnumerable<T> list1, IEnumerable<T> list2)
{
  var deletedItems = list1.Except(list2).Any();
  var newItems = list2.Except(list1).Any();
  return !newItems && !deletedItems;          
}
11
Thomas Luijken

これを考えることはあなたが望むことをするべきである:

list1.All(item => list2.Contains(item)) &&
list2.All(item => list1.Contains(item));

区別したい場合は、次のように変更できます。

list1.All(item => list2.Contains(item)) &&
list1.Distinct().Count() == list1.Count &&
list1.Count == list2.Count
7
Brian Genisio

これは少し難しい問題ですが、「2つのリストが互いの順列になっているかどうかをテストする」ということにします。

私は他の人によって提供された解決策が2つのリストが同じnique要素を含んでいるかどうかを示すだけであると信じます。これは必要ですが不十分なテストです。例えば、{1, 1, 2, 3}{3, 3, 1, 2}の順列ではありませんが、それらの数は等しく、同じ個別の要素を含んでいます。

もっとも効率的ではありませんが、これでもうまくいくはずです。

static bool ArePermutations<T>(IList<T> list1, IList<T> list2)
{
   if(list1.Count != list2.Count)
         return false;

   var l1 = list1.ToLookup(t => t);
   var l2 = list2.ToLookup(t => t);

   return l1.Count == l2.Count 
       && l1.All(group => l2.Contains(group.Key) && l2[group.Key].Count() == group.Count()); 
}
6
Ani

これは私のために働いた:
IDのように、2つのオブジェクトのリストが単一のエンティティに依存していて、その条件に一致する3番目のリストが必要な場合は、次のようにします。

list3=List1.Where(n => !List2.select(n1 => n1.Id).Contains.(n.Id));

参照してください: MSDN - C#のオブジェクトの2つのリストを比較

2
Suhail

私はこの方法を使います

public delegate bool CompareValue<in T1, in T2>(T1 val1, T2 val2);

public static bool CompareTwoArrays<T1, T2>(this IEnumerable<T1> array1, IEnumerable<T2> array2, CompareValue<T1, T2> compareValue)
{
    return array1.Select(item1 => array2.Any(item2 => compareValue(item1, item2))).All(search => search)
            && array2.Select(item2 => array1.Any(item1 => compareValue(item1, item2))).All(search => search);
}
0
TDG