Question

I found this post Match elements between 2 collections with Linq in c# which explained how you can use Intersect to find matching elements between two lists.

Can you use this to match elements in two lists that are not of exactly the same, but have "sub values" that you want to match?

My example is this: I have two collections, each containing lists of XElements. The one with elements called <link> and the other with elements called <file>, each have attributes called "path", and it is this attribute I want to match. If the path attribute is equal, I want a match.

In the result set I would like a list of all the elements whose paths match the paths of the elements.

How can this be done?

Was it helpful?

Solution

I would suggest to use LambdaComparer which can be passed into the Intersect() method as Equality Comparer, it allows specifying comparison logic in place by providing boolean condition instead introducing a new comparer class each time, so your code would be clear enough:

firstCollection.Intersect(
              secondCollection, 
              new LambdaComparer<YourClass>(
                  (item1, item2) => item1.PropertyName == item2.PropertyName));


 // Below are lists and User class which demonstrates LambdaComparer and Intersect()
 public class User
 {
      public string Name { get; set; }
 }

 IList<User> list1 = new List<User> 
       { 
          new User {Name = "A"}, 
          new User { Name = "B"}
       };
 List<User> list2 = new List<User> 
      { 
          new User {Name = "C"}, 
          new User { Name = "B"}
      };

 var resultSet = list1.Intersect<User>(
         list2, 
         new LambdaComparer<User>((item1, item2) => item1.Name == item2.Name));

Basically if you need to compare cusotm attributes, you still can encapsulate this logic into

Func<User, User, bool> userNameComparer = (user1, user2) =>
{
 // check attributes using user1.GetType().GetCustomAttributes()
};

And then use this comparer funciton as:

   var resultSet = list1.Intersect<User>(
                     list2, 
                     new LambdaComparer<User>((item1, item2) => userNameComparer));

EDIT: Note ragarding particular impelemntaion referenced in this answer There is could be a problem that by default for hash funciton is hardcoded 0

6  public LambdaComparer(Func<T, T, bool> lambdaComparer) :
7                this(lambdaComparer, o => 0)
8            {
9            }

This can lead to performance issues in some cases so I would recommend to refactor it as:

public LambdaComparer(Func<T, T, bool> lambdaComparer) :
                this(lambdaComparer, 
                      EqualityComparer<T>.Default.GetHashCode(o))
            {
            }

So it wil use built in GetHashCode() implementation

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top