两个列表之间的差异

104 c#

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

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

我在想使用.Except()是一个好主意,但我不知道如何使用这个..帮助!

Jon*_*eet 209

使用Except是正确的方法.如果你的类型覆盖EqualsGetHashCode,或者你只在引用类型相等兴趣(即两个引用只有"平等",如果他们指的是完全相同的对象),你可以使用:

var list3 = list1.Except(list2).ToList();
Run Code Online (Sandbox Code Playgroud)

如果你需要表达一个自定义的平等概念,例如通过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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后使用:

var list3 = list1.Except(list2, new IdComparer()).ToList();
Run Code Online (Sandbox Code Playgroud)

请注意,这将删除任何重复的元素.如果你需要保留重复项,最简单的方法是创建一个集合,list2并使用类似的东西:

var list3 = list1.Where(x => !set2.Contains(x)).ToList();
Run Code Online (Sandbox Code Playgroud)

  • 顺便说一句,我要补充说"Except"是一个*Set*操作,然后结果列表将有不同的值,例如`{'A','A','B','C'}. ({'B','C'})`返回`{'A'}` (16认同)
  • 我有这个:{'A','A','B','C'}.除了({'B','C'}),返回{'A'}有效......但{'B' ,'C'}.除了({'A','B','C'})不返回{'A'} (3认同)
  • 两个集合的集合差定义为第一个集合中没有出现在第二个集合中的成员(引用 MS)。因此, {B,C}.Except({A,B,C}) 不应返回任何内容,因为 B 和 C 都在第二组中。不是错误,更多与函数的数学定义有关。 (3认同)

Ton*_*ion 63

你可以这样做:

var result = customlist.Where(p => !otherlist.Any(l => p.someproperty == l.someproperty));
Run Code Online (Sandbox Code Playgroud)

  • 那些"l.someproperty"不应该是"p.someproperty"吗? (11认同)
  • 这可以通过customlist.Where简化(p => otherlist.Any(l => p.someproperty!= l.someproperty)); (6认同)
  • @Dhanuka777 这是不正确的。如果你想这样做的话,“Any”应该是“All”。这是因为第二个列表可能包含第一个列表中的项目,但如果它不是第一个检查的项目,那么它将立即得到“p.someproperty!= l.someproperty”的 true。这会导致返回两个列表中都存在的项目。为这 6 个投票的人感到羞耻。 (3认同)

Sta*_*cev 23

我认为重要的是要强调 - 使用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
Run Code Online (Sandbox Code Playgroud)

但如果你想在两个列表之间得到真正的区别:

第一个中没有项目的第一个项目和第二个项目中没有项目的项目.

您需要使用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); //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
Run Code Online (Sandbox Code Playgroud)

或者您可以使用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
Run Code Online (Sandbox Code Playgroud)


rse*_*nna 9

var third = first.Except(second);
Run Code Online (Sandbox Code Playgroud)

(如果你不喜欢引用懒惰的集合,也可以ToList()在之后调用Except().)

Except()方法比较了使用默认的比较,如果被比较的值的值是基本数据类型的,例如int,string,decimal等.

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


小智 7

对于此类任务,以下助手可能很有用:

有 2 个集合,分别称为本地集合oldValues和远程集合。newValues 您有时会收到有关远程集合上的某些元素已更改的通知,并且您想知道添加、删除和更新了哪些元素。远程集合始终返回其拥有的所有元素。

    public class ChangesTracker<T1, T2>
{
    private readonly IEnumerable<T1> oldValues;
    private readonly IEnumerable<T2> newValues;
    private readonly Func<T1, T2, bool> areEqual;

    public ChangesTracker(IEnumerable<T1> oldValues, IEnumerable<T2> newValues, Func<T1, T2, bool> areEqual)
    {
        this.oldValues = oldValues;
        this.newValues = newValues;
        this.areEqual = areEqual;
    }

    public IEnumerable<T2> AddedItems
    {
        get => newValues.Where(n => oldValues.All(o => !areEqual(o, n)));
    }

    public IEnumerable<T1> RemovedItems
    {
        get => oldValues.Where(n => newValues.All(o => !areEqual(n, o)));
    }

    public IEnumerable<T1> UpdatedItems
    {
        get => oldValues.Where(n => newValues.Any(o => areEqual(n, o)));
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

        [Test]
    public void AddRemoveAndUpdate()
    {
        // Arrange
        var listA = ChangesTrackerMockups.GetAList(10); // ids 1-10
        var listB = ChangesTrackerMockups.GetBList(11)  // ids 1-11
            .Where(b => b.Iddd != 7); // Exclude element means it will be delete
        var changesTracker = new ChangesTracker<A, B>(listA, listB, AreEqual);

        // Assert
        Assert.AreEqual(1, changesTracker.AddedItems.Count()); // b.id = 11
        Assert.AreEqual(1, changesTracker.RemovedItems.Count()); // b.id = 7
        Assert.AreEqual(9, changesTracker.UpdatedItems.Count()); // all a.id == b.iddd
    }

    private bool AreEqual(A a, B b)
    {
        if (a == null && b == null)
            return true;
        if (a == null || b == null)
            return false;
        return a.Id == b.Iddd;
    }
Run Code Online (Sandbox Code Playgroud)


小智 6

var list3 = list1.Where(x => !list2.Any(z => z.Id == x.Id)).ToList();
Run Code Online (Sandbox Code Playgroud)

注意:list3将包含不在两个列表中的项目或对象。注意:它ToList()不是toList()