Как мне оптимизировать проверку любых добавленных, удаленных или обновленных элементов в списке с помощью Linq to Objects?

#c# #linq #linq-to-objects

#c# #linq #linq-to-objects

Вопрос:

Мне нужно проверить, произошла ли какая-либо операция (вставка, удаление или обновление) с элементами, существующими в общей коллекции. У меня есть список с исходными объектами и список с новыми. Ссылки для двух объектов с одинаковым идентификатором не будут одинаковыми.

Прямо сейчас я смог решить проблему, но при выполнении 3 операций. Вот мой код:

 var oldList = new List<MyClass>();
var newList = new List<MyClass>();

//Search for items on the new list that are not present on the old one
var addedItems = newList.Where(item => !oldList.Select(x => x.Id).Contains(item.Id));

//Search for items on the old list that are not present on the new one
var deletedItems = oldList.Where(item => !newList.Select(x => x.Id).Contains(item.Id));

//Search for items present on both lists, but with any property changed
var updatedItems = from x in oldList
                   join y in newList on x.Id equals y.Id
                   where x.Name != y.Name ||
                         x.Description != y.Description ||
                         x.Quantity != y.Quantity
                   select new { OriginalEntity = x, NewEntity = y };

bool anyChanges = addedItems.Count() > 0   ||
                  deletedItems.Count() > 0 ||
                  updatedItems.Count() > 0;
  

Этот код работает, но я хотел бы знать, возможно ли достичь результата более чистым или быстрым способом (меньше операций).

Я знаю, что мог бы реализовать IEquatable<T> в MyClass, но, пожалуйста, учтите, что на данный момент это невозможно (только потому, что я хочу выяснить, есть ли решение, когда это действительно невозможно). В любом случае, это позволило бы мне использовать «Except» и легко обнаруживать вставки или удаления, но не обновления (если я чего-то не упускаю).

Кроме того, я просто хотел отметить, что по SO есть много вопросов с похожими вопросами, но я не нашел ничего, связанного с обнаружением вставки, удаления и обновления одновременно (причина, по которой я публикую это).

Заранее спасибо!

Комментарии:

1. Вы также проверяли ObservableCollection?

2. Вам просто нужно вернуть bool, чтобы указать, равны ли 2 списка?

3. @MikeHixson Да, мне просто нужен bool, чтобы указать, есть ли какие-либо изменения любого рода в более новом списке по сравнению с первым.

4. @MichelKeijzers Я только что проверил ObservableCollection. Я не использовал, но MSDN заявляет, что он может обнаруживать вставки, удаления или когда список обновляется. Итак, не упоминаются изменения свойств объектов в списке. Мне пришлось бы это протестировать.

5. @user1677919 Я уверен, что он не проверяет изменения свойств, но вы можете добавить его вручную с помощью механизма INotify (свойство); но я думаю, что это больше связано с WPF / XAML, не уверен, что вы также можете использовать его независимо.

Ответ №1:

В основном вам просто нужно кэширование улучшенные структуры данных.

 var oldList = new List<MyClass>();
var newList = new List<MyClass>();

// O(1) lookups vs O(N) lookups
var oldListIds = new HashSet<int>(oldList.Select(x => x.Id));
var newListIds new HashSet<int>(newList.Select(x => x.Id));

//Search for items on the new list that are not present on the old one
var addedItems = newList.Where(item => !oldListIds.Contains(item.Id));

//Search for items on the old list that are not present on the new one
var deletedItems = oldList.Where(item => !newListIds.Contains(item.Id));

//Search for items present on both lists, but with any property changed
var updatedItems = from x in oldList
                   join y in newList on x.Id equals y.Id
                   where x.Name != y.Name ||
                         x.Description != y.Description ||
                         x.Quantity != y.Quantity
                   select new { OriginalEntity = x, NewEntity = y };

// Use .Any() instead of .Count() so we stop after first item
bool anyChanges = addedItems.Any()   ||
                  deletedItems.Any() ||
                  updatedItems.Any();
  

Ответ №2:

Как насчет чего-то подобного:

 // determines if evey item in listA has as a match in listB
static bool Matches(List<MyClass> listA, List<MyClass> listB)
{
    var matches = listA.GroupJoin(listB, a => a.Id, b => b.Id,
        (a, b) => b.Any(c => c.Name == a.Name amp;amp; c.Description == a.Description amp;amp; c.Quantity == a.Quantity));

    return matches.All(m => m);
}
  

При этом GroupJoin() возвращает bool, если B соединяется с A. Затем он выполняет All(), чтобы увидеть, все ли соединения возвращают true.

Тогда вы могли бы назвать это так:

 bool equal = Matches(oldList, newList) amp;amp; Matches(newList, oldList);
  

Это возвращает bool, указывающий, все ли в oldList присоединяется к чему-то newList И все ли в newList присоединяется к чему-то в oldList.

Я не уверен, что это будет быстрее, чем другие предложения, но это, безусловно, требует меньше кода.