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.
c#
dictionary
merge
orip
fonte
fonte
dicA.Concat(dicB).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
dict1.Concat(dict2).GroupBy(p => p.Key).ToDictionary(g => g.Key, g => g.Last().Value)
dictA.Concat(dictB.Where(kvp => !dictA.ContainsKey(kvp.Key))).ToDictionary(kvp=> kvp.Key, kvp => kvp.Value)
.Respostas:
Isso depende em parte do que você deseja que aconteça se você encontrar duplicatas. Por exemplo, você pode fazer:
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:
É 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.
fonte
GroupBy
poderia realmente ser um pouco mais eficiente - mas duvido que seria significativo de qualquer maneira.ToDictionary
chamada 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.Eu faria assim:
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.
fonte
dictionaryFrom.ToList().ForEach(x => dictionaryTo[x.Key] = x.Value)
. Dessa forma, os valoresdictionaryFrom
substituem o valor de uma chave possivelmente existente.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):
fonte
this T me
dicionário foi declarado usandonew Dictionary<string, T>(StringComparer.InvariantCultureIgnoreCase)
ou algo semelhante, o dicionário resultante não manterá o mesmo comportamento.me
umDictionary<K,V>
, poderá fazêvar newMap = new Dictionary<TKey, TValue>(me, me.Comparer);
-lo e também evitará oT
parâmetro de tipo extra .A solução trivial seria:
fonte
Tente o seguinte
fonte
fonte
foreach(var kvp in Message.GetAttachments().Union(mMessage.GetImages()))
Utilizando-o no código de produção, se houver alguma desvantagem, entre em contato! :)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.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)?
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.
fonte
O seguinte funciona para mim. Se houver duplicatas, ele usará o valor de dictA.
fonte
Aqui está uma função auxiliar que eu uso:
fonte
if(first == null)
então essa lógica é inútil porquefirst
nãoref
é e não é retornada. E não pode serref
porque é 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).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:
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.
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:
fonte
Que tal adicionar uma
params
sobrecarga?Além disso, você deve digitá-los
IDictionary
para obter o máximo de flexibilidade.fonte
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.
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:
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 gratuitoContains()
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
Count
de 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 ...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.fonte
Com base nas respostas acima, mas adicionando um parâmetro Func para permitir que o chamador lide com as duplicatas:
fonte
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.
fonte
@ Tim: Deve ser um comentário, mas os comentários não permitem a edição do código.
Nota: Apliquei a modificação de @ANeves na solução de @Andrew Orsich, para que o MergeLeft fique assim agora:
fonte
Dictionary
tipos. Sobrecargas podem ser adicionadas a outros tipos de dicionário (ConcurrentDictionary
,ReadOnlyDictionary
etc). 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.Dictionary
objeto, mesmo que usasse o método de extensão em aConcurrentDictionary
. Isso pode levar a erros de rastreamento difíceis.Sei que essa é uma pergunta antiga, mas como agora temos o LINQ, você pode fazer isso em uma única linha como esta
ou
fonte
mergee
.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
Exemplo 2
Para cenários mais complexos, consulte outras respostas.
Espero que tenha ajudado.
fonte
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!
fonte
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:
fonte
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.
Uso:
fonte
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:
fonte
Mesclando usando um
EqualityComparer
que mapeia itens para comparação com um valor / tipo diferente. Aqui vamos mapear deKeyValuePair
(tipo de item ao enumerar um dicionário) paraKey
.Uso:
fonte
ou:
o resultado é uma união em que para entradas duplicadas "y" vence.
fonte
fonte
Uma versão de @ user166390 responde com um
IEqualityComparer
parâmetro adicionado para permitir a comparação de chaves sem distinção entre maiúsculas e minúsculas.fonte
fonte