Compare dos objetos List<T> para determinar la igualdad, ignorando el orden [duplicado]
Otra pregunta más para comparar listas.
List<MyType> list1;
List<MyType> list2;
Necesito comprobar que ambos tengan los mismos elementos, independientemente de su posición dentro de la lista. Cada objeto MyType puede aparecer varias veces en una lista. ¿Existe alguna función incorporada que verifique esto? ¿Qué pasa si garantizo que cada elemento aparece sólo una vez en una lista?
EDITAR: Chicos, gracias por las respuestas, pero olvidé agregar algo, la cantidad de apariciones de cada elemento debe ser la misma en ambas listas.
Si quieres que sean realmente iguales (es decir, los mismos elementos y el mismo número de cada elemento), creo que la solución más sencilla 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 (aproximadamente diez veces más rápido) y solo requiere IEquatable
, no IComparable
:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2) {
var cnt = new Dictionary<T, int>();
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);
}
Edición 2:
Para manejar cualquier tipo de datos como clave (por ejemplo, tipos que aceptan valores NULL como señaló Frank Tzanabetis), puede crear una versión que tome un comparador para el diccionario:
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2, IEqualityComparer<T> comparer) {
var cnt = new Dictionary<T, int>(comparer);
...
Si no le importa la cantidad de ocurrencias, lo abordaría así. El uso de conjuntos de hash le brindará un mejor rendimiento que la simple iteración.
var set1 = new HashSet<MyType>(list1);
var set2 = new HashSet<MyType>(list2);
return set1.SetEquals(set2);
Esto requerirá que haya anulado .GetHashCode()
e implementado IEquatable<MyType>
en MyType
.
Tal como está escrita, esta pregunta es ambigua. La declaración:
... 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 independientemente del orden, puede utilizar:
// 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 en cualquiera de ellas), puede usar:
// check that [(A-B) Union (B-A)] is empty
var areEquivalent = !list1.Except(list2).Union( list2.Except(list1) ).Any();
Usar las operaciones establecidas ( Intersect
,, ) es más eficiente que usar métodos Union
como . En mi opinión, también expresa mejor las expectativas de su consulta.Except
Contains
EDITAR: Ahora que ha aclarado su pregunta, puedo decir que desea utilizar el primer formulario, ya que los duplicados son importantes. A continuación se muestra un ejemplo sencillo para demostrar que obtiene el resultado que desea:
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();