简体   繁体   中英

Difference between two lists

I Have two generic list filled with 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!

Using Except is exactly the right way to go. 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:

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> . 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:

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. It does not return those elements in second that do not appear in 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. 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.)

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.

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

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. Note: Its ToList() not 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. If you need different compare pattern in some cases than you can always create an class which implements IEqualityComparer.

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. 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. 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.

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() .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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