两个清单之间的差异


118

我有两个用CustomsObjects填充的通用列表。

我需要在第三个列表中检索这两个列表(第一个列表中的项目,第二个列表中的项目)之间的差异。

我本以为使用.Except()是个好主意,但我看不到如何使用它。

c# 

Answers:


237

使用Except是完全正确的方法。如果您的类型覆盖EqualsGetHashCode,或者您只对引用类型相等感兴趣(即,如果两个引用引用完全相同的对象,则两个引用只是“相等”),您可以使用:

var list3 = list1.Except(list2).ToList();

如果您需要表达自定义的相等性想法(例如,通过ID),则需要实现IEqualityComparer<T>。例如:

public class IdComparer : IEqualityComparer<CustomObject>
{
    public int GetHashCode(CustomObject co)
    {
        if (co == null)
        {
            return 0;
        }
        return co.Id.GetHashCode();
    }

    public bool Equals(CustomObject x1, CustomObject x2)
    {
        if (object.ReferenceEquals(x1, x2))
        {
            return true;
        }
        if (object.ReferenceEquals(x1, null) ||
            object.ReferenceEquals(x2, null))
        {
            return false;
        }
        return x1.Id == x2.Id;
    }
}

然后使用:

var list3 = list1.Except(list2, new IdComparer()).ToList();

请注意,这将删除所有重复的元素。如果您需要保留重复项,则可能最简单的方法是从中创建集合list2并使用类似方法:

var list3 = list1.Where(x => !set2.Contains(x)).ToList();

18
正如顺便说一句,我想补充一点,Except是一个操作,那么结果列表将有不同的值,如{'A','A','B','C'}.Except({'B','C'}) 收益{'A'}
digEmAll

4
我有这个:{'A','A','B','C'}。Except({'B','C'})返回{'A'},但是有效...但是{'B' ,'C'}。Except({{A','B','C'})不返回{'A'}
MrLister

2
两组的组差异定义为第一组中没有出现在第二组中的成员(引用MS)。因此,{B,C} .Except({A,B,C})应该不返回任何内容,因为B和C都在第二组中。不是错误,更多与函数的数学定义有关。
史蒂夫·希伯特

因此,@ JonSkeet如果我想基于两个属性比较两个列表,我可以编写这种方式的比较器并获得不相等的项目对吗?
Ehsan Sajjad,

1
@NetMage:OP表示他们希望“第一个中的项目没有第二个中的项目”-对我来说,这听起来像是一个既定的区别。如果第一个列表包含{5、5、5、5、1},而第二个列表包含{5},则第一个列表中只有1个,而第二个列表中没有。
乔恩·斯基特

72

您可以执行以下操作:

var result = customlist.Where(p => !otherlist.Any(l => p.someproperty == l.someproperty));

它为我节省了一个foreach循环。
alice7'2

12
这些“ l.someproperty”之一不应该是“ p.someproperty”吗?
Manos Dilaverakis

6
这可以使用customlist.Where(p => otherlist.Any(l => p.someproperty!= l.someproperty))简化。
Dhanuka777 '16

@ Dhanuka777不正确。本Any应该是一个All,如果你想那样做。这是因为第二个列表可以包含第一个列表中的项目,但是如果不是第一个列表中的项目,则它将立即为true p.someproperty != l.someproperty。这将导致返回两个列表中都存在的项目。对此提出支持的6个人感到羞耻。
Murphybro2 '19

26

我想强调一点很重要-使用Except方法将返回第一个中的项目,而第二个中的项目不会返回。它不会返回第二个没有出现在第二个中的元素。

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list3 = list1.Except(list2).ToList(); //list3 contains only 1, 2

但是,如果您想获得两个列表之间的真正差异:

在第一个项目中没有第二个项目的项目,在第二个项目中没有第一个项目的项目。

您需要两次使用“除外”:

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list3 = list1.Except(list2); //list3 contains only 1, 2
var list4 = list2.Except(list1); //list4 contains only 6, 7
var resultList = list3.Concat(list4).ToList(); //resultList contains 1, 2, 6, 7

或者,您可以使用HashSet的SymmetricExceptWith方法。但这会更改调用的集:

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list1Set = list1.ToHashSet(); //.net framework 4.7.2 and .net core 2.0 and above otherwise new HashSet(list1)
list1Set.SymmetricExceptWith(list2);
var resultList = list1Set.ToList(); //resultList contains 1, 2, 6, 7

有帮助 请注意,OP明确表示他们不希望完全不同:“我需要检索这两个列表之间的差异(第一个列表中的项,第二个列表中的项)。”
rsenna '19

10
var third = first.Except(second);

(如果您不喜欢引用惰性集合,也可以ToList()在之后调用Except()。)

所述Except()如果被比较的值是基本数据类型的,如方法比较了使用默认的比较值,intstringdecimal等。

否则,将通过对象地址进行比较,这可能不是您想要的...在这种情况下,使您的自定义对象实现IComparable(或实现一个自定义IEqualityComparer并将其传递给Except()方法)。



1

由于Except扩展方法对两个IEumerables进行操作,因此在我看来它将是O(n ^ 2)运算。如果性能是一个问题(如果说您的列表很大),建议您从list1创建一个HashSet并使用HashSet的ExceptWith方法。


9
Enumerable.Except内部使用HashSet或类似用途。它绝对不使用朴素的O(n ^ 2)算法。
Jim Mischel

@吉姆·米歇尔(Jim Mischel):对,可枚举。除了使用内部Set数据结构并将两个IEnumerable中的项目添加到Set中。希望文档能对此有所说明。
foson 2011年

1

这是我的解决方案:

    List<String> list1 = new List<String>();

    List<String> list2 = new List<String>();

    List<String> exceptValue = new List<String>();

foreach(String L1 in List1) 
{
    if(!List2.Contains(L1)
    {
         exceptValue.Add(L1);
    }
}
foreach(String L2 in List2) 
{
    if(!List1.Contains(L2)
    {
         exceptValue.Add(L2);
    }
}

-1

有点晚了,但是这对我来说是可行的解决方案

 var myBaseProperty = (typeof(BaseClass)).GetProperties();//get base code properties
                    var allProperty = entity.GetProperties()[0].DeclaringType.GetProperties();//get derived class property plus base code as it is derived from it
                    var declaredClassProperties = allProperty.Where(x => !myBaseProperty.Any(l => l.Name == x.Name)).ToList();//get the difference

在上面提到的代码中,我得到了我的基类和派生类列表之间的属性差异


-1
var resultList = checklist.Where(p => myList.All(l => p.value != l.value)).ToList();

注意:“结果列表”是清单中存在但myList中不存在的项
Teezy7 '19

-2
List<ObjectC> _list_DF_BW_ANB = new List<ObjectC>();    
List<ObjectA> _listA = new List<ObjectA>();
List<ObjectB> _listB = new List<ObjectB>();

foreach (var itemB in _listB )
{     
    var flat = 0;
    foreach(var itemA in _listA )
    {
        if(itemA.ProductId==itemB.ProductId)
        {
            flat = 1;
            break;
        }
    }
    if (flat == 0)
    {
        _list_DF_BW_ANB.Add(itemB);
    }
}

接受答案后6年,并没有增加任何新的价值
米奇·

-3

如果两个列表都实现IEnumerable接口,则可以使用LINQ实现此目的。

list3 = list1.where(i => !list2.contains(i));

6
而且,如果您的清单每个包含一百万个项目,那么您将需要等待很长时间。使用IEnumerable.Except代替。
Jim Mischel

3
是的 我只有大约450件物品,我还在等待。使用时要小心。
Marnee KG7SIO

-3
        List<int> list1 = new List<int>();
        List<int> list2 = new List<int>();
        List<int> listDifference = new List<int>();

        foreach (var item1 in list1)
        {
            foreach (var item2 in list2)
            {
                if (item1 != item2)
                    listDifference.Add(item1);
            }
        }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.