Compara dos objetos List por igualdad, ignorando el orden

Otra pregunta de comparación de listas.

List list1; List list2; 

Necesito comprobar que ambos tienen los mismos elementos, independientemente de su posición dentro de la lista. Cada objeto MyType puede aparecer varias veces en una lista. ¿Hay una función incorporada que verifique esto? ¿Qué ocurre si garantizo que cada elemento aparece solo una vez en una lista?

EDITAR: Chicos gracias por las respuestas, pero me olvidé de agregar algo, el número de apariciones de cada elemento debe ser el mismo en ambas listas.

Si desea que sean realmente iguales (es decir, los mismos elementos y el mismo número de cada elemento), creo que la solución más simple es ordenar antes de comparar:

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

Editar:

Aquí hay una solución que funciona un poco mejor (unas diez veces más rápido), y solo requiere IEquatable , no 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 manejar cualquier tipo de datos como clave (por ejemplo, tipos anulables como señaló Frank Tzanabetis), puede crear una versión que tome un comparador para el diccionario:

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

Como está escrito, esta pregunta es ambigua. La statement:

… ambos tienen los mismos elementos, independientemente de su posición dentro de la lista. Cada objeto MyType puede aparecer varias veces en una lista.

no indica si desea asegurarse de que las dos listas tengan el mismo conjunto de objetos o el mismo conjunto distinto .

Si desea asegurarse de que las colecciones tengan exactamente el mismo conjunto de miembros sin importar el orden, puede usar:

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

Si desea asegurarse de que dos colecciones tengan el mismo conjunto distinto de miembros (donde se ignoran los duplicados), puede usar:

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

Usar las operaciones establecidas ( Intersect , Union , Except ) es más eficiente que usar métodos como Contains . En mi opinión, también expresa mejor las expectativas de su consulta.

EDITAR: Ahora que ha aclarado su pregunta, puedo decir que desea utilizar el primer formulario, ya que los duplicados importan. Aquí hay un ejemplo simple para demostrar que obtienes el resultado que deseas:

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

Si no te importa el número de apariciones, lo abordaría así. El uso de conjuntos de hash te dará un mejor rendimiento que la simple iteración.

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

Esto requerirá que haya anulado .GetHashCode() e implementado IEquatable en MyType .

Además de la respuesta de Guffa, puedes usar esta variante para tener una notación más descuidada.

 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 esto debería hacer lo que quieras:

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

si quieres que sea distinto, puedes cambiarlo a:

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

Este es un problema levemente difícil, que creo que se reduce a: “Prueba si dos listas son permutaciones entre sí”.

Creo que las soluciones proporcionadas por otros solo indican si las 2 listas contienen los mismos elementos únicos . Esta es una prueba necesaria pero insuficiente, por ejemplo {1, 1, 2, 3} no es una permutación de {3, 3, 1, 2} aunque sus recuentos son iguales y contienen los mismos elementos distintos.

Creo que esto debería funcionar, aunque no es el más 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()); } 

Esto funcionó para mí:
Si está comparando dos listas de objetos que dependen de una única entidad, como ID , y desea una tercera lista que coincida con esa condición, puede hacer lo siguiente:

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

Consulte: MSDN – C # Compare Dos listas de objetos

Yo uso este 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); } 

¡¡¡prueba esto!!!

Usando el siguiente código, puede comparar uno o más campos para generar una lista de resultados según su necesidad. la lista de resultados contendrá solo los artículos 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); 

Responder

Combine Count , Except y Any like this. Como Bioukh mencionó, es más rápido Count primero.

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

Manifestación

Aquí hay un violín para demostrar.

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