Esiste un metodo integrato per confrontare le collezioni?

Mi piacerebbe confrontare il contenuto di un paio di collezioni nel mio metodo Equals. Ho un dizionario e un IList. C’è un metodo integrato per fare questo?

Modificato: voglio confrontare due dizionari e due ILists, quindi penso che ciò che significa l’uguaglianza sia chiaro – se i due dizionari contengono le stesse chiavi mappate agli stessi valori, allora sono uguali.

Enumerable.SequenceEqual

Determina se due sequenze sono uguali confrontando i loro elementi usando un IEqualityComparer (T) specificato.

Non è ansible confrontare direttamente l’elenco e il dizionario, ma è ansible confrontare l’elenco di valori del Dizionario con l’elenco

Come altri hanno suggerito e notato, SequenceEqual è sensibile agli ordini. Per risolvere ciò, è ansible ordinare il dizionario per chiave (che è univoco, e quindi l’ordinamento è sempre stabile) e quindi utilizzare SequenceEqual . La seguente espressione controlla se due dizionari sono uguali indipendentemente dal loro ordine interno:

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

EDIT: Come sottolineato da Jeppe Stig Nielsen, alcuni oggetti hanno un IComparer che non è compatibile con il loro IEqualityComparer , producendo risultati errati. Quando si utilizzano le chiavi con un object di questo tipo, è necessario specificare un IComparer corretto per tali chiavi. Ad esempio, con le chiavi stringa (che presentano questo problema), per ottenere risultati corretti, è necessario effettuare le seguenti operazioni:

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

Oltre al citato SequenceEqual , che

è vero se due liste sono di uguale lunghezza ed i loro elementi corrispondenti si confrontano in modo uguale secondo un comparatore

(che può essere il comparatore predefinito, ovvero un overridato Equals() )

vale la pena ricordare che in. Net4 ci sono SetEquals su oggetti ISet , che

ignora l’ordine degli elementi e di eventuali elementi duplicati.

Quindi, se vuoi avere una lista di oggetti, ma non è necessario che siano in un ordine specifico, considera che un ISet (come un HashSet ) potrebbe essere la scelta giusta.

Dai un’occhiata al metodo Enumerable.SequenceEqual

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

.NET Manca qualsiasi potente strumento per confrontare le collezioni. Ho sviluppato una soluzione semplice che puoi trovare al link sottostante:

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

Questo eseguirà un confronto di uguaglianza indipendentemente dall’ordine:

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

Questo controllerà se gli articoli sono stati aggiunti / rimossi:

 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 

Questo vedrà quali elementi nel dizionario sono cambiati:

 var original = new Dictionary() { { 1, "a" }, { 2, "b" } }; var changed = new Dictionary() { { 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 

Non conoscevo il metodo Enumerable.SequenceEqual (impari qualcosa ogni giorno ….), ma stavo per suggerire di usare un metodo di estensione; qualcosa come questo:

  public static bool IsEqual(this List InternalList, List 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; } 

È interessante notare che, dopo aver impiegato 2 secondi per leggere SequenceEqual, sembra che Microsoft abbia sviluppato la funzione che ho descritto per voi.

Questo non risponde direttamente alle tue domande, ma forniscono sia MS ‘TestTools che NUnit

  CollectionAssert.AreEquivalent 

che fa praticamente quello che vuoi.

Per confrontare le raccolte puoi anche usare LINQ. Enumerable.Intersect restituisce tutte le coppie uguali. Puoi comporre due dizionari come questo:

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

Il primo confronto è necessario perché dict2 può contenere tutte le chiavi da dict1 e altro.

Puoi anche utilizzare le varianti utilizzando Enumerable.Except e Enumerable.Union che portano a risultati simili. Ma può essere usato per determinare le differenze esatte tra le serie.

Che ne dici di questo esempio:

  static void Main() { // Create a dictionary and add several elements to it. var dict = new Dictionary(); dict.Add("cat", 2); dict.Add("dog", 3); dict.Add("x", 4); // Create another dictionary. var dict2 = new Dictionary(); 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); } 

Per gentile concessione: https://www.dotnetperls.com/dictionary-equals

No. La struttura della collezione non ha alcun concetto di uguaglianza. Se ci pensi, non c’è modo di confrontare collezioni che non sono soggettive. Per esempio confrontando il tuo IList con il tuo Dizionario, sarebbero uguali se tutte le chiavi fossero nell’IList, tutti i valori fossero nell’IList o se entrambi fossero nell’IList? Non esiste un modo ovvio per confrontare queste due collezioni senza la conoscenza di ciò che devono essere utilizzate, quindi un metodo di uguaglianza generale non ha senso.

No, perché il framework non sa come confrontare il contenuto dei tuoi elenchi.

Dai un’occhiata a questo:

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

 public bool CompareStringLists(List list1, List list2) { if (list1.Count != list2.Count) return false; foreach(string item in list1) { if (!list2.Contains(item)) return false; } return true; } 

Non c’era, non è e potrebbe non esserlo, almeno lo credo. Il motivo dietro è l’uguaglianza della raccolta è probabilmente un comportamento definito dall’utente.

Gli elementi nelle collezioni non dovrebbero essere in un ordine particolare sebbene abbiano un ordinamento naturale, non è ciò su cui gli algoritmi di confronto dovrebbero fare affidamento. Supponi di avere due raccolte di:

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

Sono uguali o no? Devi sapere ma non so qual è il tuo punto di vista.

Le raccolte sono concettualmente non ordinate per impostazione predefinita, fino a quando gli algoritmi forniscono le regole di ordinamento. La stessa cosa che SQL Server ti porta alla tua attenzione è quando provi a fare l’impaginazione, ti richiede di fornire le regole di ordinamento:

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

Ancora altre due raccolte:

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

Di nuovo, sono uguali o no? Dimmelo tu ..

La ripetibilità dell’elemento di una collezione gioca il suo ruolo in diversi scenari e alcune raccolte come Dictionary non consentono nemmeno elementi ripetuti.

Credo che questi tipi di uguaglianza siano definiti dalle applicazioni e quindi il framework non ha fornito tutte le possibili implementazioni.

Bene, in generale Enumerable.SequenceEqual è abbastanza buono ma restituisce false nel seguente caso:

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

Ho letto alcune risposte a domande come questa (potresti google per loro) e cosa userei, in generale:

 public static class CollectionExtensions { public static bool Represents(this IEnumerable first, IEnumerable second) { if(object.ReferenceEquals(first, second)) { return true; } if(first is IOrderedEnumerable && second is IOrderedEnumerable) { return Enumerable.SequenceEqual(first, second); } if(first is ICollection && second is ICollection) { if(first.Count()!=second.Count()) { return false; } } first=first.OrderBy(x => x.GetHashCode()); second=second.OrderBy(x => x.GetHashCode()); return CollectionExtensions.Represents(first, second); } } 

Ciò significa che una raccolta rappresenta l’altra nei loro elementi, inclusi i tempi ripetuti senza tenere conto dell’ordine originale. Alcune note sull’implementazione:

  • GetHashCode() è solo per l’ordine non per l’uguaglianza; Penso che sia abbastanza in questo caso

  • Count() in realtà non enumera la collezione e ricade direttamente nell’implementazione della proprietà di ICollection.Count

  • Se i riferimenti sono uguali, è solo Boris

Per le raccolte ordinate (Elenco, Serie) utilizzare SequenceEqual

per HashSet usa SetEquals

per il dizionario puoi fare:

 namespace System.Collections.Generic { public static class ExtensionMethods { public static bool DictionaryEquals(this IReadOnlyDictionary d1, IReadOnlyDictionary 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; } } } 

(Una soluzione più ottimizzata utilizzerà l’ordinamento, ma ciò richiederà IComparable )