LINQ Joins – Multiple Match Conditions

Hi all,

Just a very quick post on an interesting piece of LINQ knowledge demonstrating how to find common matches between two C# lists, whereby you want to include multiple matching conditions.

To start, here is a typical LINQ statement (and setup) dealing with a single join condition, using the extension method approach (which I prefer), joining two lists; one defining humans and one defining aliens (just because!):

// Setup
public interface ILivingBeing
{
    string Name { get; set; }
    int Age { get; set; }
    void WriteInfo();
}

...

public abstract class LivingBeing : ILivingBeing
{
    public int Age { get; set; }
    public string Name { get; set; }
    public abstract void WriteInfo();
}

...

public class Human : LivingBeing
{
    public override void WriteInfo()
    {
        Console.WriteLine("I am a human who is {0} years old and my name is {1}", Age, Name);
    }
}

...

public class Alien : LivingBeing
{
    public override void WriteInfo()
    {
        Console.WriteLine("I am an alien who is {0} years old and my name is {1}", Age, Name);
    }
}

...

// And finally, the List setup and LINQ itself:
List<Human> humans = new List<Human>
{
    new Human { Name = "Steve", Age = 31 },
    new Human { Name = "Dave", Age = 34 },
    new Human { Name = "Alexa", Age = 56 }
};

List<Alien> aliens = new List<Alien>
{
    new Alien { Name = "Steve", Age = 31 },
    new Alien { Name = "Dave", Age = 76 },
    new Alien { Name = "Poppy", Age = 56 }
};

var matches = humans.Join(aliens, human => human.Name, alien => alien.Name, (human, alien) => human);

Here is an illustration showing the first set of results being used:

LINQ Single Join Condition.

LINQ Single Join Condition.

In the case above the results returned by ‘matches’ include both ‘Steve’ and ‘Dave’ (humans), as a match on the ‘Name’ property can be established across both lists. Everything is fine up until the point we want matches based on, say, the Name and Age properties (on the assumption we want to stick with the extension method approach). This is surprisingly easy to achieve, using anonymous types in our LINQ statement join condition, as follows:

// Return just a list containing the human 'Steve' (as he is the only one who directly matches an alien based on 'Name' and 'Age'
var matches = humans.Join(aliens, human => new { human.Name, human.Age }, alien => new { alien.Name, alien.Age }, (human, alien) => human);

The inner and outer key selectors simply use anonymous types, constructed using the new keyword, to bring the related properties into scope that we wish to match on. Then, hey presto, you can easily handle multiple matching conditions. In this instance, only the human ‘Steve’ is returned, based on a direct match with an alien of the same name/age:

LINQ Multiple Join Condition.

LINQ Multiple Join Condition.

I hope this comes in handy at some point. I’ve been kept fairly busy on the work front recently, hence my blog and twitter feed haven’t exactly been a hive of activity…I’ll work to change that as best I can in the coming weeks and months and carve out more time to publish content.

Cheers, until the next time!