2011-02-03 11 views
15

Eventuali duplicati:
Test whether two IEnumerable<T> have the same values with the same frequenciesQual è il modo più breve per confrontare se due IEnumerable <T> hanno gli stessi elementi in C#?

ho scritto

AGGIORNAMENTO - Correzione:

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

non c'è un modo più breve? So che c'è SequenceEqual ma l'ordine non ha importanza per me.

+7

Nota che c'è un bug nel tuo codice: devi usare "Eccetto" in entrambe le direzioni, dato che in realtà vuoi controllare che [disgiunzione esclusiva] (http://en.wikipedia.org/wiki/ Exclusive_disjunction) è vuoto. –

+0

@Wim corretto! –

+1

Questo ha un bug. Restituisce vero per '{1, 1, 2}' e '{1, 2, 2}'. – jason

risposta

6

Anche se l'ordine non è importante per voi, non esclude SequenceEqual come un'opzione praticabile.

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

Questo è 'O (n log n)'. Esiste una soluzione 'O (n)'. – jason

+0

Vedere 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 per una soluzione basata su LINQ O (n). – Gabe

+0

Ho corretto il campione nella domanda –

6

Ecco una soluzione O(n) che cammina solo ogni sequenza una sola volta (in realtà, potrebbe anche non camminare completamente la seconda sequenza, ha possibilità di risoluzione anticipata):

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

Uno svantaggio per questa soluzione è che non ha intenzione di interoperare con LINQ a SQL, EF, NHiberate ecc.

+0

Vorrei concludere che 'e' in una istruzione' using' o semplicemente andare con 'foreach' (qualsiasi motivo per l'approccio' while e.MoveNext() '?). –

+0

@Dan Tao: buon punto. Nessun motivo, è solo quello che mi è venuto naturale. – jason

+0

@Jason: È divertente, +1 perché questo è chiaramente O (n) come hai detto tu; eppure continuo a fissarlo e pensare "Ma ci * deve * essere un modo migliore ..." Non so nemmeno perché. –

Problemi correlati