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

时间:2016-04-18 18:28:50

标签: c# list enumeration list-comparison

TL; DR版本:

有没有办法枚举两个列表,只有一个元素(可能是一个额外的元素或一个缺失的元素),并且在匹配所有其他元素的同时告诉哪个元素被更改了?

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

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

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

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

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

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

3 个答案:

答案 0 :(得分:2)

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

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

然后你可以得到所需的添加/删除项目:

//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];

答案 1 :(得分:2)

在代码行方面不是最短的解决方案,但如果已知只能单个元素可以一次插入或删除 (但更多而不是整个列表中的单个更改)您可以使用以下循环:

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)max complexicity
  • 更多控制如何找到更改:Except解决方案只能检查相等性,但在这里您可以检查某些模型属性是否已更改而其他模型属性未发生

答案 2 :(得分:0)

如果您将动作状态{无,已添加,已更新,已删除}添加到您的View项目中,则非常容易

View
{
  ActionState State{get;set;}
}

然后您可以从输入列表中查询

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

接下来,根据您可以添加/更新/删除现有列表中相应视图的操作。

希望得到这个帮助。