2011-02-03 10 views
15

Duplicar posible:
Test whether two IEnumerable<T> have the same values with the same frequencies¿Cuál es la forma más corta de comparar si dos IEnumerable <T> tienen los mismos elementos en C#?

me escribió

ACTUALIZADO - Corrección:

static bool HaveSameItems<T>(this IEnumerable<T> self, IEnumerable<T> other) 
{ 
    return ! 
    ( 
     other.Except(this).Any() || 
     this.Except(other).Any() 
    ); 
} 

¿No hay un camino más corto? Sé que hay SequenceEqual pero el orden no es importante para mí.

+7

Tenga en cuenta que hay un error en su propio código: debe usar 'Except' en ambas direcciones, ya que realmente desea verificar que la [disyunción exclusiva] (http://en.wikipedia.org/wiki/ Exclusive_disjunction) está vacío. –

+0

¡@Wim corregido! –

+1

Esto tiene un error. Devuelve verdadero para '{1, 1, 2}' y '{1, 2, 2}'. – jason

Respuesta

6

Incluso si el pedido no es importante para usted, no excluye SequenceEqual como una opción viable.

var lst1 = new [] { 2,2,2,2 }; 
var lst2 = new [] { 2,3,4,5 }; 
var lst3 = new [] { 5,4,3,2 }; 

//your current function which will return true 
//when you compare lst1 and lst2, even though 
//lst1 is just a subset of lst2 and is not actually equal 
//as mentioned by Wim Coenen 
(lst1.Count() == lst2.Count() && 
     !lst1.Except(lst2).Any()); //incorrectly returns true 

//this also only checks to see if one list is a subset of another 
//also mentioned by Wim Coenen 
lst1.Intersect(lst2).Any(); //incorrectly returns true 

//So even if order doesn't matter, you can make it matter just for 
//the equality check like so: 
lst1.OrderBy(x => x).SequenceEqual(lst2.OrderBy(x => x)); //correctly returns false 
lst3.OrderBy(x => x).SequenceEqual(lst2.OrderBy(x => x)); // correctly returns true 
+1

Esto es 'O (n log n)'. Existe una solución 'O (n)'. – jason

+0

Ver http://stackoverflow.com/questions/4576723/c- and-linq-want-1-1-2-3-1-2-3-1-returns-true-but-1-1-2- 3-1-2-3-re/4576854 # 4576854 para una solución O (n) LINQ. – Gabe

+0

Corregí la muestra en la pregunta –

6

Aquí hay una solución O(n) que sólo camina cada secuencia una vez (de hecho, es posible que ni siquiera caminar por completo la segunda secuencia, tiene posibilidades de terminación temprana):

public static bool HaveSameItems<T>(IEnumerable<T> a, IEnumerable<T> b) { 
    var dictionary = a.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count()); 
    foreach(var item in b) { 
     int value; 
     if (!dictionary.TryGetValue(item, out value)) { 
      return false; 
     } 
     if (value == 0) { 
      return false; 
     } 
     dictionary[item] -= 1; 
    } 
    return dictionary.All(x => x.Value == 0); 
} 

Una desventaja de esta solución es que no va a interferir con LINQ to SQL, EF, NHiberate etc. muy bien.

+0

Me gustaría envolver esa 'e' en una declaración' using' o simplemente ir con 'foreach' (¿alguna razón para el enfoque' while e.MoveNext() '?). –

+0

@Dan Tao: Buen punto. No hay razón, es solo lo que vino a mí naturalmente. – jason

+0

@Jason: Es gracioso, +1 porque esto es claramente O (n) como dijiste; y, sin embargo, sigo mirándolo y pensando "Pero * debe * haber una mejor manera ..." Ni siquiera sé por qué. –

Cuestiones relacionadas