0

I have two set of lists and I want to create one list with unique values and other with existing if stockNumber and supplierName matches. So that I can do Update/Insert operation accordingly.

My criteria are:

  • if number and name matches in list1 and list2 then it will be part of existingRecords list
  • else move them to newRecords list

Current List:

var existingSuppliers = _dbContext.Supplier
    .Where(_ => _.Id.Equals(existingException.Id)).ToList();

var currentSuppliers = exception.Suppliers
    .ToLookup(x => x.SupplierName).Select(g => g.First()).ToList();

Output: enter image description here

enter image description here

I tried with Except and Intersect but looks like I am not doing this right.

If I do:

var intersect = existingSuppliers.Intersect(currentSuppliers).ToList(); // returns 0
var except = existingSuppliers.Except(currentSuppliers).ToList(); // returns existingSuppliers (1 records)

And if I do other way:

var intersect2 = currentSuppliers.Intersect(existingSuppliers).ToList(); // 0 records
var except2 = currentSuppliers.Except(existingSuppliers).ToList(); // currentSupplier (2 records)
GThree
  • 2,708
  • 7
  • 34
  • 67
  • 2
    You need to implement Equals on your objects so Linq will understand what you consider a unique item otherwise reference equality will be used – Anthony G. Apr 12 '23 at 20:33
  • 1
    Don't forget to override both the `Equals()` and `GetHashCode()` methods. Check this https://stackoverflow.com/q/2265503/19112855 – Codingwiz Apr 12 '23 at 20:44
  • why dont use a for? – Leandro Bardelli Apr 12 '23 at 20:48
  • 1
    You cannot `Intersect` nor `Except` two different types of list. They need to be both of the same type or use a DTO to map one of them to the type of the other. – Codingwiz Apr 12 '23 at 20:53
  • @Codingwiz These are the same types of list with different values – GThree Apr 12 '23 at 20:55
  • @AnthonyG. Do you mean `.SequenceEqual`? If yes, I am getting false for `list1.SequenceEqual(list2)`. If no, is it possible to give an example? – GThree Apr 12 '23 at 20:58
  • C# is a language of types. Don't post JSON instead of C# code. – NetMage Apr 12 '23 at 20:59
  • To remove existing list `list1` from unique list `list2`, you need `list2.Except(list1).ToList()` and this will give you the remaining `list2` that are not in `list1`. Please post full code to see where you got it wrong. – Codingwiz Apr 12 '23 at 20:59
  • @Codingwiz Perhaps a C# reference would be more pertinent. – NetMage Apr 12 '23 at 21:00
  • @NetMage Got it. Since this is a list, I thought these example might suffice. But I'll keep this in mind. – GThree Apr 12 '23 at 21:00
  • See the [`Enumerable.Except` documentation](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.except?view=net-8.0#system-linq-enumerable-except-1(system-collections-generic-ienumerable((-0))-system-collections-generic-ienumerable((-0)))). The difference is computed using the "default equality comparer" for the type which is [`EqualityComparer.Default`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.equalitycomparer-1.default?view=net-7.0#remarks). – NetMage Apr 12 '23 at 21:04
  • 1
    Your type needs to implement `IEquatable` or it needs to override `Object.Equals` and `Object.GetHashCode` or you need to pass in a custom `EqualityComparer` to `Except` and `Intersect`. – NetMage Apr 12 '23 at 21:04

2 Answers2

1

Here's one way to solve your problem:

public static void Compare()
{
    List<TestRecordDto> list1 = new List<TestRecordDto>
    {
        new TestRecordDto{ name = "Apple", number = "123"},
        new TestRecordDto{ name = "Banana", number = "123"},
    };
    List<TestRecordDto> list2 = new List<TestRecordDto>
    {
        new TestRecordDto{ name = "Apple", number = "123"},
        new TestRecordDto{ name = "Cherry", number = "123"},
        new TestRecordDto{ name = "Apple", number = "234"},
    };

    var existingRecords = list1.Where(x => list2.Any(y => y.number == x.number && y.name == x.name)).ToList();

    var newRecords = list1.Where(x => !existingRecords.Any(y => y.name == x.name &&  y.number == x.number)).ToList();
    var newRecordsFromList2 = list2.Where(x => !existingRecords.Any(y => y.name == x.name && y.number == x.number)).ToList();

    newRecords.AddRange(newRecordsFromList2);
}

private class TestRecordDto
{
    public string number { get; set; }
    public string name { get; set; }
}

Note that I've declared a TestRecordDto class that holds your list objects.

The logic is as follows:

  1. I start by finding all records in list1 that match with list2 and putting them in existingRecords. (Pretty straightforward.)
  2. Next line goes through list1 to find all records that don't existing in existingRecords list and puts then in newRecords.
  3. Finally, same thing with list2 and you have the final list.

The outputs are existingRecords and newRecords (with newRecordsFromList2 being a throwaway intermediate output.) You could directly AddRange second output without putting it in the throwaway list, but having that variable is useful to verify the results.

R J
  • 495
  • 5
  • 12
0

Another way to answer your problem:

private record TestRecordDto
{
    public string number { get; set; }
    public string name { get; set; }
}

public static void Compare()
{
    var list1 = new List<TestRecordDto>
    {
        new TestRecordDto{ name = "Apple", number = "123"},
        new TestRecordDto{ name = "Banana", number = "123"},
    };
    var list2 = new List<TestRecordDto>
    {
        new TestRecordDto{ name = "Apple", number = "123"},
        new TestRecordDto{ name = "Cherry", number = "123"},
        new TestRecordDto{ name = "Apple", number = "234"},
    };

    var existingRecords = list1.Intersect(list2).ToList();

    var newRecords = list1.Except(existingRecords).ToList();
    newRecords.AddRange(list2.Except(existingRecords));
}

Note that in this case, I've defined TestRecordDto as a record instead of class.

Now Intersect and Except work as you'd expect (since equality comparer compares on actual values of the object rather than the arbitrary reference that gets allocated when initializing the object.)

There are other implications to using record that may make it undesirable for your needs, but it's certainly quite useful when you're writing some self-contained logic.

R J
  • 495
  • 5
  • 12