比较两个列表以在其中一个列表中查找添加或删除的元素

Comparing two lists to find added or removed element in one of them

TL;DR 版本:

有没有一种方法可以枚举恰好相差一个元素(可能是附加元素或缺失元素)的两个列表,并在匹配所有其他元素的同时指出更改了哪个元素?


我的 class 有一个 List<View> 属性,它是通过构造函数注入的 List<Model> 创建的。每个 View 将其各自的 Model 包装为 public 属性.

在相同的范围内,有一个void Update(List<Model> newList)例程,它接收另一个Model的列表,条件如下:它与之前的List<Model>相同,恰好加上或减去一个元素。这是因为每次在其他地方的数据绑定列表中添加或删除项目时都会调用 Update,并且应用程序一次只能选择 add/remove 一个元素。

Update 操作的动机,而不是简单地用新列表替换旧列表的模型(包装在视图中),是 View classes store state,所以我想替换他们的模型,但保留他们的状态——无论如何都不会存储在模型中。

问题是:如果有新模型,我必须添加新视图,当相应模型不存在时,我必须删除相应视图,并且我必须 将现有视图与其匹配已经存在的模型,除了添加或删除的元素。我不确定该怎么做。

所以我在考虑将列表第一个元素中的模型与新列表的第一个元素进行比较,如果它们匹配则转到第二个元素,依此类推。在某一点上,它们会有所不同,但这可能是因为添加了一个元素(然后剩余的元素将移动一个索引),或者由于删除了一个元素(在这种情况下,剩余的元素将向下移动,并且不同的元素实际上是列表中已经存在的元素)。

是否有任何明智的方法来判断第一个不同的元素是由于添加还是删除?

如果您像这样向您的 View 项目添加操作状态 {None、已添加、已更新、已删除},这将非常容易

View
{
  ActionState State{get;set;}
}

然后您可以像这样从您的输入列表中查询

var changedElement= views.FirstOrDefault(x=> x.State != ActionState.None);

接下来,根据操作,您可以 add/update/remove 现有列表中的相应视图。

希望对您有所帮助。

您可以使用 Except() 扩展名,例如:

List<Model> models = new List<Model>();
List<View> views = new List<View>();

然后您可以获得所需的 added/removed 项目:

//If models.Count > views.Count
var addedModel = models.Except(views.Select(v => v.Model)).FirstOrDefault();

//If views.Count > models.Count
var modelToRemove = views.Select(v => v.Model).Except(models).FirstOrDefault();
var viewToRemove = views.Single(v => v.Model == modelToRemove);

也许您需要传递 IEqualityComparer<Model> 进行自定义比较。


如果列表是或可以根据某种标准排序,你可以按照你在问题中所说的,找到列表不同的索引:

//for simplicity sorts the lists according to some 'Id'
models.Sort((a, b) => a.Id.CompareTo(b.Id));
views.Sort((a, b) => a.Model.Id.CompareTo(b.Model.Id));

然后,获取比较值的索引:

var index = Enumerable.Range(0, Math.Min(models.Count, views.Count))
                      .First(i => !Model.Equals(models[i], views[i].Model));

//If models.Count > views.Count
var addedModel = models[index];

//If views.Count > models.Count
var viewToRemove = views[index];

就代码行而言,这不是最短的解决方案,但如果知道一次不能插入或删除 单个 元素 (但整个列表不止一个变化)你可以使用下面的循环:

var i = 0;
var j = 0;

while(i < oldList.Count || j < newList.Count)
{
    if (i >= oldList.Count)
    {
        for(; j < newList.Count; j++)
        {
            Console.WriteLine("{0} added", newList[j]);
        }
        break;
    }
    if (j >= newList.Count)
    {
        for(; i < oldList.Count; i++)
        {
            Console.WriteLine("{0} removed", oldList[i]);
        }
        break;
    }

    if (oldList[i] == newList[j])
    {
        Console.WriteLine("{0} not changed", oldList[i]);
        i++;
        j++;
    }
    else if(j < (newList.Count - 1) && oldList[i] == newList[j+1])
    {
        Console.WriteLine("{0} added", newList[j]);
        j++;
    }
    else
    {
        Console.WriteLine("{0} removed", oldList[i]);
        i++;

    }
}

我认为,它比 Except 解决方案有以下优点:

  • 可以找到不止一个变化
  • O(n+m) 最大复杂度
  • 在查找更改的方式方面有更多控制:Except解决方案只能检查相等性,但在这里您可以检查是否某些模型属性已更改而其他模型属性未更改