Сравните два списка объектов для новых, измененных, обновленных по определенному свойству

Я пытался и не смог найти решение для сравнения с списками объектов на основе свойств объектов. Я читал другие подобные решения, но они либо не были подходящими (или я не понял ответа!).

Код - С#

У меня есть модель, представляющая изображение

public class AccommodationImageModel
{
    public int Id { get; set; }
    public string Path { get; set; }
    public string Caption { get; set; }
    public string Description { get; set; }
    public bool CoverImage { get; set; }
    public bool Visible { get; set; }     
}

У меня есть два списка этой модели. Один из них - это существующий список, другой - обновленный список. Мне нужно сравнить два списка, чтобы увидеть, какие из них были удалены, обновлены или новые.

Мне не нужно сравнивать весь объект, просто сравните их с их идентификатором свойства.

List<AccommodationImageModel> masterList;
List<AccommodationImageModel> compareList;

New

Если compareList содержит любой объект AccommodationImageModel с Id = 0, то они новы, потому что новые записи еще не имеют назначенного идентификатора.

Будет удалено

Если masterList содержит любой объект AccommodationImageModel с идентификаторами, которые не указаны в compareList, они должны быть удалены, поскольку они были удалены из списка сравнения и должны быть удалены из masterList. Поэтому мне нужен список тех, которые нужно удалить.

Для обновления

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

Каждый из трех результатов должен быть возвращен как List of AccommodationImageModel, чтобы затем я мог выполнить соответствующее обновление, удалить, добавить.

Изменить

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

Методы тестирования

[TestMethod]
    public void Test_Deleted_Image()
    {
        // set up the masterList
        List<AccommodationImageModel> masterList = new List<AccommodationImageModel>();
        masterList.Add(new AccommodationImageModel { Id = 1 });
        masterList.Add(new AccommodationImageModel { Id = 2 });

        // set up the compare list
        List<AccommodationImageModel> compareList = new List<AccommodationImageModel>();
        compareList.Add(new AccommodationImageModel { Id = 1 });
        compareList.Add(new AccommodationImageModel { Id = 3 });
        compareList.Add(new AccommodationImageModel { Id = 0 });

        // get the deleted models
        List<AccommodationImageModel> result = masterList.Where(c => !compareList.Any(d => d.Id == c.Id)).ToList();

        // result should hold first model with id 2
        Assert.AreEqual(2, result.FirstOrDefault().Id);
    }

    [TestMethod]
    public void Test_Added_Image()
    {
        // set up the masterList
        List<AccommodationImageModel> masterList = new List<AccommodationImageModel>();
        masterList.Add(new AccommodationImageModel { Id = 1 });
        masterList.Add(new AccommodationImageModel { Id = 2 });

        // set up the compare list
        List<AccommodationImageModel> compareList = new List<AccommodationImageModel>();
        compareList.Add(new AccommodationImageModel { Id = 1 });
        compareList.Add(new AccommodationImageModel { Id = 3 });
        compareList.Add(new AccommodationImageModel { Id = 0 });

        // get the added models
        List<AccommodationImageModel> result = compareList.Where(c => c.Id == 0).ToList();

        // result should hold first model with id 0
        Assert.AreEqual(0, result.FirstOrDefault().Id);
    }

    [TestMethod]
    public void Test_Updated_Image()
    {
        // set up the masterList
        List<AccommodationImageModel> masterList = new List<AccommodationImageModel>();
        masterList.Add(new AccommodationImageModel { Id = 1 });
        masterList.Add(new AccommodationImageModel { Id = 2 });

        // set up the compare list
        List<AccommodationImageModel> compareList = new List<AccommodationImageModel>();
        compareList.Add(new AccommodationImageModel { Id = 1 });
        compareList.Add(new AccommodationImageModel { Id = 3 });
        compareList.Add(new AccommodationImageModel { Id = 0 });

        // get the updated models
        List<AccommodationImageModel> result = masterList.Where(c => compareList.Any(d => c.Id == d.Id)).ToList();

        // result should hold first model with id 1
        Assert.AreEqual(1, result.FirstOrDefault().Id);
    }

Ответ 1

Простой Linq

New

List<AccommodationImageModel> new = compareList.Where(c=>c.id==0).ToList();

Будет удалено

List<AccomodationImageModel> deleted = masterList.Where(c => !compareList.Any(d => c.id == d.id)).ToList();

Для обновления

List<AccomodationImageModel> toBeUpdated = masterList.Where(c => compareList.Any(d => c.id == d.id)).ToList();

Ответ 2

Предполагая, что две модели с одинаковыми Id считаются одной и той же моделью, вы можете написать IEqualityComparer следующим образом:

public class AccommodationImageModelComparer : IEqualityComparer<AccommodationImageModel>
{
    public bool Equals(AccommodationImageModel x, AccommodationImageModel y)
    {
        if(x == null && y == null)
           return true;

        return x.Id == y.Id;
    }

    public int GetHashCode(AccommodationImageModel model)
    {
        return model.Id.GetHashCode();
    }
}

Затем вы можете использовать Linq для получения списков, которые вы хотите:

var comparer = new AccommodationImageModelComparer();

var newItems = compareList.Where (l => l.Id == 0).ToList();
var toBeDeleted = masterList.Except(compareList, comparer).ToList();
var toBeUpdated = masterList.Intersect(compareList, comparer).ToList();

Первый фильтр просто фильтрует элементы с Id из 0, которые являются новыми. Второй запрос возвращает элементы в masterList, которые не находятся в compareList. Последний запрос возвращает элементы, которые находятся в обоих списках. Этот код компилируется, но не проверен.

Ответ 3

Одним простым подходом было бы переопределить оператор == в вашем AccomodationImageModel как таковой:

public static override bool operator ==(AccommodationImageModel a, AccommodationImageModel b)
{
    return a.Id == b.Id;
}

Затем при сравнении просто проверьте главный список на список сравнения и удалите те из главного списка, у которых нет идентичного объекта в списке сравнения:

List<AccomodationImageModel> rem = new List<AccomodationImageModel>;
List<AccomodationImageModel> newobj = new List<AccomodationImageModel>;
foreach(AccomodationImageModel a in compareList) {
          if(a.Id == 0) { // id == 0 => new item
                 newobj.Add(a); // add new item later
          } else {
                 // check those existing items as to whether they need to be updated or removed
                 bool match = false;
                 foreach(AccomodationImageModel b in masterList) {
                      if(a == b) match = true; // match found
                 }
                 if(!match) rem.Add(a); else Update(a); // will be removed or updated
          }
}
// now remove unmatched items
foreach(AccomodationImageModel a in rem) { masterList.Remove(a); }
foreach(AccomodationImageModel a in newobj) { AddNew(a); }

Примечание Update(AccomodationImageModel a) - это ваш метод для обновления определенного элемента, а AddNew(AccomodationImageModel a) - ваш метод ввода нового элемента в главный список.

Также, поскольку вы можете удалить заметки и вставить в главный список после, вы зациклировали главный список!

Ответ 4

    ///If image is not in list then add the image to the list

public void AddNew (List<AccomodationImageModel> masterList, AccomodationImageModel theImage)
{
    masterList.Add(theImage);
}

    /// If Image is in List then change listitem with new one

public void Update (List<AccomodationImageModel> masterList, int OldOnesID, AccomodationImageModel theNew)
{
    masterList[OldOnesID] = theNew;
}

    /// If Image should delete then removes the image from list

public void Delete (List<AccomodationImageModel> imgList, AccomodationImageModel theImageToDelete)
{
    masterList.Remove(theImageToDelete);
}


    /// this method checks the image state and do the work

public void CheckState (List<AccommodationImageModel> masterList, AccomodationImageModel theImage, bool deleteIt)
{


       for(int i = 0; i < masterList.Count; i++)
       {

         if (deleteIt)
         {
            Delete(masterList, theImage);
         }

         else
         {
           if(theImage.ID == 0)
           {
             AddNew(masterList, theImage);
           }

           if(masterList[i].ID == theImage.ID)
           {
             Update(masterList, i, theImage);
           }
       }
}

Если вы предпочитаете использовать 2 списка в качестве основного и временного списка, то вы можете просто перебрать свой временный список, а каждый элемент templist использовать метод CheckState.

Надеюсь, что это поможет.