ChatGPT解决这个技术问题 Extra ChatGPT

两个列表之间的区别

c#

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

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

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


R
Robert Harvey

使用 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();

顺便说一句,我要补充一点,Except 是一个 Set 操作,那么结果列表将具有不同的值,例如 {'A','A','B','C'}.Except({'B','C'}) 返回 {'A'}
我有这个: {'A','A','B','C'}.Except({'B','C'}) 返回 {'A'} 有效...但是 {'B' ,'C'}.Except({'A','B','C'}) 不返回 {'A'}
两个集合的集合差异定义为第一个集合的成员没有出现在第二个集合中(引用 MS)。因此, {B,C}.Except({A,B,C}) 不应该返回任何内容,因为 B 和 C 都在第二组中。不是错误,更多的是与函数的数学定义有关。
所以@JonSkeet如果我想根据假设2个属性比较两个列表,我可以用这种方式编写比较器并获得不相等的项目对吗?
@NetMage:OP 表示他们想要“第一个中的项目没有第二个中的项目” - 这对我来说听起来像是一个设定的差异。如果第一个列表包含 { 5, 5, 5, 5, 1 } 而第二个列表包含 { 5 } 则只有 1 在第一个列表中,而不是第二个。
B
Ben

你可以这样做:

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

它为我节省了一个 foreach 循环。
这些“l.someproperty”中的一个不应该是“p.someproperty”吗?
这可以通过 customlist.Where(p => otherlist.Any(l => p.someproperty != l.someproperty)); 来简化。
@Dhanuka777 这是不正确的。如果您想这样做,Any 应该是 All。这是因为第二个列表可能包含第一个列表中的项目,但如果它不是第一个被选中的,那么它会立即为 p.someproperty != l.someproperty 得到一个真值。这会导致返回两个列表中都存在的项目。为这 6 个点赞的人感到羞耻。
S
Stas Boyarincev

我认为重要的是要强调 - 使用 Except method 将返回第一个中的项目,而没有第二个中的项目。它不会在第二个返回那些没有出现在第一个的元素。

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

但是,如果您想在两个列表之间获得真正的区别:

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

您需要使用 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

或者您可以使用 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 明确表示他们不想要完全不同:“我需要检索这两个列表之间的差异(第一个列表中的项目没有第二个列表中的项目)”。
r
rsenna
var third = first.Except(second);

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

如果要比较的值是基本数据类型(例如 intstringdecimal 等),则 Except() 方法使用默认比较器比较值。

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


n
nasir kiyani
var list3 = list1.Where(x => !list2.Any(z => z.Id == x.Id)).ToList();

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


u
user3526723

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

有 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)));
    }
}

用法

        [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;
    }

c
croban

要从两个列表中获得独特的差异,您可以合并它们(联合)除了两个列表中相同的值(相交),例如:

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

var diffs = list1.Union(list2).Except(list1.Intersect(list2));

对于复杂类型,如果您始终使用相同的比较模式比较您的实例,请实现 IComparable。如果您在某些情况下需要不同的比较模式,那么您总是可以创建一个实现 IEqualityComparer 的类。


f
foson

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


Enumerable.Except 在内部使用 HashSet 或类似的东西。它绝对不使用朴素的 O(n^2) 算法。
@Jim Mischel:你是对的,Enumerable.Except 使用内部 Set 数据结构并将两个 IEnumerables 中的项目添加到 Set 中。希望文档能说明这一点。
R
Roni Al Ka

这是我的解决方案:

    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);
    }
}

A
Atul Chaudhary

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

 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

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


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

注意:Result List 是存在于 checklist 但不存在于 myList 中的项目
S
Stephen Rauch
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 年后,并没有增加任何新的价值
d
dcarneiro

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

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

如果您的每个列表都包含一百万个项目,那么您将等待很长时间。请改用 IEnumerable.Except
是的。我只有大约 450 件物品,我还在等待。小心使用这个。
M
Milad
        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);
            }
        }