When you merge two dictionaries, you can either merge them in-place, or create a new dictionary and copy the values over to it.
The following extension method does an in-place merge of two dictionaries. It loops through items in the right dictionary, adding them to the left dictionary. When duplicate keys exist, it’s keeping the value from the left (instead of throwing an ArgumentException, or keeping the value from the right).
public static class DictUtils
{
public static Dictionary<Key, Value> MergeInPlace<Key, Value>(this Dictionary<Key, Value> left, Dictionary<Key, Value> right)
{
if (left == null)
{
throw new ArgumentNullException("Can't merge into a null dictionary");
}
else if (right == null)
{
return left;
}
foreach (var kvp in right)
{
if (!left.ContainsKey(kvp.Key))
{
left.Add(kvp.Key, kvp.Value);
}
}
return left;
}
}
Code language: C# (cs)
Note: Even though this is doing an in-place merge, it’s returning a dictionary. This is so you can use this in a fluent manner, for example: left.MergeInPlace(dict1).MergeInPlace(dict2).Count().
Unit tests for MergeInPlace()
Here are unit tests that exercise all the behavior in the MergeInPlace() method.
[TestClass()]
public class DictUtilsTests
{
[TestMethod()]
public void MergeInPlaceTest_WhenLeftNull_ThrowsNullArgument()
{
//arrange
Dictionary<string, string> left = null;
var right = new Dictionary<string, string>();
//act & assert
Assert.ThrowsException<ArgumentNullException>(() => left.MergeInPlace(right));
}
[TestMethod()]
public void MergeInPlaceTest_WhenRightNull_ReturnsLeft()
{
//arrange
var left = new Dictionary<string, string>();
Dictionary<string, string> right = null;
//act
var merged = left.MergeInPlace(right);
//assert
Assert.AreSame(left, merged);
}
[TestMethod()]
public void MergeInPlaceTest_WhenItemInRight_MergesIntoLeft()
{
//arrange
var testKey = "TEST";
var testValue = "1";
var left = new Dictionary<string, string>();
var right = new Dictionary<string, string>()
{
[testKey] = testValue
};
//act
left.MergeInPlace(right);
//assert
Assert.AreEqual(testValue, left[testKey]);
}
[TestMethod()]
public void MergeInPlaceTest_WhenItemInLeft_StillInLeft()
{
//arrange
var testKey = "TEST";
var testValue = "1";
var left = new Dictionary<string, string>()
{
[testKey] = testValue
};
var right = new Dictionary<string, string>();
//act
left.MergeInPlace(right);
//assert
Assert.AreEqual(testValue, left[testKey]);
}
[TestMethod()]
public void MergeInPlaceTest_ItemsInLeftAndRight_PutsAllInLeft()
{
//arrange
var leftTestKey = "TEST";
var leftTestValue = "1";
var left = new Dictionary<string, string>()
{
[leftTestKey] = leftTestValue
};
var rightTestKey = "TEST2";
var rightTestValue = "2";
var right = new Dictionary<string, string>()
{
[rightTestKey] = rightTestValue
};
var expected = new Dictionary<string, string>()
{
[leftTestKey] = leftTestValue,
[rightTestKey] = rightTestValue
};
//act
left.MergeInPlace(right);
//assert
CollectionAssert.AreEqual(expected, left);
}
[TestMethod()]
public void MergeInPlaceTest_WhenKeyInBothLeftAndRight_TakesTheValueFromLeft()
{
//arrange
var testKey = "TEST";
var leftTestValue = "1";
var left = new Dictionary<string, string>()
{
[testKey] = leftTestValue
};
var rightTestValue = "2";
var right = new Dictionary<string, string>()
{
[testKey] = rightTestValue
};
//act
left.MergeInPlace(right);
//assert
Assert.AreEqual(leftTestValue, left[testKey]);
}
}
Code language: C# (cs)
Why not use Linq Union() or Concat()?
You might be wondering why you’d need to write your own merge method, instead of using the Linq Union() / Concat() methods.
The reason is because they can’t handle duplicate keys.
For example, let’s say I have two dictionaries that have the same key “TEST”.
var testKey = "TEST";
var testValueLeft = "1";
var testValueRight = "2";
var left = new Dictionary<string, string>()
{
[testKey] = testValueLeft
};
var right = new Dictionary<string, string>()
{
[testKey] = testValueRight
};
var merged = left.Union(right).ToDictionary(t => t.Key, t => t.Value);
Code language: C# (cs)
This throws System.ArgumentException: An item with the same key has already been added.
In the MergeInPlace() method I showed in this article, when the key exists in both dictionaries, it takes the value from the left. Because you’re writing your own merging logic, you can make it handle duplicates however you want.
With the Linq methods, they simply don’t handle duplicate keys at all.