Compare dois objects List para igualdade, ignorando a ordem

Ainda outra lista de comparação de perguntas.

List list1; List list2; 

Eu preciso verificar se ambos têm os mesmos elementos, independentemente de sua posição na lista. Cada object MyType pode aparecer várias vezes em uma lista. Existe uma function interna que verifica isso? E se eu garantir que cada elemento aparece apenas uma vez em uma lista?

EDIT: caras obrigado pelas respostas, mas eu esqueci de adicionar algo, o número de ocorrências de cada elemento deve ser o mesmo em ambas as listas.

Se você quiser que eles sejam realmente iguais (ou seja, os mesmos itens e o mesmo número de cada item), acho que a solução mais simples é classificar antes de comparar:

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

Editar:

Aqui está uma solução que funciona um pouco melhor (cerca de dez vezes mais rápido) e requer apenas IEquatable , não IComparable :

 public static bool ScrambledEquals(IEnumerable list1, IEnumerable list2) { var cnt = new Dictionary(); 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); } 

Editar 2:

Para manipular qualquer tipo de dados como chave (por exemplo, tipos anuláveis ​​como Frank Tzanabetis apontou), você pode fazer uma versão que leve um comparador para o dictionary:

 public static bool ScrambledEquals(IEnumerable list1, IEnumerable list2, IEqualityComparer comparer) { var cnt = new Dictionary(comparer); ... 

Como está escrito, esta questão é ambígua. A declaração:

… ambos têm os mesmos elementos, independentemente da sua posição na lista. Cada object MyType pode aparecer várias vezes em uma lista.

não indica se você deseja garantir que as duas listas tenham o mesmo conjunto de objects ou o mesmo conjunto distinto .

Se você quiser garantir que as collections tenham exatamente o mesmo conjunto de membros, independentemente do pedido, você pode usar:

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

Se você quiser garantir que duas collections tenham o mesmo conjunto distinto de membros (onde as duplicatas em qualquer um são ignoradas), você pode usar:

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

Usar as operações definidas ( Intersect , Union , Except ) é mais eficiente do que usar methods como Contains . Na minha opinião, também expressa melhor as expectativas da sua consulta.

EDIT: Agora que você esclareceu sua pergunta, posso dizer que você deseja usar o primeiro formulário – desde duplicatas importam. Aqui está um exemplo simples para demonstrar que você obtém o resultado desejado:

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

Se você não se importa com o número de ocorrências, eu abordaria isso assim. Usando conjuntos de hash lhe dará melhor desempenho do que simples iteração.

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

Isso exigirá que você tenha substituído .GetHashCode() e implementado IEquatable em MyType .

Além da resposta de Guffa, você poderia usar essa variante para ter uma notação mais curta.

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

Pensando que isso deve fazer o que você quer:

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

Se você quiser que seja diferente, você pode alterá-lo para:

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

Este é um problema um pouco difícil, que acho que reduz a: “Teste se duas listas são permutações entre si.”

Acredito que as soluções fornecidas por outros apenas indicam se as duas listas contêm os mesmos elementos exclusivos . Este é um teste necessário, mas insuficiente, por exemplo {1, 1, 2, 3} não é uma permutação de {3, 3, 1, 2} embora suas contagens sejam iguais e contenham os mesmos elementos distintos.

Eu acredito que isso deve funcionar, embora não seja o mais eficiente:

 static bool ArePermutations(IList list1, IList 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()); } 

Isso funcionou para mim:
Se você estiver comparando duas listas de objects, depende de uma única entidade, como o ID , e você quer uma terceira lista que corresponda a essa condição, então você pode fazer o seguinte:

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

Consulte: MSDN – C # Compare Duas listas de objects

Eu uso esse método)

 public delegate bool CompareValue(T1 val1, T2 val2); public static bool CompareTwoArrays(this IEnumerable array1, IEnumerable array2, CompareValue 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); } 

tente isso !!!

Usando o seguinte código você pode comparar um ou vários campos para gerar uma lista de resultados conforme sua necessidade. A lista de resultados conterá apenas itens modificados.

 // veriables been used List diffList = new List(); List gotResultList = new List(); // compare First field within my MyList gotResultList = MyList1.Where(a => !MyList2.Any(a1 => a1.MyListTField1 == a.MyListTField1)).ToList().Except(gotResultList.Where(a => !MyList2.Any(a1 => a1.MyListTField1 == a.MyListTField1))).ToList(); // Generate result list diffList.AddRange(gotResultList); // compare Second field within my MyList gotResultList = MyList1.Where(a => !MyList2.Any(a1 => a1.MyListTField2 == a.MyListTField2)).ToList().Except(gotResultList.Where(a => !MyList2.Any(a1 => a1.MyListTField2 == a.MyListTField2))).ToList(); // Generate result list diffList.AddRange(gotResultList); MessageBox.Show(diffList.Count.ToString); 

Responda

Combine Count , Except e Any como este. Como Bioukh mencionou, é mais rápido Count primeiro.

 public static bool AreTheSameIgnoringOrder(List x, List y) { return x.Count() == y.Count() && !x.Except(y).Any() && !y.Except(x).Any(); // re: Servy's comment. } 

Demonstração

Aqui está um violino para demonstrar.

 using System; using System.Collections.Generic; using System.Linq; public class Program { public static void Main() { var x = new List() { "a", "b", "c"}; var result1 = AreTheSameIgnoringOrder(x, new List() { "c", "b", "a"}); Console.WriteLine(result1); var result2 = AreTheSameIgnoringOrder(x, new List() { "c", "b", "a", "b" }); Console.WriteLine(result2); } public static bool AreTheSameIgnoringOrder(List x, List y) { return x.Count() == y.Count() && !x.Except(y).Any() && !y.Except(x).Any(); } }