比較コレクション"平等にかかわらず、各項目にて
-
09-06-2019 - |
質問
私の二つのコレクション(C#)が、なんなのか、最適な方法が分からない実効果的です。
を読んで、他のスレッドの約 Enumerable.SequenceEqual, などもやってみたいです。
私の場合は、所蔵する等しい場合はその両方が同じ項目なしています。
例:
collection1 = {1, 2, 3, 4};
collection2 = {2, 4, 1, 3};
collection1 == collection2; // true
私が通常はループを通じて各項目の収集-見に存在する場合には、他のすべてのコレクション、ループを通じて各項目の他のすべてのコレクションを見に存在する場合には、フラワーデモンストレーション.(だんだんとの比較により長さ).
if (collection1.Count != collection2.Count)
return false; // the collections are not equal
foreach (Item item in collection1)
{
if (!collection2.Contains(item))
return false; // the collections are not equal
}
foreach (Item item in collection2)
{
if (!collection1.Contains(item))
return false; // the collections are not equal
}
return true; // the collections are equal
しかし、このイメージが広がり、内容に間違いがないか確認し、あるんじゃないでしょうかは、最も効率的方法の二つのコレクションを進めていきます。
例として私が考えられることはいう間違いには:
collection1 = {1, 2, 3, 3, 4}
collection2 = {1, 2, 2, 3, 4}
これ等も実装されます。ばいいだけの人数を数倍の各項目が見つかっていることを確認してくださいカウントが等しい両方ですか?
この例はあるある種のC#(う擬似C#)がお答えにな言語をご希望の際は、ありません。
注意: 使用した整数型の例を簡単かもしれないけど、私は利用できるように参照-typeオブジェクトもしない動作が正しく行としての鍵でのみ、参照のオブジェクトを比較すると、コンテンツ).
解決
もMicrosoftがこの覆われたそのテスティングフレームワーク: CollectionAssert.AreEquivalent
備考
二つのコレクション相場 同じ要素が同じ 量もます。要素 が等しい場合はその値が等しい ない場合もご参照が同じオブジェクトです。
使用反射板、コードを変更した後AreEquivalent()を対応する平等comparer.ではよりも、既存の回答がかかるため、そのnull、実IEqualityComparerとしての効率とエッジの場合確認をしています。プラスで Microsoft :)
public class MultiSetComparer<T> : IEqualityComparer<IEnumerable<T>>
{
private readonly IEqualityComparer<T> m_comparer;
public MultiSetComparer(IEqualityComparer<T> comparer = null)
{
m_comparer = comparer ?? EqualityComparer<T>.Default;
}
public bool Equals(IEnumerable<T> first, IEnumerable<T> second)
{
if (first == null)
return second == null;
if (second == null)
return false;
if (ReferenceEquals(first, second))
return true;
if (first is ICollection<T> firstCollection && second is ICollection<T> secondCollection)
{
if (firstCollection.Count != secondCollection.Count)
return false;
if (firstCollection.Count == 0)
return true;
}
return !HaveMismatchedElement(first, second);
}
private bool HaveMismatchedElement(IEnumerable<T> first, IEnumerable<T> second)
{
int firstNullCount;
int secondNullCount;
var firstElementCounts = GetElementCounts(first, out firstNullCount);
var secondElementCounts = GetElementCounts(second, out secondNullCount);
if (firstNullCount != secondNullCount || firstElementCounts.Count != secondElementCounts.Count)
return true;
foreach (var kvp in firstElementCounts)
{
var firstElementCount = kvp.Value;
int secondElementCount;
secondElementCounts.TryGetValue(kvp.Key, out secondElementCount);
if (firstElementCount != secondElementCount)
return true;
}
return false;
}
private Dictionary<T, int> GetElementCounts(IEnumerable<T> enumerable, out int nullCount)
{
var dictionary = new Dictionary<T, int>(m_comparer);
nullCount = 0;
foreach (T element in enumerable)
{
if (element == null)
{
nullCount++;
}
else
{
int num;
dictionary.TryGetValue(element, out num);
num++;
dictionary[element] = num;
}
}
return dictionary;
}
public int GetHashCode(IEnumerable<T> enumerable)
{
if (enumerable == null) throw new ArgumentNullException(nameof(enumerable));
int hash = 17;
foreach (T val in enumerable.OrderBy(x => x))
hash = hash * 23 + (val?.GetHashCode() ?? 42);
return hash;
}
}
使用例:
var set = new HashSet<IEnumerable<int>>(new[] {new[]{1,2,3}}, new MultiSetComparer<int>());
Console.WriteLine(set.Contains(new [] {3,2,1})); //true
Console.WriteLine(set.Contains(new [] {1, 2, 3, 3})); //false
ただしみやすく心のこもった"の二つの集直:
var comp = new MultiSetComparer<string>();
Console.WriteLine(comp.Equals(new[] {"a","b","c"}, new[] {"a","c","b"})); //true
Console.WriteLine(comp.Equals(new[] {"a","b","c"}, new[] {"a","b"})); //false
最後に、ご利用できますが、平等comparerの選択:
var strcomp = new MultiSetComparer<string>(StringComparer.OrdinalIgnoreCase);
Console.WriteLine(strcomp.Equals(new[] {"a", "b"}, new []{"B", "A"})); //true
他のヒント
シンプルでかなり効率が高いソリューションはソートの両方のコレクションやそれと比較し、同等:
bool equal = collection1.OrderBy(i => i).SequenceEqual(
collection2.OrderBy(i => i));
このアルゴリズムはO(N*logN)、ソリューションはO(N^2).
場合はコレクションにおいて特定のプロパティできる場合もありますが、いずれの実施により、速く。例えば、お客様の両コレクションのハッシュセットを含めないでください重複している。また、かどうかの確認のハッシュセットの一部の要素は非常に早いです。その場合アルゴリズムをこのようなしくできます。
を辞書"辞書名またを設定した各フラワーデモンストレーションを、dict[委員]++;
そして、ループ以上のコレクションしたり、各メンバーはdict[委員]--.
最後に、ループ全員の辞:
private bool SetEqual (List<int> left, List<int> right) {
if (left.Count != right.Count)
return false;
Dictionary<int, int> dict = new Dictionary<int, int>();
foreach (int member in left) {
if (dict.ContainsKey(member) == false)
dict[member] = 1;
else
dict[member]++;
}
foreach (int member in right) {
if (dict.ContainsKey(member) == false)
return false;
else
dict[member]--;
}
foreach (KeyValuePair<int, int> kvp in dict) {
if (kvp.Value != 0)
return false;
}
return true;
}
編集:調査を実施しているのは、同じかこれは同じめとして最も効率的なアルゴリズムです。このアルゴリズムはO(N)をとした場合の辞書使用にはO(1)ルックアップ
まだ誰もここでの影響を強く受けD.ジュニングス)の汎用実装の比較方法(C#):
/// <summary>
/// Represents a service used to compare two collections for equality.
/// </summary>
/// <typeparam name="T">The type of the items in the collections.</typeparam>
public class CollectionComparer<T>
{
/// <summary>
/// Compares the content of two collections for equality.
/// </summary>
/// <param name="foo">The first collection.</param>
/// <param name="bar">The second collection.</param>
/// <returns>True if both collections have the same content, false otherwise.</returns>
public bool Execute(ICollection<T> foo, ICollection<T> bar)
{
// Declare a dictionary to count the occurence of the items in the collection
Dictionary<T, int> itemCounts = new Dictionary<T,int>();
// Increase the count for each occurence of the item in the first collection
foreach (T item in foo)
{
if (itemCounts.ContainsKey(item))
{
itemCounts[item]++;
}
else
{
itemCounts[item] = 1;
}
}
// Wrap the keys in a searchable list
List<T> keys = new List<T>(itemCounts.Keys);
// Decrease the count for each occurence of the item in the second collection
foreach (T item in bar)
{
// Try to find a key for the item
// The keys of a dictionary are compared by reference, so we have to
// find the original key that is equivalent to the "item"
// You may want to override ".Equals" to define what it means for
// two "T" objects to be equal
T key = keys.Find(
delegate(T listKey)
{
return listKey.Equals(item);
});
// Check if a key was found
if(key != null)
{
itemCounts[key]--;
}
else
{
// There was no occurence of this item in the first collection, thus the collections are not equal
return false;
}
}
// The count of each item should be 0 if the contents of the collections are equal
foreach (int value in itemCounts.Values)
{
if (value != 0)
{
return false;
}
}
// The collections are equal
return true;
}
}
編集:と実感していたことん作品のセットでない適切に対応レーション重複した項目です。例えば、{1,1,2}および{2,2,1}が等しいと見なされるからこのアルゴリズムの視点です。がコレクションを設定(または平等が測定できるとされることがありますが、おたのしみください。以下の便利なのです。
の溶液を使ってい:
return c1.Count == c2.Count && c1.Intersect(c2).Count() == c1.Count;
Linqの辞書ものにもとO(N)(注でO(1)コレクションにな同じサイズ)。
かったので、アメニティチェック"を用いSetEqual方法を提案するダニエルのOrderBy/SequenceEquals方法を提案するIgor、私た。結果は以下のとおりであり、O(N*LogN)のためのイゴール-O(N)のための鉱山やダニエル。
私はそのLinqが交差するコードで、好ましい。
__Test Latency(ms)__
N, SetEquals, OrderBy, Intersect
1024, 0, 0, 0
2048, 0, 0, 0
4096, 31.2468, 0, 0
8192, 62.4936, 0, 0
16384, 156.234, 15.6234, 0
32768, 312.468, 15.6234, 46.8702
65536, 640.5594, 46.8702, 31.2468
131072, 1312.3656, 93.7404, 203.1042
262144, 3765.2394, 187.4808, 187.4808
524288, 5718.1644, 374.9616, 406.2084
1048576, 11420.7054, 734.2998, 718.6764
2097152, 35090.1564, 1515.4698, 1484.223
なしの場合は繰り返さない、以下のEqualityComparerできるために使用される蔵のオブジェクトは辞書のキーとして:
public class SetComparer<T> : IEqualityComparer<IEnumerable<T>>
where T:IComparable<T>
{
public bool Equals(IEnumerable<T> first, IEnumerable<T> second)
{
if (first == second)
return true;
if ((first == null) || (second == null))
return false;
return first.ToHashSet().SetEquals(second);
}
public int GetHashCode(IEnumerable<T> enumerable)
{
int hash = 17;
foreach (T val in enumerable.OrderBy(x => x))
hash = hash * 23 + val.GetHashCode();
return hash;
}
}
こちらの のToHashSet()の実装使いました。の ハッシュコードのアルゴリズム らに効果的なコロジカルフットプリントによるJon Skeet).
static bool SetsContainSameElements<T>(IEnumerable<T> set1, IEnumerable<T> set2) {
var setXOR = new HashSet<T>(set1);
setXOR.SymmetricExceptWith(set2);
return (setXOR.Count == 0);
}
解があります。純3.5に System.Collections.Generic
namespace. マイクロソフト社によると, SymmetricExceptWith
は O(n+m) 操作は、 n の数を表す要素が最初のセット m 代表の要素数です。きのいずれであっても、常に付加する等comparerこの機能が必要です。
ません。を除く()
// Create the IEnumerable data sources.
string[] names1 = System.IO.File.ReadAllLines(@"../../../names1.txt");
string[] names2 = System.IO.File.ReadAllLines(@"../../../names2.txt");
// Create the query. Note that method syntax must be used here.
IEnumerable<string> differenceQuery = names1.Except(names2);
// Execute the query.
Console.WriteLine("The following lines are in names1.txt but not names2.txt");
foreach (string s in differenceQuery)
Console.WriteLine(s);
ご利用の場合 Shouldly, 利用できShouldAllBe付が入っています。●
collection1 = {1, 2, 3, 4};
collection2 = {2, 4, 1, 3};
collection1.ShouldAllBe(item=>collection2.Contains(item)); // true
最後に記述することができます。
public static class ShouldlyIEnumerableExtensions
{
public static void ShouldEquivalentTo<T>(this IEnumerable<T> list, IEnumerable<T> equivalent)
{
list.ShouldAllBe(l => equivalent.Contains(l));
}
}
更新
Aオプションパラメータが存在し ShouldBe 方法。
collection1.ShouldBe(collection2, ignoreOrder: true); // true
複製後のものが トッ溶液を比較するための集.このインターネットはモバイル:
これを行う等との比較を問わず順:
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
オリジナルポスト こちらの.
ここでの私の拡張方法の変異体ohadscの答えの場合、この人の役に立
static public class EnumerableExtensions
{
static public bool IsEquivalentTo<T>(this IEnumerable<T> first, IEnumerable<T> second)
{
if ((first == null) != (second == null))
return false;
if (!object.ReferenceEquals(first, second) && (first != null))
{
if (first.Count() != second.Count())
return false;
if ((first.Count() != 0) && HaveMismatchedElement<T>(first, second))
return false;
}
return true;
}
private static bool HaveMismatchedElement<T>(IEnumerable<T> first, IEnumerable<T> second)
{
int firstCount;
int secondCount;
var firstElementCounts = GetElementCounts<T>(first, out firstCount);
var secondElementCounts = GetElementCounts<T>(second, out secondCount);
if (firstCount != secondCount)
return true;
foreach (var kvp in firstElementCounts)
{
firstCount = kvp.Value;
secondElementCounts.TryGetValue(kvp.Key, out secondCount);
if (firstCount != secondCount)
return true;
}
return false;
}
private static Dictionary<T, int> GetElementCounts<T>(IEnumerable<T> enumerable, out int nullCount)
{
var dictionary = new Dictionary<T, int>();
nullCount = 0;
foreach (T element in enumerable)
{
if (element == null)
{
nullCount++;
}
else
{
int num;
dictionary.TryGetValue(element, out num);
num++;
dictionary[element] = num;
}
}
return dictionary;
}
static private int GetHashCode<T>(IEnumerable<T> enumerable)
{
int hash = 17;
foreach (T val in enumerable.OrderBy(x => x))
hash = hash * 23 + val.GetHashCode();
return hash;
}
}
こちらはーソリューションの改善をもたらす この.
public static bool HasSameElementsAs<T>(
this IEnumerable<T> first,
IEnumerable<T> second,
IEqualityComparer<T> comparer = null)
{
var firstMap = first
.GroupBy(x => x, comparer)
.ToDictionary(x => x.Key, x => x.Count(), comparer);
var secondMap = second
.GroupBy(x => x, comparer)
.ToDictionary(x => x.Key, x => x.Count(), comparer);
if (firstMap.Keys.Count != secondMap.Keys.Count)
return false;
if (firstMap.Keys.Any(k1 => !secondMap.ContainsKey(k1)))
return false;
return firstMap.Keys.All(x => firstMap[x] == secondMap[x]);
}
り多様なソリューションにこの問題です。いつ、複製を継続してご利用いただけ並べ替えます。最初にしていることをご確認ください同じですね。その後ソにあります。その後binsearch各項目のコレクションにソートされます。ない場合は見た目の停止、falseを返します。の複雑さにこの:-ソートのフラワーデモンストレーション:NLog(N) 検索項目にならないのは、最初のNLOG(N) なんと2*N*LOG(N)と仮定して試合をお見上げます。これは、その複雑さにソートします。またこれにより、給付を停止した場合は、それより早います。しかし、この場合の両方を整理する前に本比較しい選別によるものを使っておけば間違いないようにqsortのソートします。が最適です。他の代替では、サマーフェスティバルの小さな集がご存知の範囲の要素の使用ビットマスク。このままO(n)時間です。他のインタビューを受けたことがある使用のハッシュ、一見の価値ありです。小さな集いての注意をお読みくださいの選別やビットマスク。ハッシュテーブルの不利益の悪い地域です。再度、これは当たり前のことだどんなトラブルを重複している。したい場合に複製と分別します。
多くの場合のみに適した答えは、Igor Ostrovsky、その他の回答に基づくオブジェクトのハッシュコードです。でも、生成するハッシュコードオブジェクトこの限りではありませんのみに基づく不変の分野などのオブジェクトIdフィールドの場合データベースエンティティ)- なぜことが重要なオーバーライドGetHashCodeがEqualsメソッドがオーバーライド?
このように、この場合の二つのコレクション、結果がtrueの比較方法でも、分野の異なる項目は非平等なものとする。深部比較コレクション、を使用する必要がありIgorの方法を実施IEqualirity.
をお読みくださいコメントのmr.Schniderの最も票を投じます。
ジェームス
を複製、 IEnumerable<T>
場合に設定していることが望ましいと言えま\可能)と"を無視して注"対応することができるでしょうを使用 .GroupBy()
.
私は専門家ではないですが、いまの複雑さを測定すが、私の初歩的な理解では、このO(n)I理O(n^2)としてから行きにはO(n)の運用の中にO(n)のように操作 ListA.Where(a => ListB.Contains(a)).ToList()
.各項目ListB評価等に対し各項目ListA.
のように言ったので、私の理解の複雑さが限られているので、正しいことは一切について知っておきましょ間違っています。
public static bool IsSameAs<T, TKey>(this IEnumerable<T> source, IEnumerable<T> target, Expression<Func<T, TKey>> keySelectorExpression)
{
// check the object
if (source == null && target == null) return true;
if (source == null || target == null) return false;
var sourceList = source.ToList();
var targetList = target.ToList();
// check the list count :: { 1,1,1 } != { 1,1,1,1 }
if (sourceList.Count != targetList.Count) return false;
var keySelector = keySelectorExpression.Compile();
var groupedSourceList = sourceList.GroupBy(keySelector).ToList();
var groupedTargetList = targetList.GroupBy(keySelector).ToList();
// check that the number of grouptings match :: { 1,1,2,3,4 } != { 1,1,2,3,4,5 }
var groupCountIsSame = groupedSourceList.Count == groupedTargetList.Count;
if (!groupCountIsSame) return false;
// check that the count of each group in source has the same count in target :: for values { 1,1,2,3,4 } & { 1,1,1,2,3,4 }
// key:count
// { 1:2, 2:1, 3:1, 4:1 } != { 1:3, 2:1, 3:1, 4:1 }
var countsMissmatch = groupedSourceList.Any(sourceGroup =>
{
var targetGroup = groupedTargetList.Single(y => y.Key.Equals(sourceGroup.Key));
return sourceGroup.Count() != targetGroup.Count();
});
return !countsMissmatch;
}
このグループに参加"ボタンを 力の IEnumerable
's汎用タイプの実施 IComparable
.での
OrderBy
"の定義で設定します。
だいたいそのような想定はい使用するソリューションには以下のようなものを使いますコード:
bool equal = collection1.OrderBy(i => i?.GetHashCode())
.SequenceEqual(collection2.OrderBy(i => i?.GetHashCode()));