2010-03-08 14 views
16

Tengo dos lista de los miembros de esta manera:LINQ encontrar las diferencias en dos listas

Antes: Peter, Ken, Julia, Tom

Después de: Peter, Robert, Julia, Tom

Como puede ver, Ken está fuera y Robert está adentro.

Lo que quiero es detectar los cambios. Quiero una lista de lo que ha cambiado en ambas listas. ¿Cómo puede linq ayudarme?

+2

Creo que es necesario definir el "cambio" un poco más. ¿Es un cambio en el orden de los artículos un "cambio" para usted, por ejemplo? –

Respuesta

27

Su pregunta no está completamente especificada pero supongo que está buscando las diferencias como conjuntos (es decir, el orden no importa). Si es así, quiere el symmetric difference de los dos conjuntos. Esto se puede conseguir usando Enumerable.Except:

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

@Will: Gracias por las correcciones. – jason

5

Otra forma:

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

Como alternativa a LINQ respuestas, que tienen que pasar las dos listas en dos ocasiones, utilizan HashSet.SymmetricExceptWith():

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

Podría ser considerablemente más eficiente.

2

Aquí está la versión que presenta una O (n) la complejidad, siempre y cuando sus secuencias se ha dado la orden:

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

Esto funcionaría solo para secuencias ordenadas, ¿verdad? Uno no puede hacer symdiff en solo O (n) para secuencias arbitrarias. – Vlad

+0

Claro que sí, solo mira la co-iteración. Es una versión ligeramente modificada de fusión secuencial de merge-sort. Aunque no funcionará sin clasificar, es muy eficiente para clasificar, por lo que en muchos casos será más rápido y más amigable con la memoria ordenar previamente la colección y luego usar este filtro ~> n + 2 * (n log n) en lugar de algunos naiive all-vs-all n^2 o algunos hots de memoria hashtable ..en caso de _many_ elementos por supuesto :) – quetzalcoatl

Cuestiones relacionadas