2009-11-04 15 views
59

Devo determinare se due set contengono esattamente gli stessi elementi. L'ordine non ha importanza.LINQ: Determina se due sequenze contengono esattamente gli stessi elementi

Ad esempio, i due array deve essere considerato uguale:

IEnumerable<int> data = new []{ 3,5,6,9 }; 
IEnumerable<int> otherData = new []{ 6,5,9,3} 

Un set non può contenere elementi, che non sono altro.

È possibile farlo utilizzando gli operatori di query incorporati? E quale sarebbe il modo più efficace per implementarlo, considerando che il numero di elementi potrebbe variare da poche a centinaia?

+3

Considerate sequenze '{1,1,2}' e '{1,2}' "equivalenti"? –

+0

@ Mehrdad, Sì, vorrei che fossero considerati uguali. – driis

+0

Per "set", presumo tu intendi che tutti gli elementi sono unici? – Kobi

risposta

100

Se si desidera trattare gli array come "set" e l'ordine ignorare e duplicare gli oggetti, è possibile utilizzare HashSet<T>.SetEquals method:

var isEqual = new HashSet<int>(first).SetEquals(second); 

In caso contrario, la soluzione migliore è probabilmente ordinamento entrambe le sequenze nello stesso modo e utilizzando SequenceEqual per confrontarli.

+1

Penso che HashSet . SetEquals era il metodo che stavo cercando :-) – driis

+0

Buona risposta-- Ho dimenticato SetEquals! Se si dispone di duplicati, prima di ordinare dovresti probabilmente copiare le sequenze in un elenco e confrontare prima le lunghezze - questo ti risparmia le (costose) operazioni di ordinamento o SequenceEqual() nel caso in cui le lunghezze siano diverse. –

+2

@ Grant Justin - Non seguo ... È necessario rimuovere i duplicati prima di confrontare le lunghezze, e questo è tanto costoso quanto l'ordinamento. – Kobi

-1

Questo dovrebbe aiutare:

IEnumerable<int> data = new []{ 3,5,6,9 }; 
    IEnumerable<int> otherData = new[] {6, 5, 9, 3}; 

    if(data.All(x => otherData.Contains(x))) 
    { 
     //Code Goes Here 
    } 
+3

È O (n²) in complessità. Pericoloso se hai più di diverse dozzine di oggetti nella tua lista. –

+0

Semplice, ma questo non funzionerà abbastanza bene per il mio scenario. – driis

+4

Questo non verrà rilevato se 'otherData' contiene elementi aggiuntivi. –

3

Se si potrebbe avere duplicati (o se si desidera una soluzione che funziona meglio per gli elenchi più lunghi), mi piacerebbe provare qualcosa di simile:

static bool IsSame<T>(IEnumerable<T> set1, IEnumerable<T> set2) 
{ 
    if (set1 == null && set2 == null) 
     return true; 
    if (set1 == null || set2 == null) 
     return false; 

    List<T> list1 = set1.ToList(); 
    List<T> list2 = set2.ToList(); 

    if (list1.Count != list2.Count) 
     return false; 

    list1.Sort(); 
    list2.Sort(); 

    return list1.SequenceEqual(list2); 
} 

AGGIORNAMENTO: oops, voi ragazzi avete ragione-- la soluzione Except() qui sotto deve guardare in entrambe le direzioni prima di attraversare la strada. E ha pessimo perf per liste più lunghe. Ignora il suggerimento qui sotto! :-)

Ecco un modo semplice per farlo. Si noti che questo presuppone che gli elenchi non abbiano duplicati.

bool same = data.Except (otherData).Count() == 0; 
+3

È possibile utilizzare .Any() anziché Count(), quindi non enumererà tutti gli elementi nell'elenco. –

+4

Cosa succede se 'data = {1,2}, otherData = {1,2,3}'? Dovresti anche controllare il contrario. – Kobi

+0

Questo non funzionerà nel mio scenario, senza controllare entrambi i modi come suggerito da Kobi. E con poche centinaia di elementi, sarei preoccupato per le prestazioni per quell'approccio. – driis

0
  1. In primo luogo, verificare la lunghezza. Se sono diversi, gli insiemi sono diversi.
  2. è possibile eseguire data.Intersect(otherData); e verificare che la lunghezza sia identica.
  3. OPPURE, semplifica l'ordinamento dei set e scorre iterarli.
+1

"data.Intersect (otherData), e controlla che la lunghezza sia identica" - devi assicurarti che la lunghezza sia identica a ENTRAMBE altre sequenze –

+0

@Mark - Sul primo passo dovevi controllare che fossero entrambi della stessa lunghezza , quindi dovresti essere ok. Non è scritto molto bene, sono d'accordo. (anche, parla della lunga coda ... in 2 anni per ottenere un commento ':)') – Kobi

+0

Sì, è vero. Controllare la lunghezza così tanto (dato che è IEnumerable) non è necessariamente fattibile se paragonato con la creazione di un HashSet dalla sequenza 1 e il confronto con la sequenza 2. L'intersect fondamentalmente lo farà comunque. –

41

Suggerisco di ordinare entrambi e di eseguire un confronto elemento per elemento.

data.OrderBy(x => x).SequenceEqual(otherData.OrderBy(x => x)) 

non sono sicuro quanto velocemente l'attuazione di OrderBy è, ma se è un O (n log n) sorta come ci si aspetterebbe l'algoritmo totale è O (n log n) pure.

Per alcuni casi di dati, è possibile migliorare su questo utilizzando un'implementazione personalizzata di OrderBy che ad esempio utilizza un ordinamento di conteggio, per O (n + k), con k la dimensione dell'intervallo in cui si trovano i valori.

-1

In primo luogo verificare se entrambe le raccolte di dati hanno lo stesso numero di elementi ed il controllo se tutti gli elementi di una collezione sono presentati negli altri

 IEnumerable<int> data = new[] { 3, 5, 6, 9 }; 
     IEnumerable<int> otherData = new[] { 6, 5, 9, 3 }; 

     bool equals = data.Count() == otherData.Count() && data.All(x => otherData.Contains(x)); 
+0

Per un array regolare, .Contains è O (N) e. All è anche O (N), rendendo questo O (N^2). Le opzioni di ordinamento e/o set sono migliori se il tuo set di dati non è banalmente piccolo. – Pagefault

+0

Inoltre, non fornisce la risposta corretta se nell'input sono consentiti duplicati. – Pagefault

2

So che questa è una vecchia questione, ma qui è un altro modo per farlo

IEnumerable<int> data = new[] { 3, 5, 6, 9 };    
IEnumerable<int> otherData = new[] { 6, 5, 9, 3 }; 

data = data.OrderBy(d => d); 
otherData = otherData.OrderBy(d => d); 
data.Zip(otherData, (x, y) => Tuple.Create(x, y)).All(d => d.Item1 == d.Item2); 
+1

L'ultima riga è strizzata. Usa 'var isEqual = data.Zip (otherData, (x, y) => x == y). All (w => w)' invece;). –

+0

@ shA, t come è sbagliato int == int? Ho visto che si trattava di un problema con una classe ma il loro non dovrebbe essere qualcosa di sbagliato qui –

+0

alla tua destra stavo usando un'estensione interna prima di –

Problemi correlati