#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.
Я не уверен, что это будет быстрее, чем другие предложения, но это, безусловно, требует меньше кода.