简体   繁体   English

两个列表之间的区别

[英]Difference between two lists

I Have two generic list filled with CustomsObjects.我有两个通用列表,其中填充了 CustomsObjects。

I need to retrieve the difference between those two lists(Items who are in the first without the items in the second one) in a third one.我需要在第三个列表中检索这两个列表之间的差异(第一个列表中没有第二个列表中的项目)。

I was thinking using .Except() was a good idea but I don't see how to use this.. Help!我在想使用.Except()是个好主意,但我不知道如何使用它。帮助!

Using Except is exactly the right way to go.使用Except是正确的方法。 If your type overrides Equals and GetHashCode , or you're only interested in reference type equality (ie two references are only "equal" if they refer to the exact same object), you can just use:如果您的类型覆盖EqualsGetHashCode ,或者您只对引用类型相等性感兴趣(即两个引用仅在引用完全相同的对象时才“相等”),则可以使用:

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

If you need to express a custom idea of equality, eg by ID, you'll need to implement IEqualityComparer<T> .如果您需要表达自定义的平等概念,例如通过 ID,您将需要实现IEqualityComparer<T> For example:例如:

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

Then use:然后使用:

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

Note that this will remove any duplicate elements.请注意,这将删除任何重复的元素。 If you need duplicates to be preserved, it would probably be easiest to create a set from list2 and use something like:如果您需要保留重复项,从list2创建一个集合并使用类似的东西可能是最简单的:

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

你可以这样做:

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

I think important to emphasize - using Except method will return you items who are in the first without the items in the second one only.我认为重要的是要强调 - 使用except 方法将返回第一个中的项目,而仅返回第二个中的项目。 It does not return those elements in second that do not appear in first.它不会返回那些在 second 中没有出现在 first 中的元素。

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

But if you want get real difference between two lists:但是,如果您想获得两个列表之间的真正区别:

Items who are in the first without the items in the second one and items who are in the second without the items in the first one.第一个项目没有第二个项目,第二个项目没有第一个项目。

You need using Except twice:您需要两次使用“除外”:

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

Or you can use SymmetricExceptWith method of HashSet.或者您可以使用 HashSet 的SymmetricExceptWith方法。 But it changes the set on which called:但它改变了调用的集合:

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
var third = first.Except(second);

(you can also call ToList() after Except() , if you don't like referencing lazy collections.) (如果您不喜欢引用惰性集合,您也可以在Except() ToList()之后调用ToList() 。)

The Except() method compares the values using the default comparer, if the values being compared are of base data types, such as int , string , decimal etc.如果被比较的值是基本数据类型,例如intstringdecimal等, Except()方法使用默认比较器比较值。

Otherwise the comparison will be made by object address, which is probably not what you want... In that case, make your custom objects implement IComparable (or implement a custom IEqualityComparer and pass it to the Except() method).否则将通过对象地址进行比较,这可能不是您想要的......在这种情况下,让您的自定义对象实现IComparable (或实现自定义IEqualityComparer并将其传递给Except()方法)。

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

Note: list3 will contain the items or objects that are not in both lists.注意: list3将包含不在两个列表中的项目或对象。 Note: Its ToList() not toList()注意:它的ToList()不是toList()

To get unique differences from both lists you could merge them (Union) Except those values which are same inside both lists (Intersect) eg:要从两个列表中获得独特的差异,您可以合并它们(联合)除了两个列表中相同的值(相交),例如:

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

For complex types implement IComparable if you comparing your instances all the time with the same compare pattern.对于复杂类型,如果您始终使用相同的比较模式比较您的实例,请实施 IComparable。 If you need different compare pattern in some cases than you can always create an class which implements IEqualityComparer.如果您在某些情况下需要不同的比较模式,您总是可以创建一个实现 IEqualityComparer 的 class。

Following helper can be usefull if for such task:如果对于此类任务,以下助手可能会很有用:

There are 2 collections local collection called oldValues and remote called newValues From time to time you get notification bout some elements on remote collection have changed and you want to know which elements were added, removed and updated.有 2 个本地集合称为oldValues和远程称为newValues有时您会收到有关远程集合上某些元素已更改的通知,并且您想知道添加、删除和更新了哪些元素。 Remote collection always returns ALL elements that it has.远程集合总是返回它拥有的所有元素。

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

Usage用法

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

Since the Except extension method operates on two IEumerables, it seems to me that it will be a O(n^2) operation.由于Except 扩展方法在两个IEumerables 上运行,在我看来这将是一个O(n^2) 操作。 If performance is an issue (if say your lists are large), I'd suggest creating a HashSet from list1 and use HashSet's ExceptWith method.如果性能是一个问题(如果说你的列表很大),我建议从 list1 创建一个 HashSet 并使用 HashSet 的 exceptWith 方法。

here is my solution:这是我的解决方案:

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

(LINQ) (C#) This example shows how to use LINQ to compare two lists of strings and output those lines that are in names1.txt but not in names2.txt with .Except() . (LINQ)(C#)此示例显示如何使用LINQ比较两个字符串列表,并输出names1.txt中但不在names2.txt中的.Except()行

    // Create the IEnumerable data sources.  
    string[] names1 = System.IO.File.ReadAllLines(@"../../../names1.txt");  
    string[] names2 = System.IO.File.ReadAllLines(@"../../../names2.txt");  

    // Create the query. Note that method syntax must be used here.  
    IEnumerable<string> differenceQuery = names1.Except(names2);  

    // Execute the query.  
    Console.WriteLine("The following lines are in names1.txt but not names2.txt");  
    foreach (string s in differenceQuery)  
        Console.WriteLine(s);  

    // Keep the console window open in debug mode.  
    Console.WriteLine("Press any key to exit");  
    Console.ReadKey(); 

bit late but here is working solution for me有点晚了,但这是对我有用的解决方案

 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

In above mention code I am getting the properties difference between my base class and derived class list在上面提到的代码中,我得到了基类和派生类列表之间的属性差异

var resultList = checklist.Where(p => myList.All(l => p.value != l.value)).ToList();
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);
    }
}
        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);
            }
        }

如果您的两个列表都实现了 IEnumerable 接口,您可以使用 LINQ 实现这一点。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM