Объедините два словаря с LINQ
Мой вопрос был помечен как возможный дубликат этого вопроса: Как совместить два словаря без цикла?
Я считаю, что мой вопрос отличается от того, что я задаю вопрос о том, как объединить два словаря определенным образом: я хочу, чтобы все слова из словаря 1 плюс все элементы из словаря 2, которых нет (т.е. ключ не существует) в Dictionary1.
У меня есть два словаря типа:
var d1 = new Dictionary<string,object>();
var d2 = new Dictionary<string,object>();
d1["a"] = 1;
d1["b"] = 2;
d1["c"] = 3;
d2["a"] = 11;
d2["e"] = 12;
d2["c"] = 13;
Я хотел бы объединить их в новый словарь (технически, он не обязательно должен быть словарем, он может быть просто последовательностью KeyValuePairs
), так что вывод содержит все KeyValuePairs
из d1 и только KeyValuePairs из d2
, чей ключ не отображается в d1
.
Концептуально:
var d3 = d1.Concat(d2.Except(d1))
Но это дает мне все элементы из d1 и d2.
Похоже, это должно быть очевидно, но я должен что-то пропускать.
Ответы
Ответ 1
При использовании Except
по умолчанию используется сопоставитель равенства по умолчанию, который для типа KeyValuePair
сравнивает как ключи, так и значения. Вместо этого вы можете:
var d3 = d1.Concat(d2.Where(kvp => !d1.ContainsKey(kvp.Key)));
Ответ 2
var d3 = d1.Concat(d2.Where(kvp => ! d1.ContainsKey(kvp.Key)))
.ToDictionary(x => x.Key, x => x.Value);
Это работает для меня.
Ответ 3
Ну, я не знаю, является ли это новой функцией в LinQ, но именно то, что делает .Union()
:
var d3 = d1.Union(d2);
Конечно, со словарями вам нужно будет предоставить собственный сопоставитель сравнений для соответствия только ключам:
class KeyValuePairComparer<TKey, TValue> : IEqualityComparer<KeyValuePair<TKey, TValue>>
{
public bool Equals(KeyValuePair<TKey, TValue> x, KeyValuePair<TKey, TValue> y)
{
return x.Key.Equals(y.Key);
}
public int GetHashCode(KeyValuePair<TKey, TValue> x)
{
return x.GetHashCode();
}
}
а затем:
var d3 = d1.Union(d2, new KeyValuePairComparer<string, object>());
В вашем примере вывод будет (протестирован на С# interactive):
> d1.Union(d2, new KeyValuePairComparer<string, object>())
UnionIterator { { "a", 1 }, { "b", 2 }, { "c", 3 }, { "e", 12 } }
Обратите внимание на разницу:
> d2.Union(d1, new KeyValuePairComparer<string, object>())
UnionIterator { { "a", 11 }, { "e", 12 }, { "c", 13 }, { "b", 2 } }
Ответ 4
Вы также можете использовать свой собственный IEqualityComparer
. Пример ниже:
public class MyComparer : IEqualityComparer<KeyValuePair<string,string>> {
public bool Equals(KeyValuePair<string, string> x, KeyValuePair<string, string> y) {
return x.Key.Equals(y.Key);
}
public int GetHashCode(KeyValuePair<string, string> obj) {
return obj.Key.GetHashCode();
}
}
...
Dictionary<string, string> d1 = new Dictionary<string, string>();
d1.Add("A", "B");
d1.Add("C", "D");
Dictionary<string, string> d2 = new Dictionary<string, string>();
d2.Add("E", "F");
d2.Add("A", "D");
d2.Add("G", "H");
MyComparer comparer = new MyComparer();
var d3 = d1.Concat(d2.Except(d1, comparer));
foreach (var a in d3) {
Console.WriteLine("{0}: {1}", a.Key, a.Value);
}
Ответ 5
Jon Skeet (как обычно) имеет метод расширения, позволяющий вам сделать это: Могу ли я указать мой явный компаратор типа inline?
Ответ 6
Другое решение, использующее ваш собственный IEqualityComparer
, как в ответе @bitxwise и @DaveShaw, но не используя Except()
, что делает его немного проще:
var d3 = d1.Concat(d2).Distinct(new MyComparer());