Existe um método embutido para comparar collections?

Gostaria de comparar o conteúdo de algumas collections no meu método Equals. Eu tenho um dictionary e um IList. Existe um método embutido para fazer isso?

Editado: Eu quero comparar dois Dictionaries e dois ILists, então eu acho que o que significa igualdade é claro – se os dois dictionarys contêm as mesmas chaves mapeadas para os mesmos valores, então eles são iguais.

Enumerable.SequenceEqual

Determina se duas seqüências são iguais, comparando seus elementos usando um especificado IEqualityComparer (T).

Você não pode comparar diretamente a lista e o dictionary, mas pode comparar a lista de valores do Dicionário com a lista

Como outros sugeriram e notaram, o SequenceEqual é sensível à ordem. Para resolver isso, você pode classificar o dictionary por chave (que é única e, portanto, a sorting é sempre estável) e, em seguida, usar SequenceEqual . A expressão a seguir verifica se dois dictionarys são iguais, independentemente de sua ordem interna:

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

EDIT: Como apontado por Jeppe Stig Nielsen, alguns objects têm um IComparer que é incompatível com seu IEqualityComparer , produzindo resultados incorretos. Ao usar chaves com um object desse tipo, você deve especificar um IComparer correto para essas chaves. Por exemplo, com chaves de string (que exibem esse problema), você deve fazer o seguinte para obter resultados corretos:

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

Além do mencionado SequenceEqual , que

é verdadeiro se duas listas são de igual comprimento e seus elementos correspondentes se comparam iguais de acordo com um comparador

(que pode ser o comparador padrão, ou seja, um Equals() )

Vale ressaltar que em .Net4 há SetEquals em objects ISet , que

ignora a ordem dos elementos e quaisquer elementos duplicados.

Então, se você quiser ter uma lista de objects, mas eles não precisam estar em uma ordem específica, considere que um ISet (como um HashSet ) pode ser a escolha certa.

Dê uma olhada no método 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 Não possui ferramentas poderosas para comparar collections. Eu desenvolvi uma solução simples que você pode encontrar no link abaixo:

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

Isso executará uma comparação de igualdade, independentemente da ordem:

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

Isso verificará se os itens foram adicionados / removidos:

 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 

Isto irá ver quais itens no dictionary mudaram:

 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 

Eu não sabia sobre o método Enumerable.SequenceEqual (você aprende algo todos os dias …), mas eu sugeriria usar um método de extensão; algo assim:

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

Curiosamente, depois de demorar 2 segundos para ler sobre o SequenceEqual, parece que a Microsoft criou a function que descrevi para você.

Isso não está respondendo diretamente às suas perguntas, mas tanto o MS ‘TestTools quanto o NUnit fornecem

  CollectionAssert.AreEquivalent 

que faz praticamente o que você quer.

Para comparar collections, você também pode usar o LINQ. Enumerable.Intersect retorna todos os pares iguais. Você pode comparar dois dictionarys como este:

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

A primeira comparação é necessária porque o dict2 pode conter todas as chaves do dict1 e mais.

Você também pode pensar em variações usando Enumerable.Except e Enumerable.Union que levam a resultados semelhantes. Mas pode ser usado para determinar as diferenças exatas entre conjuntos.

Como sobre este exemplo:

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

Cortesia: https://www.dotnetperls.com/dictionary-equals

Não. A estrutura de coleta não tem nenhum conceito de igualdade. Se você pensar nisso, não há como comparar collections que não sejam subjetivas. Por exemplo, comparando seu IList ao seu Dicionário, eles seriam iguais se todas as chaves estivessem no IList, todos os valores estivessem no IList ou se ambos estivessem no IList? Não há nenhuma maneira óbvia de comparar essas duas collections sem o conhecimento do que elas devem ser usadas, de modo que um propósito geral, método igual, não faz sentido.

Não, porque o framework não sabe como comparar o conteúdo de suas listas.

Veja isso:

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

Não havia, não é e pode não ser, pelo menos eu acreditaria que sim. A razão por trás é a igualdade de coleção é provavelmente um comportamento definido pelo usuário.

Elementos em collections não deveriam estar em uma ordem específica, embora eles tenham uma ordenação naturalmente, não é o que os algoritmos de comparação devem confiar. Digamos que você tenha duas collections de:

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

Eles são iguais ou não? Você deve saber, mas não sei qual é o seu ponto de vista.

Coleções são conceitualmente desordenadas por padrão, até que os algoritmos forneçam as regras de sorting. A mesma coisa que o SQL Server chamará a sua atenção é quando você tenta fazer paginação, ele exige que você forneça regras de sorting:

http://www.google.com/support

Mais outras duas collections:

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

Mais uma vez, eles são iguais ou não? Diz-me tu ..

A repetibilidade de elementos de uma coleção desempenha seu papel em diferentes cenários e algumas collections como Dictionary não permitem elementos repetidos.

Acredito que esses tipos de igualdade sejam definidos pela aplicação e, portanto, a estrutura não forneceu todas as implementações possíveis.

Bem, em geral casos Enumerable.SequenceEqual é bom o suficiente, mas retorna false no seguinte caso:

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

Eu li algumas respostas para perguntas como essa (você pode pesquisar no Google por elas) e o que eu usaria, em geral:

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

Isso significa que uma coleção representa a outra em seus elementos, incluindo tempos repetidos, sem levar em conta o pedido original. Algumas notas da implementação:

  • GetHashCode() é apenas para a ordenação e não para igualdade; Eu acho que é o suficiente neste caso

  • Count() realmente não enumera a coleção e cai diretamente na implementação da propriedade de ICollection.Count

  • Se as referências são iguais, é apenas Boris

Para collections ordenadas (List, Array) use SequenceEqual

para HashSet use SetEquals

para o dictionary que você pode fazer:

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

(Uma solução mais otimizada usará a sorting, mas isso exigirá IComparable )