Based on all the answers on this post, here's the most generic solution I could came up with.
I created 2 versions of the IDictionary.Merge() extension :
- Merge<T, U>(sourceLeft, sourceRight)
- Merge<T, U>(sourceLeft, sourceRight, Func<U, U, U> mergeExpression)
Where the second is a modified version of the first that lets you specify a lambda expression to handle duplicates like this :
Dictionary<string, object> customAttributes =
HtmlHelper
.AnonymousObjectToHtmlAttributes(htmlAttributes)
.ToDictionary(
ca => ca.Key,
ca => ca.Value
);
Dictionary<string, object> fixedAttributes =
new RouteValueDictionary(
new {
@class = "form-control"
}).ToDictionary(
fa => fa.Key,
fa => fa.Value
);
//appending the html class attributes
IDictionary<string, object> editorAttributes = fixedAttributes.Merge(customAttributes, (leftValue, rightValue) => leftValue + " " + rightValue);
(You can focus on the ToDictionary()
and Merge()
parts)
And here's the extension class (with 2 versions of the extension that take a collection of IDictionary
on the right side):
public static class IDictionaryExtension
{
public static IDictionary<T, U> Merge<T, U>(this IDictionary<T, U> sourceLeft, IDictionary<T, U> sourceRight)
{
IDictionary<T, U> result = new Dictionary<T,U>();
sourceLeft
.Concat(sourceRight)
.ToList()
.ForEach(kvp =>
result[kvp.Key] = kvp.Value
);
return result;
}
public static IDictionary<T, U> Merge<T, U>(this IDictionary<T, U> sourceLeft, IDictionary<T, U> sourceRight, Func<U, U, U> mergeExpression)
{
IDictionary<T, U> result = new Dictionary<T,U>();
//Merge expression example
//(leftValue, rightValue) => leftValue + " " + rightValue;
sourceLeft
.Concat(sourceRight)
.ToList()
.ForEach(kvp =>
result[kvp.Key] =
(!result.ContainsKey(kvp.Key))
? kvp.Value
: mergeExpression(result[kvp.Key], kvp.Value)
);
return result;
}
public static IDictionary<T, U> Merge<T, U>(this IDictionary<T, U> sourceLeft, IEnumerable<IDictionary<T, U>> sourcesRight)
{
IDictionary<T, U> result = new Dictionary<T, U>();
new[] { sourceLeft }
.Concat(sourcesRight)
.ToList()
.ForEach(dic =>
result = result.Merge(dic)
);
return result;
}
public static IDictionary<T, U> Merge<T, U>(this IDictionary<T, U> sourceLeft, IEnumerable<IDictionary<T, U>> sourcesRight, Func<U, U, U> mergeExpression)
{
IDictionary<T, U> result = new Dictionary<T, U>();
new[] { sourceLeft }
.Concat(sourcesRight)
.ToList()
.ForEach(dic =>
result = result.Merge(dic, mergeExpression)
);
return result;
}
}
The mergeExpression
let's you easily handle the way you want to merge the items, like addition, division, multiplication or any kind of specific process you desire.
Note that I've not yet tested the collection versions of the extension... they may still require some tuning.
Also, the extension does NOT modify the original dictionaries, you'll have to assign it back if you want to.