Exclude items of one list in another with different object data types, LINQ?

38,710

Solution 1

Instead of creating new objects, how about checking the properties as part of the linq query

List<Human> humans = _unitOfWork.GetHumans();
List<AnotherHuman> anotherHumans = _unitofWork.GetAnotherHumans();

// Get all anotherHumans where the record does not exist in humans
var result = anotherHumans
               .Where(ah => !humans.Any(h => h.LastName == ah.LastName
                               && h.Name == ah.Name
                               && h.Birthday == ah.Birthday
                               && (!h.PersonalId.HasValue || h.PersonalId == ah.PersonalId)))
               .ToList();

Solution 2

var duplicates = from h in humans
                 from a in anotherHumans
                 where (h.PersonalID == a.PersonalID) ||
                       (h.LastName == a.LastName && 
                        h.FirstName == a.FirstName && 
                        h.Birthday == a.Birthday)
                 select a;

anotherHumans = anotherHumans.Except(duplicates);

Solution 3

var nonIdItems = anotherHumans
   .Where(ah => !ah.PersonalID.HasValue)
   .Join(humans, 
         ah => new{ah.LastName, 
           ah.FirstName, 
           ah.Birthday}, 
         h => new{h.LastName, 
           h.FirstName, 
           h.Birthday}, 
         (ah,h) => ah);
var idItems = anotherHumans
   .Where(ah => ah.PersonalID.HasValue)
   .Join(humans, 
         ah => ah.PersonalID
         h => h.PersonalID, 
         (ah,h) => ah);
var allAnotherHumansWithMatchingHumans = nonIdItems.Concat(idItems);
var allAnotherHumansWithoutMatchingHumans = 
      anotherHumans.Except(allAnotherHumansWithMatchingHumans);
Share:
38,710
Gyuzal R
Author by

Gyuzal R

Updated on October 24, 2020

Comments

  • Gyuzal R
    Gyuzal R over 3 years

    I have two lists filled with their own data. lets say there are two models Human and AnotherHuman. Each model contains different fields, however they have some common fields like LastName, FirstName, Birthday, PersonalID.

    List<Human> humans = _unitOfWork.GetHumans();
    List<AnotherHuman> anotherHumans = _unitofWork.GetAnotherHumans();
    

    I would like to exclude the items from list anotherHumans where LastName, FirstName, Birthday are all equal to the corresponding fields of any item in list humans.

    However if any item in anotherHumans list has PersonalID and item in list humans have the same PersonalID, then it is enough to compare Human with AnotherHuman only by this PersonalID, otherwise by LastName, FirstName and Birthday.

    I tried to create new list of dublicates and exclude it from anotherHumans:

    List<AnotherHuman> duplicates = new List<AnotherHuman>();
    foreach(Human human in humans)
    {
       AnotherHuman newAnotherHuman = new AnotherHuman();
       newAnotherHuman.LastName = human.LastName;
       newAnotherHuman.Name= human.Name;
       newAnotherHuman.Birthday= human.Birthday;
       duplicates.Add(human) 
    }
    anotherHumans = anotherHumans.Except(duplicates).ToList();
    

    But how can I compare PersonalID from both lists if it presents (it is nullable). Is there any way to get rid from creating new instance of AnotherHuman and list of duplicates and use LINQ only?

    Thanks in advance!

  • spender
    spender over 10 years
    For every item in anotherHumans, you'd need to iterate the humans collection, so performance of this would degrade significantly as the sizes of the collections increase.
  • spender
    spender over 10 years
    This will only compare items that have a PersonalID. I don't think that this is what was asked.
  • spender
    spender over 10 years
    Still not so good, because in the case that a PersonalID is present, there's no requirement to check the LastName, Name and Age, right?
  • NinjaNye
    NinjaNye over 10 years
    I've updated the PersonalID fix. With regard to performance, I agree that this will degrade with large datasets however I am not sure this is a concern of the op
  • NinjaNye
    NinjaNye over 10 years
    LastName, Name, and Birthday are always checked. The final line will return true if the PersonalId is null or the PersonalId matches that of another human
  • Gyuzal R
    Gyuzal R over 10 years
    How do you use lambda expression in Contains()?? It must accept an object,isn't it? Contains(human)?
  • NinjaNye
    NinjaNye over 10 years
    Sorry my fault. should be .Any. Updating
  • Gyuzal R
    Gyuzal R over 10 years
    Any() returns a bool value, you cannot convert it ToList()
  • NinjaNye
    NinjaNye over 10 years
    I was missing a bracket at the end (before the .ToList()). you probably are to