C #에서 사전 병합


493

Dictionary<T1,T2>C #에서 둘 이상의 사전 ( ) 을 병합하는 가장 좋은 방법은 무엇입니까 ? (LINQ와 같은 3.0 기능이 좋습니다).

나는 다음과 같은 방법으로 메소드 서명을 생각하고 있습니다.

public static Dictionary<TKey,TValue>
                 Merge<TKey,TValue>(Dictionary<TKey,TValue>[] dictionaries);

또는

public static Dictionary<TKey,TValue>
                 Merge<TKey,TValue>(IEnumerable<Dictionary<TKey,TValue>> dictionaries);

편집 : JaredPar와 Jon Skeet에서 멋진 솔루션을 얻었지만 중복 키를 처리하는 것을 생각하고있었습니다. 충돌의 경우 일관된 한 어떤 값이 dict에 저장되는지는 중요하지 않습니다.


174
관련이 없지만 중복 키 확인없이 두 개의 사전 만 병합하려는 경우에는 다음과 같이 작동합니다.dicA.Concat(dicB).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
Benjol

6
당신은 대답 섹션이 추가 득점도 가능했을 것입니다 @Benjol
M.Kumaran

4
C #에서 Clojure의 합병 :dict1.Concat(dict2).GroupBy(p => p.Key).ToDictionary(g => g.Key, g => g.Last().Value)
Bruce

@Benjol :로 첫 번째 사전의 항목을 선호하여 중복을 제거하십시오 dictA.Concat(dictB.Where(kvp => !dictA.ContainsKey(kvp.Key))).ToDictionary(kvp=> kvp.Key, kvp => kvp.Value).
Suncat2000

답변:


320

이것은 부분적으로 중복이 발생했을 때 어떤 일이 일어나고 싶은가에 달려 있습니다. 예를 들어 다음을 수행 할 수 있습니다.

var result = dictionaries.SelectMany(dict => dict)
                         .ToDictionary(pair => pair.Key, pair => pair.Value);

중복 키가 있으면 예외가 발생합니다.

편집 : ToLookup을 사용하면 키 당 여러 값을 가질 수있는 조회가 표시됩니다. 당신은 할 수 후 사전에 그 변환 :

var result = dictionaries.SelectMany(dict => dict)
                         .ToLookup(pair => pair.Key, pair => pair.Value)
                         .ToDictionary(group => group.Key, group => group.First());

약간 추악하고 비효율적이지만 코드 측면에서 가장 빠른 방법입니다. (필자는 테스트하지 않았습니다.)

물론 더 나은 이름으로 자신의 ToDictionary2 확장 방법을 작성할 수 있습니다 (지금은 생각할 시간이 없습니다). 복잡한 키를 덮어 쓰거나 무시하는 것만으로는 어렵지 않습니다. 중요한 점은 내 생각에 SelectMany를 사용하고 사전이 키 / 값 쌍에 대한 반복을 지원한다는 것을 깨닫는 것입니다.


3
Jon Skeet의 편집 답변에서 group => group.First ()를 group => group.SelectMany (value => value)로 바꿀 수 있습니다.
andreialecu

3
이제 GroupBy가 ToLookup보다 더 적합 할까 궁금합니다. ILookup은 인덱서, 크기 속성을 추가하고 IGrouping 위에 메소드를 포함한다고 생각합니다. 그래서 조금 더 빨라야합니까?
toong

2
@ toong : 솔직히 말해서 괜찮을 것입니다. GroupBy실제로 사용 하는 것이 조금 더 효율적일 수는 있지만 어느 쪽이든 중요 할 것입니다.
Jon Skeet

1
@Joe : ToDictionary메소드 호출에 비교자를 제공하면 충분합니다 . 더 일반적인 경우에 대한 답변을 더 단순하게 유지하고 싶습니다. 맞춤형 비교기가 필요한 사람은 관련 과부하를 찾을 수 있기를 바랍니다.
Jon Skeet

1
@Serge : 정확한 요구 사항을 가진 새로운 질문을하는 것이 좋습니다.
Jon Skeet

264

나는 이렇게 할 것입니다 :

dictionaryFrom.ToList().ForEach(x => dictionaryTo.Add(x.Key, x.Value));

간단하고 쉽습니다. 이 블로그 게시물 에 따르면 기본 구현이 열거자가 아닌 인덱스로 요소에 액세스하므로 대부분의 루프보다 훨씬 빠릅니다 (이 답변 참조) .

중복이있는 경우 물론 예외가 발생하므로 병합하기 전에 확인해야합니다.


169
문제가 중복되면를 사용하십시오 dictionaryFrom.ToList().ForEach(x => dictionaryTo[x.Key] = x.Value). 이런 식으로 값 dictionaryFrom이 기존 키의 값보다 우선합니다.
okrumnow 2012

7
이것은 루프보다 빠를 것 같지 않습니다. ToList ()가 실제로 사전을 복사한다는 것을 잊어 버립니다. 그래도 빠른 코드! ;-)
Søren Boisen

6
ToList ()는 실제로 사전을 복사하지 않으므로 내부의 요소 참조 만 복사합니다. 기본적으로리스트 객체 자체는 메모리에 새로운 주소를 가지게됩니다. 객체는 동일합니다 !!
GuruC

4
블로그 게시물이 사라졌지 만 yay Wayback 시스템 : web.archive.org/web/20150311191313/http://diditwith.net/2006/10/…
CAD bloke

1
흠,보다 나은 존 소총의 대답, 나는 추정.
Sнаđошƒаӽ

102

여러 개의 키가있는 경우 ( "오른쪽"키가 "왼쪽"키를 대체 함), 여러 사전을 병합하고 (필요한 경우) 유형을 보존 할 수 있습니다 (유의 한 기본 공용 생성자가 필요하다는 제한 사항 포함).

public static class DictionaryExtensions
{
    // Works in C#3/VS2008:
    // Returns a new dictionary of this ... others merged leftward.
    // Keeps the type of 'this', which must be default-instantiable.
    // Example: 
    //   result = map.MergeLeft(other1, other2, ...)
    public static T MergeLeft<T,K,V>(this T me, params IDictionary<K,V>[] others)
        where T : IDictionary<K,V>, new()
    {
        T newMap = new T();
        foreach (IDictionary<K,V> src in
            (new List<IDictionary<K,V>> { me }).Concat(others)) {
            // ^-- echk. Not quite there type-system.
            foreach (KeyValuePair<K,V> p in src) {
                newMap[p.Key] = p.Value;
            }
        }
        return newMap;
    }

}

잘 했어. 이것은 내가 필요한 것입니다. 제네릭을 잘 사용합니다. 구문이 약간 어색하지만 동의 할 수는 없습니다.
Peter M

2
이 솔루션이 마음에 들었지만주의 할 점이 있습니다. this T me사전을 사용하여 new Dictionary<string, T>(StringComparer.InvariantCultureIgnoreCase)또는 이와 유사한 것을 사용하여 선언 한 경우 결과 사전은 동일한 동작을 유지하지 않습니다.
João Portela

3
당신이 만드는 경우 meDictionary<K,V>, 당신은 할 수있다 var newMap = new Dictionary<TKey, TValue>(me, me.Comparer);, 당신은 또한 여분의 피 T형식 매개 변수를.
ANeves

1
누군가이 짐승을 사용하는 방법에 대한 예가 있습니까?
Tim

1
@Tim : 아래 짐승의 예를 추가하고 ANeves의 솔루션을 추가했으며 결과가
길어졌습니다.

50

사소한 해결책은 다음과 같습니다.

using System.Collections.Generic;
...
public static Dictionary<TKey, TValue>
    Merge<TKey,TValue>(IEnumerable<Dictionary<TKey, TValue>> dictionaries)
{
    var result = new Dictionary<TKey, TValue>();
    foreach (var dict in dictionaries)
        foreach (var x in dict)
            result[x.Key] = x.Value;
    return result;
}

22

다음을 시도하십시오

static Dictionary<TKey, TValue>
    Merge<TKey, TValue>(this IEnumerable<Dictionary<TKey, TValue>> enumerable)
{
    return enumerable.SelectMany(x => x).ToDictionary(x => x.Key, y => y.Value);
}

19
Dictionary<String, String> allTables = new Dictionary<String, String>();
allTables = tables1.Union(tables2).ToDictionary(pair => pair.Key, pair => pair.Value);

1
그냥 궁금해. 단지 유니온 만이 작동하지 않을까? foreach(var kvp in Message.GetAttachments().Union(mMessage.GetImages()))생산 코드에서 사용하면 단점이 있으면 알려주십시오! :)
화성 로버트슨

1
@Michal : Union은 IEnumerable<KeyValuePair<TKey, TValue>>키와 값의 동등성에 의해 동등성이 정의되는 위치 를 반환합니다 (대체를 허용하기 위해 과부하가 있음). 이것은 foreach 루프에 적합합니다. 여기서 이것이 필요한 모든 것이지만 실제로 사전을 원하는 경우가 있습니다.
Timothy

15

나는 파티에 매우 늦었고 어쩌면 뭔가를 잃어 버렸지 만 중복 키가 없거나 OP가 말한 것처럼 "충돌의 경우 어떤 값이 dict에 저장되어 있는지는 중요하지 않습니다. 일관된 ""이 문제가 무엇입니까 (D2를 D1에 병합)?

foreach (KeyValuePair<string,int> item in D2)
            {
                 D1[item.Key] = item.Value;
            }

충분히 간단 해 보일 수도 있고 너무 간단 할 수도 있습니다. 이것은 중복 키가 없다는 것을 알고있는 일부 코드에서 사용중인 것입니다. 그래도 여전히 테스트 중이므로 나중에 찾는 것이 아니라 무언가를 간과하고 있는지 알고 싶습니다.


4
가장 깨끗하고 읽기 쉬운 솔루션 중 하나이며 다른 많은 사람들이 사용하는 ToList ()도 피합니다.
404

15

다음은 나를 위해 작동합니다. 중복이 있으면 dictA의 값을 사용합니다.

public static IDictionary<TKey, TValue> Merge<TKey, TValue>(this IDictionary<TKey, TValue> dictA, IDictionary<TKey, TValue> dictB)
    where TValue : class
{
    return dictA.Keys.Union(dictB.Keys).ToDictionary(k => k, k => dictA.ContainsKey(k) ? dictA[k] : dictB[k]);
}

@ EsbenSkovPedersen 나는 이것에 대답했을 때 (새로운 C # dev)에 직면했다고 생각하지 않습니다
Ethan Reesor

Guid를 값으로 사용하기 위해 'where TValue : class'를 제거해야했습니다
om471987 년

@ om471987 예, Guid는 클래스가 아닌 구조체이므로 의미가 있습니다. 나는 원래 그 절을 추가하지 않았을 때 약간의 문제가 있다고 생각합니다. 더 이상 이유를 기억하지 마십시오.
Ethan Reesor

10

내가 사용하는 도우미 기능은 다음과 같습니다.

using System.Collections.Generic;
namespace HelperMethods
{
    public static class MergeDictionaries
    {
        public static void Merge<TKey, TValue>(this IDictionary<TKey, TValue> first, IDictionary<TKey, TValue> second)
        {
            if (second == null || first == null) return;
            foreach (var item in second) 
                if (!first.ContainsKey(item.Key)) 
                    first.Add(item.Key, item.Value);
        }
    }
}

여기에 훌륭한 솔루션이 많이 있지만, 나는 이것의 단순함을 가장 좋아합니다. 저의 개인적인 취향입니다.
Jason Jason

3
if(first == null)이 논리는 반환 first되지 않기 때문에 쓸모 ref가 없습니다. 그리고 그것은 수 없습니다 ref이 인터페이스의 인스턴스로 선언 있기 때문에; 오류 검사를 제거하거나 클래스 인스턴스로 선언하거나 확장 방법없이 새 사전을 반환해야합니다.
Grault

@Jesdisciple-귀하의 제안에 따라 문제를 제거
Andrew Harry

8

옵션 1 : 두 사전에 중복 키가없는 경우 어떤 일이 발생하는지에 따라 다릅니다. 당신이 할 수있는 것보다 :

var result = dictionary1.Union(dictionary2).ToDictionary(k => k.Key, v => v.Value)

참고 : 사전에 중복 키가 있으면 오류가 발생합니다.

옵션 2 : 중복 키를 가질 수 있다면 where 절을 사용하여 중복 키를 처리해야합니다.

var result = dictionary1.Union(dictionary2.Where(k => !dictionary1.ContainsKey(k.Key))).ToDictionary(k => k.Key, v => v.Value)

참고 : 중복 키는 얻지 않습니다. 중복 키가 있으면 dictionary1의 키를 얻습니다.

옵션 3 : ToLookup을 사용하려는 경우 그러면 키당 여러 값을 가질 수있는 조회가 제공됩니다. 해당 조회를 사전으로 변환 할 수 있습니다.

var result = dictionaries.SelectMany(dict => dict)
                         .ToLookup(pair => pair.Key, pair => pair.Value)
                         .ToDictionary(group => group.Key, group => group.First());

6

params과부하 를 추가하는 것은 어떻습니까?

또한 IDictionary최대한의 유연성 을 위해 입력해야 합니다.

public static IDictionary<TKey, TValue> Merge<TKey, TValue>(IEnumerable<IDictionary<TKey, TValue>> dictionaries)
{
    // ...
}

public static IDictionary<TKey, TValue> Merge<TKey, TValue>(params IDictionary<TKey, TValue>[] dictionaries)
{
    return Merge((IEnumerable<TKey, TValue>) dictionaries);
}

4
그는 DictionaryExtensions 클래스를 더 멋지게 만들 수 있음을 지적하여 여기에 다른 답변을 추가하고 있습니다. 아마도이 질문은 위키 또는 git ...에 체크인 한 클래스가되어야합니다.
Chris Moschini

5

고려하여 사전 키 조회 및 삭제의 성능을 질문의 표현을했다가 해시 작업이기 때문에, 그리고 고려하는 가장 좋은 방법은, 내가 아래에 완벽하게 유효한 방법이라고 생각하고 다른 사람은 약간, 이럴를 너무 복잡하다.

    public static void MergeOverwrite<T1, T2>(this IDictionary<T1, T2> dictionary, IDictionary<T1, T2> newElements)
    {
        if (newElements == null) return;

        foreach (var e in newElements)
        {
            dictionary.Remove(e.Key); //or if you don't want to overwrite do (if !.Contains()
            dictionary.Add(e);
        }
    }

또는 다중 스레드 응용 프로그램에서 작업하고 사전이 스레드 안전해야하는 경우 다음을 수행해야합니다.

    public static void MergeOverwrite<T1, T2>(this ConcurrentDictionary<T1, T2> dictionary, IDictionary<T1, T2> newElements)
    {
        if (newElements == null || newElements.Count == 0) return;

        foreach (var ne in newElements)
        {
            dictionary.AddOrUpdate(ne.Key, ne.Value, (key, value) => value);
        }
    }

그런 다음 이것을 감싸서 사전의 열거를 처리 할 수 ​​있습니다. 에 관계없이, 당신은 이후 약 ~ O (3N) (모든 조건 인 완벽한)에서 찾고 .Add()할 것, 추가 필요하지만, 실질적으로 무료 Contains()뒤에서. 나는 그것이 훨씬 나아질 것이라고 생각하지 않습니다.

대규모 컬렉션에서 추가 작업을 제한 Count하려면 병합하려는 각 사전을 합산 하고 대상 사전의 용량을 그로 설정해야하므로 나중에 크기 조정 비용이 발생하지 않습니다. 최종 제품은 다음과 같습니다.

    public static IDictionary<T1, T2> MergeAllOverwrite<T1, T2>(IList<IDictionary<T1, T2>> allDictionaries)
    {
        var initSize = allDictionaries.Sum(d => d.Count);
        var resultDictionary = new Dictionary<T1, T2>(initSize);
        allDictionaries.ForEach(resultDictionary.MergeOverwrite);
        return resultDictionary;
    }

IList<T>내가이 방법을 사용 했다는 점에 유의하십시오 ... 주로을 복용 IEnumerable<T>하면 동일한 세트의 여러 열거를 열었으므로 연기 된 LINQ에서 사전 모음을 가져 오는 경우 비용이 많이들 수 있습니다 성명서.


4

위의 답변을 기반으로하지만 호출자가 중복을 처리 할 수 ​​있도록 Func 매개 변수를 추가하십시오.

public static Dictionary<TKey, TValue> Merge<TKey, TValue>(this IEnumerable<Dictionary<TKey, TValue>> dicts, 
                                                           Func<IGrouping<TKey, TValue>, TValue> resolveDuplicates)
{
    if (resolveDuplicates == null)
        resolveDuplicates = new Func<IGrouping<TKey, TValue>, TValue>(group => group.First());

    return dicts.SelectMany<Dictionary<TKey, TValue>, KeyValuePair<TKey, TValue>>(dict => dict)
                .ToLookup(pair => pair.Key, pair => pair.Value)
                .ToDictionary(group => group.Key, group => resolveDuplicates(group));
}

4

파티는 이제 거의 죽었지 만, 여기 내 확장 라이브러리에 들어간 user166390의 "개선 된"버전이 있습니다. 세부 사항 외에도 병합 값을 계산하기 위해 대리자를 추가했습니다.

/// <summary>
/// Merges a dictionary against an array of other dictionaries.
/// </summary>
/// <typeparam name="TResult">The type of the resulting dictionary.</typeparam>
/// <typeparam name="TKey">The type of the key in the resulting dictionary.</typeparam>
/// <typeparam name="TValue">The type of the value in the resulting dictionary.</typeparam>
/// <param name="source">The source dictionary.</param>
/// <param name="mergeBehavior">A delegate returning the merged value. (Parameters in order: The current key, The current value, The previous value)</param>
/// <param name="mergers">Dictionaries to merge against.</param>
/// <returns>The merged dictionary.</returns>
public static TResult MergeLeft<TResult, TKey, TValue>(
    this TResult source,
    Func<TKey, TValue, TValue, TValue> mergeBehavior,
    params IDictionary<TKey, TValue>[] mergers)
    where TResult : IDictionary<TKey, TValue>, new()
{
    var result = new TResult();
    var sources = new List<IDictionary<TKey, TValue>> { source }
        .Concat(mergers);

    foreach (var kv in sources.SelectMany(src => src))
    {
        TValue previousValue;
        result.TryGetValue(kv.Key, out previousValue);
        result[kv.Key] = mergeBehavior(kv.Key, kv.Value, previousValue);
    }

    return result;
}

2

@Tim : 주석이어야하지만 주석은 코드 편집을 허용하지 않습니다.

Dictionary<string, string> t1 = new Dictionary<string, string>();
t1.Add("a", "aaa");
Dictionary<string, string> t2 = new Dictionary<string, string>();
t2.Add("b", "bee");
Dictionary<string, string> t3 = new Dictionary<string, string>();
t3.Add("c", "cee");
t3.Add("d", "dee");
t3.Add("b", "bee");
Dictionary<string, string> merged = t1.MergeLeft(t2, t2, t3);

참고 : @ ANdrews의 수정 사항을 @Andrew Orsich의 솔루션에 적용 했으므로 이제 MergeLeft는 다음과 같습니다.

public static Dictionary<K, V> MergeLeft<K, V>(this Dictionary<K, V> me, params IDictionary<K, V>[] others)
    {
        var newMap = new Dictionary<K, V>(me, me.Comparer);
        foreach (IDictionary<K, V> src in
            (new List<IDictionary<K, V>> { me }).Concat(others))
        {
            // ^-- echk. Not quite there type-system.
            foreach (KeyValuePair<K, V> p in src)
            {
                newMap[p.Key] = p.Value;
            }
        }
        return newMap;
    }

내가 쓴 것에 가깝습니다. 이 기능은 Dictionary유형 에만 적용 됩니다. 다른 사전 유형 ( ConcurrentDictionary, ReadOnlyDictionary등)에 과부하를 추가 할 수 있습니다 . 새 목록을 만들지 않는다고 생각하고 개인적으로 concat이 필요합니다. 방금 params 배열을 반복 한 다음 각 사전의 각 KVP를 반복했습니다. 단점이 보이지 않습니다.
분쇄

Dictionary
Mariusz Jamro

Dictionary에서 확장 메소드를 사용한 경우에도 항상 객체를 가져 옵니다 ConcurrentDictionary. 이로 인해 버그를 추적하기가 어려울 수 있습니다.
Marcel

2

나는 이것이 오래된 질문이라는 것을 알고 있지만 이제 우리는 LINQ를 가지고 있으므로 다음과 같이 한 줄로 할 수 있습니다

Dictionary<T1,T2> merged;
Dictionary<T1,T2> mergee;
mergee.ToList().ForEach(kvp => merged.Add(kvp.Key, kvp.Value));

또는

mergee.ToList().ForEach(kvp => merged.Append(kvp));

죄송합니다 downvote의 @Cruces에 대한,하지만 첫 번째 예는 응답 복제 stackoverflow.com/a/6695211/704808을 하고 두 번째 예는 단지에서 각 항목을 추가 한 후 확장을 IEnumerable <KeyValuePair <문자열, 문자열을 >> 폐기 유지 mergee.
weir

2

C #에 익숙하지 않은 복잡한 답변을 보는 것이 무서웠습니다.

다음은 간단한 답변입니다.
d1, d2 등을 병합합니다. 사전을 만들고 겹치는 키를 처리합니다 (아래 예에서 "b").

실시 예 1

{
    // 2 dictionaries,  "b" key is common with different values

    var d1 = new Dictionary<string, int>() { { "a", 10 }, { "b", 21 } };
    var d2 = new Dictionary<string, int>() { { "c", 30 }, { "b", 22 } };

    var result1 = d1.Concat(d2).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.First().Value);
    // result1 is  a=10, b=21, c=30    That is, took the "b" value of the first dictionary

    var result2 = d1.Concat(d2).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.Last().Value);
    // result2 is  a=10, b=22, c=30    That is, took the "b" value of the last dictionary
}

실시 예 2

{
    // 3 dictionaries,  "b" key is common with different values

    var d1 = new Dictionary<string, int>() { { "a", 10 }, { "b", 21 } };
    var d2 = new Dictionary<string, int>() { { "c", 30 }, { "b", 22 } };
    var d3 = new Dictionary<string, int>() { { "d", 40 }, { "b", 23 } };

    var result1 = d1.Concat(d2).Concat(d3).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.First().Value);
    // result1 is  a=10, b=21, c=30, d=40    That is, took the "b" value of the first dictionary

    var result2 = d1.Concat(d2).Concat(d3).GroupBy(ele => ele.Key).ToDictionary(ele => ele.Key, ele => ele.Last().Value);
    // result2 is  a=10, b=23, c=30, d=40    That is, took the "b" value of the last dictionary
}

더 복잡한 시나리오는 다른 답변을 참조하십시오.
도움이 되었기를 바랍니다.


2
using System.Collections.Generic;
using System.Linq;

public static class DictionaryExtensions
{
    public enum MergeKind { SkipDuplicates, OverwriteDuplicates }
    public static void Merge<K, V>(this IDictionary<K, V> target, IDictionary<K, V> source, MergeKind kind = MergeKind.SkipDuplicates) =>
        source.ToList().ForEach(_ => { if (kind == MergeKind.OverwriteDuplicates || !target.ContainsKey(_.Key)) target[_.Key] = _.Value; });
}

당신은 건너 뛰거나 무시하거나 (중복) 중복을 덮어 쓸 수 있습니다 : 그리고 Bob의 삼촌은 Linq 성능에 대해 지나치게 까다 롭지 않지만 대신 간결한 유지 관리 가능한 코드를 선호합니다.이 경우 기본 MergeKind를 제거 할 수 있습니다. 발신자를위한 선택과 개발자에게 결과가 무엇인지 알도록하십시오!


bool이 할 때 불필요한 이진 열거없이 아래의 세련된 답변 ...
mattjs

2

'추가'라는 확장 메서드를 사용하는 경우 컬렉션 초기화 도구를 사용하여 다음과 같이 필요한만큼 사전을 결합 할 수 있습니다.

public static void Add<K, V>(this Dictionary<K, V> d, Dictionary<K, V> other) {
  foreach (var kvp in other)
  {
    if (!d.ContainsKey(kvp.Key))
    {
      d.Add(kvp.Key, kvp.Value);
    }
  }
}


var s0 = new Dictionary<string, string> {
  { "A", "X"}
};
var s1 = new Dictionary<string, string> {
  { "A", "X" },
  { "B", "Y" }
};
// Combine as many dictionaries and key pairs as needed
var a = new Dictionary<string, string> {
  s0, s1, s0, s1, s1, { "C", "Z" }
};

1

확장 방법을 사용하여 병합. 중복 키가있는 경우 예외가 발생하지 않지만 해당 키를 두 번째 사전의 키로 바꿉니다.

internal static class DictionaryExtensions
{
    public static Dictionary<T1, T2> Merge<T1, T2>(this Dictionary<T1, T2> first, Dictionary<T1, T2> second)
    {
        if (first == null) throw new ArgumentNullException("first");
        if (second == null) throw new ArgumentNullException("second");

        var merged = new Dictionary<T1, T2>();
        first.ToList().ForEach(kv => merged[kv.Key] = kv.Value);
        second.ToList().ForEach(kv => merged[kv.Key] = kv.Value);

        return merged;
    }
}

용법:

Dictionary<string, string> merged = first.Merge(second);

1

기존의 경우 부울 기본값으로 비파괴 병합의 부울 기본값을 사용하거나 열거 형을 사용하지 않고 참이면 완전히 덮어 쓰는 사용에서 단순화되었습니다. 더 멋진 코드가 필요없이 여전히 내 자신의 요구에 맞습니다.

using System.Collections.Generic;
using System.Linq;

public static partial class Extensions
{
    public static void Merge<K, V>(this IDictionary<K, V> target, IDictionary<K, V> source, bool overwrite = false)
    {
        source.ToList().ForEach(_ => {
            if ((!target.ContainsKey(_.Key)) || overwrite)
                target[_.Key] = _.Value;
        });
    }
}

0

를 사용하여 병합하면 EqualityComparer항목을 다른 값 / 유형과 비교할 수 있습니다. 여기에서 KeyValuePair(사전을 열거 할 때 항목 유형)을로 매핑 합니다 Key.

public class MappedEqualityComparer<T,U> : EqualityComparer<T>
{
    Func<T,U> _map;

    public MappedEqualityComparer(Func<T,U> map)
    {
        _map = map;
    }

    public override bool Equals(T x, T y)
    {
        return EqualityComparer<U>.Default.Equals(_map(x), _map(y));
    }

    public override int GetHashCode(T obj)
    {
        return _map(obj).GetHashCode();
    }
}

용법:

// if dictA and dictB are of type Dictionary<int,string>
var dict = dictA.Concat(dictB)
                .Distinct(new MappedEqualityComparer<KeyValuePair<int,string>,int>(item => item.Key))
                .ToDictionary(item => item.Key, item=> item.Value);

0

또는 :

public static IDictionary<TKey, TValue> Merge<TKey, TValue>( IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y)
    {
        return x
            .Except(x.Join(y, z => z.Key, z => z.Key, (a, b) => a))
            .Concat(y)
            .ToDictionary(z => z.Key, z => z.Value);
    }

결과는 중복 항목 "y"가이기는 조합입니다.


0
public static IDictionary<K, V> AddRange<K, V>(this IDictionary<K, V> one, IDictionary<K, V> two)
        {
            foreach (var kvp in two)
            {
                if (one.ContainsKey(kvp.Key))
                    one[kvp.Key] = two[kvp.Key];
                else
                    one.Add(kvp.Key, kvp.Value);
            }
            return one;
        }

0

@ user166390의 버전은 추가 된 답변 IEqualityComparer 대소 문자를 구분하지 않는 키 비교를 위해 매개 변수로 합니다.

    public static T MergeLeft<T, K, V>(this T me, params Dictionary<K, V>[] others)
        where T : Dictionary<K, V>, new()
    {
        return me.MergeLeft(me.Comparer, others);
    }

    public static T MergeLeft<T, K, V>(this T me, IEqualityComparer<K> comparer, params Dictionary<K, V>[] others)
        where T : Dictionary<K, V>, new()
    {
        T newMap = Activator.CreateInstance(typeof(T), new object[] { comparer }) as T;

        foreach (Dictionary<K, V> src in 
            (new List<Dictionary<K, V>> { me }).Concat(others))
        {
            // ^-- echk. Not quite there type-system.
            foreach (KeyValuePair<K, V> p in src)
            {
                newMap[p.Key] = p.Value;
            }
        }
        return newMap;
    }

0
fromDic.ToList().ForEach(x =>
        {
            if (toDic.ContainsKey(x.Key))
                toDic.Remove(x.Key);
            toDic.Add(x);
        });
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.