2010-03-08 4 views
16

У меня есть два списка членов, как это:Linq найти различия в двух списках

До: Питер, Кен, Джулия, Том

После: Питер, Роберт, Джулия, Том

Как вам может видеть, Кен отсутствует, и Роберт находится.

Я хочу, чтобы обнаружить изменения. Мне нужен список изменений в обоих списках. Как linq поможет мне?

+2

Я думаю, вам нужно определить «изменить» немного больше. Например, изменение в порядке вещей «изменение» для вас? –

ответ

27

Ваш вопрос не полностью указан, но я предполагаю, что вы ищете различия как наборы (то есть, порядок не имеет значения). Если это так, вы хотите, чтобы symmetric difference из двух наборов. Вы можете добиться этого с помощью Enumerable.Except:

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

@Will: Спасибо за исправления. – jason

5

Другой способ:

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

В качестве альтернативы LinQ ответы, которые должны пройти в обоих списках дважды, используйте HashSet.SymmetricExceptWith():

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

Могли быть значительно более эффективными.

2

Вот версия, имеющий O (п) сложность, если ваши последовательности заказали:

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

Это будет работать только для отсортированных последовательностей, верно? Нельзя сделать symdiff только для O (n) для произвольных последовательностей. – Vlad

+0

Уверен, что это так, просто посмотрите на совместную итерацию. Это немного измененная версия последовательного слияния из merge-sort. Хотя он не будет работать для unsorted, он очень эффективен для сортировки, поэтому во многих случаях он будет быстрее и удобнее для памяти для предварительной сортировки коллекции, а затем использовать этот фильтр ~> n + 2 * (n log n) а не какие-то naiive all-vs-all n^2 или некоторые хеш-таблицы памяти hogs .. в случае _many_ элементов, конечно :) – quetzalcoatl

Смежные вопросы