SteGriff

Blog

Next & Previous

LINQ join where second collection overrides and supplements the first

Say you have a collection of default preferences, and a collection of user preferences which overrides some of the default values but also includes some values which don’t have a corresponding key in the set of defaults.

You could write something really ugly for this with lots of looping and conditions or you could use this relatively elegant technique to join the collections twice and keep the union of the results.

Just to visualise the basic data before we jump into code sample:

ID Key Value Source
1 SortOrder NewestFirst Default
2 PageSize 20 Default
3 SortOrder Alphabetical User
4 EmailNotifications WeeklyDigest User

What we expect out of this is to keep the User value where it exists, and keep the default value where there is no user override. The expected list is this:

Key Value Source
SortOrder Alphabetical User (override)
PageSize 20 Default (no override exists)
EmailNotifications WeeklyDigest User (supplements defaults)

OK, what’s the code like?

void Main()
{
    // Set up some data
    var defaultPreferences = new List<Preference>()
    {
        new Preference(){Id = 1, Key = "SortOrder", Value = "NewestFirst"},
        new Preference(){Id = 2, Key = "PageSize", Value = "20"}
    };
    
    var userPreferences = new List<Preference>()
    {
        new Preference(){Id = 3, Key = "SortOrder", Value = "Alphabetical"},
        new Preference(){Id = 4, Key = "EmailNotifications", Value = "WeeklyDigest"}
    };
    
    // Left join to keep user overrides or default props if no user value
    var prefs1 = from dp in defaultPreferences
                 from up in userPreferences
                   .Where(p => p.Key == dp.Key)
                   .DefaultIfEmpty()
                 select up ?? dp;
                 
    // Display the value (for LinqPad)
    prefs1.Dump();
    
    // Left join the other way to keep user props whether or not a default exists
    var prefs2 = from up in userPreferences
                 from dp in defaultPreferences
                   .Where(p => p.Key == up.Key)
                   .DefaultIfEmpty()
                 select up;
                 
    prefs2.Dump();
    
    // Union the two sets. This removes duplicates.
    var prefsU = prefs1.Union(prefs2);
    prefsU.Dump();
}

class Preference
{
    public int Id {get;set;}
    public string Key {get;set;}
    public string Value {get;set;}
}

Results

You can run the code above as a ‘C# program’ in LinqPad to get the results pictured. Note that using Union on the two collections calls the default equality comparer and removes duplicates of any values deemed to be equal to each other.

I hope this helps you to solve cool problems today :)

Cheers