Posted by: Cirilo Meggiolaro | 04/23/2009

Tip of the day #191 – Matching differences in collections

Yesterday´s Tip described how to match two IEnumerable collections and retrieve the intersected results between them. Let’s check today how to match two collections but retrieve only the items that are only in one collection. To achieve that we are going to use an extension method called Except that is available for collections that implement the IEnumerable interface.

Overloads

Two overloads are available:

  • IEnumerable<TSource> Except<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second);
  • IEnumerable<TSource> Except<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer);

How to…

For this example we are going to create two generic lists for a custom type called Country as defined below:

public class Country
{
    public string Name { get; set; }
    public int ID { get; set; }
}

Let’s create the lists and populate them:

Country country1 = new Country() { ID = 1, Name = “Brazil” };
Country country2 = new Country() { ID = 2, Name = “Canada” };
Country country3 = new Country() { ID = 3, Name = “USA” };
Country country4 = new Country() { ID = 4, Name = “Japan” };
Country country5 = new Country() { ID = 5, Name = “France” };
Country country6 = new Country() { ID = 6, Name = “Portugal” };

List<Country> countries = new List<Country>();

countries.Add(country1);
countries.Add(country2);
countries.Add(country3);
countries.Add(country4);
countries.Add(country5);
countries.Add(country6);

List<Country> countries2 = new List<Country>();

countries2.Add(country1);
countries2.Add(country3);
countries2.Add(country6);

To check the items that exist only in the first list we are going to use the Except extension method that will match all items that are only in the first collection and retrieve them:

IEnumerable<Country> countriesOnlyInFirst = countries.Except(countries2); // or
List<Country> countriesOnlyInFirst = countries.Except(countries2).ToList();

Let’s check the results:

Console.WriteLine(countriesOnlyInFirst.Count()); // Output: 3

foreach (Country country in countriesOnlyInFirst)
{
    Console.WriteLine(country.Name);
}

Output:
Canada
Japan
France

As the same way than the intersection operations sometimes you will not have the same instance of the country object in the lists resulting in a match that does not retrieve any item. Let’s use the second Except method overload that accepts a generic IEqualityComparer object as parameter. Tip #42 explains how to code a custom comparer class. The code snippet below defines our class that will match the countries based on its ID:

public class CountryComparer : IEqualityComparer<Country>
{
    public bool Equals(Country country1, Country country2)
    {
        return country1.ID == country2.ID;
    }

    public int GetHashCode(Country country)
    {
        return country.ID;
    }
}

Let’s recreate our lists assuming we have different instances for each country in both collections:

List<Country> countries = new List<Country>();

countries.Add(new Country() { ID = 1, Name = “Brazil” });
countries.Add(new Country() { ID = 2, Name = “Canada” });
countries.Add(new Country() { ID = 4, Name = “Japan” });
countries.Add(new Country() { ID = 5, Name = “France” });
countries.Add(new Country() { ID = 6, Name = “Portugal” });

List<Country> countries2 = new List<Country>();

countries2.Add(new Country() { ID = 1, Name = “Brazil” });
countries2.Add(new Country() { ID = 2, Name = “Canada” });
countries2.Add(new Country() { ID = 3, Name = “USA” });

To match the collections properly we need an instance of our comparer class. This time we are going to check the items that are only in the second list though.

IEnumerable<Country> countriesOnlyInSecond = countries2.Except(countries, new CountryComparer()); // or
List<Country> countriesOnlyInSecond = countries2.Except(countries, new CountryComparer()).ToList();

Let’s check the results:

Console.WriteLine(countriesOnlyInSecond.Count()); // Output: 1

foreach (Country country in countriesOnlyInSecond)
{
    Console.WriteLine(country.Name);
}

Output:
USA


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: