2010-03-08 17 views
16

Ho due elenco dei membri piace questo:Linq trovare le differenze in due liste

Prima: Peter, Ken, Julia, Tom

Dopo: Peter, Robert, Julia, Tom

Come si può vedere, Ken è fuori e Robert è in.

Quello che voglio è rilevare i cambiamenti. Voglio un elenco di ciò che è cambiato in entrambe le liste. Come può linq aiutarmi?

+2

Credo che è necessario definire "cambiamento" un po 'di più. Ad esempio, un cambiamento nell'ordine degli articoli è un "cambiamento"? –

risposta

27

La domanda non è stata completamente specificata, ma presumo che si stiano cercando le differenze come insiemi (ovvero, l'ordine non ha importanza). Se è così, vuoi il symmetric difference dei due set. È possibile raggiungere questo obiettivo usando Enumerable.Except:

before.Except(after).Union(after.Except(before)); 
+0

@Will: Grazie per le correzioni. – jason

5

Un altro modo:

before.Union(after).Except(before.Intersect(after)) 
20

In alternativa a LINQ risposte, che devono passare entrambi gli elenchi per due volte, utilizzare HashSet.SymmetricExceptWith():

var difference = new HashSet(before); 
difference.SymmetricExceptWith(after); 

Could essere notevolmente più efficiente

2

Ecco la O (n) la complessità versione avere, purché le vostre sequenze sono entrambi ordinato:

public static IEnumerable<T> SymmetricDifference<T>(IEnumerable<T> coll1, IEnumerable<T> coll2, IComparer<T> cmp) 
    { 
     using (IEnumerator<T> enum1 = coll1.GetEnumerator()) 
     using (IEnumerator<T> enum2 = coll2.GetEnumerator()) 
     { 
      bool enum1valid = enum1.MoveNext(); 
      bool enum2valid = enum2.MoveNext(); 
      while (enum1valid && enum2valid) 
      { 
       int cmpResult = cmp.Compare(enum1.Current, enum2.Current); 
       if (cmpResult < 0) 
       { 
        yield return enum1.Current; 
        enum1valid = enum1.MoveNext(); 
       } 
       else if (cmpResult > 0) 
       { 
        yield return enum2.Current; 
        enum2valid = enum2.MoveNext(); 
       } 
       else 
       { 
        enum1valid = enum1.MoveNext(); 
        enum2valid = enum2.MoveNext(); 
       } 
      } 
      while (enum1valid) 
      { 
       yield return enum1.Current; 
       enum1valid = enum1.MoveNext(); 
      } 
      while (enum2valid) 
      { 
       yield return enum2.Current; 
       enum2valid = enum2.MoveNext(); 
      } 
     } 
    } 


    public static IEnumerable<T> SymmetricDifference<T>(IEnumerable<T> coll1, IEnumerable<T> coll2) 
    { 
     return SymmetricDifference(coll1, coll2, Comparer<T>.Default); 
    } 
+1

Funzionerebbe solo per le sequenze ordinate, giusto? Non è possibile creare symdiff solo in O (n) per sequenze arbitrarie. – Vlad

+0

Certo che lo fa, basta guardare la co-iterazione. È una versione leggermente modificata di Sequential-merge di merge-sort. Sebbene non funzioni per unsorted, è _molto_efficiente per l'ordinamento, quindi in molti casi sarà più veloce e più facile da memorizzare per ordinare la raccolta e quindi utilizzare questo filtro ~> n + 2 * (n log n) invece di alcuni naiive tutto-contro-tutti n^2 o alcuni maiali di memoria hashtable .. nel caso di _many_ elementi ovviamente :) – quetzalcoatl

Problemi correlati