Mesclando dicionários em C #


493

Qual é a melhor maneira de mesclar 2 ou mais dicionários ( Dictionary<T1,T2>) em C #? (Recursos 3.0 como o LINQ são bons).

Estou pensando em uma assinatura de método ao longo das linhas de:

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

ou

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

Edição: Obtive uma solução legal de JaredPar e Jon Skeet, mas eu estava pensando em algo que lida com chaves duplicadas. Em caso de colisão, não importa qual valor é salvo no ditado, desde que seja consistente.


174
Independente, mas para quem procura mesclar apenas dois dicionários sem verificações de chave duplicadas, isso funciona muito bem:dicA.Concat(dicB).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
Benjol

6
@Benjol, você poderia ter adicionado isso na seção de respostas
M.Kumaran 2/13/13

4
A mesclagem de Clojure em C #:dict1.Concat(dict2).GroupBy(p => p.Key).ToDictionary(g => g.Key, g => g.Last().Value)
Bruce

@ Benjol: elimine duplicatas, preferindo entradas do primeiro dicionário com dictA.Concat(dictB.Where(kvp => !dictA.ContainsKey(kvp.Key))).ToDictionary(kvp=> kvp.Key, kvp => kvp.Value).
Suncat2000

Respostas:


321

Isso depende em parte do que você deseja que aconteça se você encontrar duplicatas. Por exemplo, você pode fazer:

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

Isso gerará uma exceção se você receber chaves duplicadas.

EDIT: Se você usar o ToLookup, obterá uma pesquisa que pode ter vários valores por chave. Você pode converter isso em um dicionário:

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

É um pouco feio - e ineficiente - mas é a maneira mais rápida de fazer isso em termos de código. (Eu não testei, é certo.)

Você poderia escrever seu próprio método de extensão ToDictionary2, é claro (com um nome melhor, mas não tenho tempo para pensar em um agora) - não é muito difícil de fazer, substituindo (ou ignorando) chaves duplicadas. A parte importante (na minha opinião) é usar o SelectMany e perceber que um dicionário suporta iteração sobre seus pares de chave / valor.


3
Para realmente mesclar os valores em vez de apenas tirá-los do primeiro dicionário, você pode substituir group => group.First () por group => group.SelectMany (value => value) na resposta editada por Jon Skeet.
18711

3
Agora estou pensando que o GroupBy seria mais adequado do que o ToLookup? Eu acho que o ILookup apenas adiciona um indexador, uma propriedade size e contém um método no topo do IGrouping - por isso deve ser um pouquinho mais rápido?
toong

2
@ toong: Qualquer um deve ficar bem, para ser honesto. Usar GroupBypoderia realmente ser um pouco mais eficiente - mas duvido que seria significativo de qualquer maneira.
Jon Skeet

1
@ Joe: Isso é simples, fornecendo o comparador para a ToDictionarychamada do método. Eu preferiria manter a resposta mais simples para o caso mais comum e espero que qualquer pessoa que precise de um comparador personalizado seja capaz de encontrar a sobrecarga relevante.
perfil completo de Jon Skeet

1
@ Emerge: sugiro que faça uma nova pergunta com requisitos precisos.
Jon Skeet

265

Eu faria assim:

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

Simples e fácil. De acordo com este post do blog , é ainda mais rápido que a maioria dos loops, pois sua implementação subjacente acessa elementos por índice, em vez de enumerador (veja esta resposta) .

Obviamente, isso gerará uma exceção se houver duplicatas, portanto você deverá verificar antes da mesclagem.


169
Se duplicados importam, use dictionaryFrom.ToList().ForEach(x => dictionaryTo[x.Key] = x.Value). Dessa forma, os valores dictionaryFromsubstituem o valor de uma chave possivelmente existente.
okrumnow

7
É improvável que seja mais rápido que um loop - você esquece que ToList () acaba copiando o dicionário. Mais rápido para codificar! ;-)
Søren Boisen

6
Isso é mais rápido. Como o ToList () na verdade não acaba copiando o dicionário, ele apenas copia as referências dos elementos dentro deles. Basicamente, o próprio objeto da lista terá um novo endereço na memória, os objetos são os mesmos !!
GuruC 18/03/2015

4
A publicação no blog desapareceu, mas a máquina Wayback: web.archive.org/web/20150311191313/http://diditwith.net/2006/10/…
CAD bloke

1
Hmm, melhor do que a resposta de Jon Skeet, eu presumo.
12126

102

Isso não explode se houver várias chaves (as teclas "mais corretas" substituem as teclas "esquerdas"), pode mesclar vários dicionários (se desejado) e preserva o tipo (com a restrição de que requer um construtor público padrão significativo):

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

}

Bem feito. Era exatamente disso que eu precisava. Bom uso de genéricos. Concordou que a sintaxe é um pouco estranha, mas nada que você possa fazer sobre isso.
Peter M

2
Gostei dessa solução, mas há uma ressalva: se o this T medicionário foi declarado usando new Dictionary<string, T>(StringComparer.InvariantCultureIgnoreCase)ou algo semelhante, o dicionário resultante não manterá o mesmo comportamento.
João Portela

3
Se você criar meum Dictionary<K,V>, poderá fazê var newMap = new Dictionary<TKey, TValue>(me, me.Comparer);-lo e também evitará o Tparâmetro de tipo extra .
ANeves

1
Alguém tem um exemplo de como usar este animal?
Tim

1
@ Tim: Eu adicionado um exemplo para o animal a seguir, I adicionada a solução por ANeves e o resultado é um animal domesticado mais :)
keni

50

A solução trivial seria:

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

Tente o seguinte

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
Apenas imaginando - a Union não funcionaria? foreach(var kvp in Message.GetAttachments().Union(mMessage.GetImages()))Utilizando-o no código de produção, se houver alguma desvantagem, entre em contato! :)
Mars Robertson

1
@Michal: Union retornará um IEnumerable<KeyValuePair<TKey, TValue>>onde igualdade é definida pela igualdade de chave e valor (há uma sobrecarga para permitir alternativas). Isso é bom para um loop foreach, onde isso é tudo o que é necessário, mas há casos em que você realmente deseja o dicionário.
Timothy

15

Estou muito atrasado para a festa e talvez esteja perdendo alguma coisa, mas se não houver chaves duplicadas ou, como o OP diz: "Em caso de colisão, não importa qual valor será salvo no ditado, desde que seja consistente ", o que há de errado com este (mesclando D2 em D1)?

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

Parece simples o suficiente, talvez muito simples, eu me pergunto se estou perdendo alguma coisa. É isso que estou usando em algum código em que sei que não há chaves duplicadas. No entanto, ainda estou testando, então gostaria de saber agora se estou ignorando alguma coisa, em vez de descobrir mais tarde.


4
Uma das soluções mais limpas e legíveis imo e também evita o ToList () que muitos outros usam.
404

15

O seguinte funciona para mim. Se houver duplicatas, ele usará o valor de 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 Eu não acho que eu tinha encontrado que quando eu respondi a essa (nova C # dev)
Ethan Reesor

Eu tive que remover 'where TValue: class' para usar Guid como um valor
om471987

@ om471987 Sim, Guid é uma estrutura, não uma classe, então isso faz sentido. Acho que originalmente tive algum problema quando não adicionei essa cláusula. Não lembro mais o porquê.
Ethan Reesor

10

Aqui está uma função auxiliar que eu uso:

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

Existem muitas ótimas soluções listadas aqui, mas eu gosto bastante da simplicidade dessa. Apenas minha preferência pessoal.
jason

3
if(first == null)então essa lógica é inútil porque firstnão refé e não é retornada. E não pode ser refporque é declarado como uma instância de interface; você precisa remover a verificação de erros ou declará-la como uma instância de classe ou simplesmente retornar um novo dicionário (sem método de extensão).
Grault 15/03

@Jesdisciple - removido a questão como por sua sugestão
Andrew Harry

8

Opção 1: Isso depende do que você deseja que aconteça se tiver certeza de que não possui chave duplicada nos dois dicionários. do que você poderia fazer:

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

Nota: Isso gerará erro se você receber alguma chave duplicada nos dicionários.

Opção 2: se você pode ter uma chave duplicada, precisará lidar com a chave duplicada com a cláusula using of where.

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

Nota: Não receberá chave duplicada. se houver alguma chave duplicada, ela receberá a chave do dictionary1.

Opção 3: se você deseja usar o ToLookup. você terá uma pesquisa que pode ter vários valores por chave. Você pode converter essa pesquisa em um dicionário:

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

6

Que tal adicionar uma paramssobrecarga?

Além disso, você deve digitá-los IDictionarypara obter o máximo de flexibilidade.

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
Ele meio que adiciona outras respostas aqui, observando que você pode tornar a classe DictionaryExtensions ainda mais agradável. Talvez essa pergunta deva se tornar um wiki ou uma classe registrada no git ....
Chris Moschini

5

Considerando o desempenho das pesquisas e exclusões de chaves do dicionário, pois são operações de hash, e considerando que a redação da pergunta era a melhor maneira, acho que abaixo é uma abordagem perfeitamente válida, e as outras são um pouco complicadas demais, IMHO.

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

OU se você estiver trabalhando em um aplicativo multithread e seu dicionário precisar ser seguro para threads de qualquer maneira, faça o seguinte:

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

Em seguida, você pode agrupar isso para fazer com que ele lide com uma enumeração de dicionários. Independentemente disso, você está olhando para ~ O (3n) (todas as condições são perfeitas), já que .Add()ele fará um adicional, desnecessário, mas praticamente gratuito Contains()nos bastidores. Eu não acho que fica muito melhor.

Se você deseja limitar operações extras em coleções grandes, você deve resumir o Countde cada dicionário que você está prestes a mesclar e definir a capacidade do dicionário de destino para isso, o que evita o custo posterior de redimensionamento. Então, o produto final é algo como isto ...

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

Observe que eu adotei um IList<T>método nesse método ... principalmente porque se você IEnumerable<T>usar um , você se abriu para várias enumerações do mesmo conjunto, o que pode ser muito caro se você tiver sua coleção de dicionários de um LINQ adiado declaração.


4

Com base nas respostas acima, mas adicionando um parâmetro Func para permitir que o chamador lide com as duplicatas:

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

A festa está praticamente morta agora, mas aqui está uma versão "aprimorada" do user166390 que entrou na minha biblioteca de extensões. Além de alguns detalhes, adicionei um delegado para calcular o valor mesclado.

/// <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: Deve ser um comentário, mas os comentários não permitem a edição do código.

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

Nota: Apliquei a modificação de @ANeves na solução de @Andrew Orsich, para que o MergeLeft fique assim agora:

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

Perto do que eu escrevi. Isso, ofc, funcionará apenas para Dictionarytipos. Sobrecargas podem ser adicionadas a outros tipos de dicionário ( ConcurrentDictionary, ReadOnlyDictionaryetc). Não acho que a criação de uma nova lista e o concat sejam necessários pessoalmente. Eu apenas iterava primeiro a matriz de parâmetros, depois iterava cada KVP de cada dicionário. Não vejo um retrocesso.
esmagar

Você pode usar IDictionary vez de dicionário
Mariusz Jamro

Você sempre obteria um Dictionaryobjeto, mesmo que usasse o método de extensão em a ConcurrentDictionary. Isso pode levar a erros de rastreamento difíceis.
Marcel

2

Sei que essa é uma pergunta antiga, mas como agora temos o LINQ, você pode fazer isso em uma única linha como esta

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

ou

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

Desculpe pelo voto negativo @Cruces, mas seu primeiro exemplo duplica a resposta stackoverflow.com/a/6695211/704808 e seu segundo exemplo continua descartando o IEnumerable expandido <KeyValuePair <string, string >> após anexar cada item de mergee.
Weir

2

Ficou com medo de ver respostas complexas, sendo novo no C #.

Aqui estão algumas respostas simples.
Mesclando d1, d2 e assim por diante .. dicionários e manipule quaisquer teclas sobrepostas ("b" nos exemplos abaixo):

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

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

Para cenários mais complexos, consulte outras respostas.
Espero que tenha ajudado.


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

Você pode pular / ignorar (padrão) ou substituir as duplicatas: e Bob é seu tio, desde que você não seja muito exigente com o desempenho do Linq, mas prefira um código sustentável e conciso como eu: nesse caso, você pode remover o MergeKind.SkipDuplicates padrão para impor uma escolha para o chamador e conscientize o desenvolvedor sobre quais serão os resultados!


Resposta refinado abaixo sem enum binário desnecessária quando um bool vai fazer ...
mattjs

2

Observe que se você usar um método de extensão chamado 'Adicionar', poderá usar inicializadores de coleção para combinar quantos dicionários forem necessários, como este:

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

Mesclando usando um método de extensão. Não lança exceção quando há chaves duplicadas, mas as substitui por chaves do segundo dicionário.

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

Uso:

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

1

Simplificado do uso em comparação com a minha resposta anterior com um padrão booleano de mesclagem não destrutiva, se existente ou sobrescrever completamente se verdadeiro, em vez de usar uma enumeração. Ele ainda atende às minhas próprias necessidades sem que nenhum código mais sofisticado seja necessário:

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

Mesclando usando um EqualityComparerque mapeia itens para comparação com um valor / tipo diferente. Aqui vamos mapear de KeyValuePair(tipo de item ao enumerar um dicionário) para 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();
    }
}

Uso:

// 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

ou:

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

o resultado é uma união em que para entradas duplicadas "y" vence.


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

Uma versão de @ user166390 responde com um IEqualityComparerparâmetro adicionado para permitir a comparação de chaves sem distinção entre maiúsculas e minúsculas.

    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);
        });
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.