2009-03-10 16 views
31

¿Hay una cosa construida en LINQ método que puede utilizar para averiguar si dos secuencias contienen los mismos elementos, no teniendo el orden en cuenta?C#: comparar el contenido de dos IEnumerables

Por ejemplo:

{1, 2, 3} == {2, 1, 3} 
{1, 2, 3} != {2, 1, 3, 4} 
{1, 2, 3} != {1, 2, 4} 

Usted tiene los SequenceEquals, pero entonces tendría que ordenar las dos secuencias en primer lugar, ¿no?

+0

posible duplicado de [Comparación de dos colecciones para la igualdad independientemente del orden de los elementos en ellas] (http://stackoverflow.com/questions/50098/comparing-two-collections-for-equality-irrespective-of-the- order-of-items-in-the) – nawfal

Respuesta

31

Hay bastantes maneras. Supongamos que A y B son IEnumerable.

!A.Except(B).Any() && !B.Except(A).Any() 
A.Count() == B.Count() && A.Intersect(B).Count() == B.Count() 
etc 
+0

usando Intersección allí, es lo que hay que hacer las dos cosas? – Svish

+0

Creo que esto es mucho más lento que ordenar las secuencias. A menos que estés tratando con árboles de expresión. –

+0

@Svish: Sí, * * Qué hay que hacer las dos cosas si y sólo si A = B A Δ B = Φ. –

1

creo ordenar la secuencia es la manera más rápida se puede lograr esto.

+0

Para SequenceEquals tiene que ser del mismo orden. – leppie

+0

"El método SequenceEqual <(Of <(TSource>)>) (IEnumerable <(Of <(TSource>)>), IEnumerable <(Of <(TSource>)>)) enumera las dos secuencias fuente en paralelo y compara los elementos correspondientes utilizando el comparador de igualdad predeterminado para TSource , Defecto." – Svish

+0

Sí, he leído mal la pregunta. Esto es O (nlogn) que es el tiempo asintótico más rápido que un algoritmo puede lograr para este propósito. –

1

Hice esto por la fusión de nuevos elementos en una colección sin duplicados, Se necesitan dos colecciones y devuelve todos los elementos, con los duplicados a cabo

List<Campaign> nonMatching = (from n in newCampaigns 
where !(from e in Existing select e.Id).Contains<int>(n.Id) 
select n).ToList<Campaign>(); 

Ahora quitando el! para la contiene comunicado

List<Campaign> nonMatching = (from n in newCampaigns 
where (from e in Existing select e.Id).Contains<int>(n.Id) 
select n).ToList<Campaign>(); 

devolverá los duplicados

0

Si usted está realmente haciendo una prueba para ver si hay duplicados, entonces la sugerencia de leppie debería funcionar:

if (A.Except(B).Count == 0 && B.Except(A).Count == 0) {...} 

Pero si solo necesita llegar a un IEnumerable sin duplicados:

var result = A.Union(B).Distinct(); 
3

Pruebe el HashSet c muchacha:

var enumA = new[] { 1, 2, 3, 4 }; 
var enumB = new[] { 4, 3, 1, 2 }; 

var hashSet = new HashSet<int>(enumA); 
hashSet.SymmetricExceptWith(enumB); 
Console.WriteLine(hashSet.Count == 0); //true => equal 

Pero eso solo funciona correctamente si los valores son distintos.

Por ejemplo

var enumA = new[] { 1, 1, 1, 2 }; 
var enumB = new[] { 1, 2, 2, 2 }; 

también son considerados como "igual" con el método mencionado.

+1

'hashSet.SetEquals retorno (enumB);' – Spook

8

Con dos IEnumerables (A y B):

bool equal = (A.Count() == B.Count() && (!A.Except(B).Any() || !B.Except(A).Any())) 

Creo que esto es mejor que la excepción (A) .Count porque toda la excepcion no será evaluado. Se detendrá tan pronto como se encuentre un elemento en el Excepto. Con Count, se evalúa todo Except. Además de esto, podemos evitar la evaluación de estos costosos Excepto simplemente al verificar primero las propiedades del Recuento. Si los recuentos no son iguales, entonces verificamos las excepciones.

+0

Amor que Excepto producirá en la primera discrepancia - agradable! – U007D

2

Si no se preocupan por los duplicados (es decir, se consideraría {1, 2, 3} a ser igual a {1, 2, 3, 2}) a continuación:

new HashSet<int>(A).SetEquals(B) 

(O cualquier tipo es el elemento escriba en lugar de int).

De lo contrario:

public static bool SequenceEqualUnordered<T>(IEnumerable<T> first, IEnumerable<T> second) 
{ 
    if (first == null) 
     return second == null; // or throw if that's more appropriate to your use. 
    if (second == null) 
     return false; // likewise. 
    var dict = new Dictionary<T, int>(); // You could provide a IEqualityComparer<T> here if desired. 
    foreach(T element in first) 
    { 
     int count; 
     dict.TryGetValue(element, out count); 
     dict[element] = count + 1; 
    } 
    foreach(T element in second) 
    { 
     int count; 
     if (!dict.TryGetValue(element, out count)) 
      return false; 
     else if (--count == 0) 
      dict.Remove(element); 
     else 
      dict[element] = count; 
    } 
    return dict.Count == 0; 
} 

llevar la cuenta de cada elemento de la primera secuencia, a continuación, comprobar la segunda contra ella. En el momento en que tenga demasiados en la segunda secuencia, puede devolver el resultado falso; de lo contrario, si no le queda nada en el diccionario de recuentos, son iguales o falsos si quedan elementos.

En lugar de los dos tipos O (n log n) de usar OrderBy() seguidos por la comparación O (n), tiene una operación O (n) que genera el conjunto de recuentos, y un O (n) verificación contra eso.