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;}
}
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