Qual é a melhor maneira de iterar sobre um dicionário?

2627

Eu já vi algumas maneiras diferentes de percorrer um dicionário em c #. Existe uma maneira padrão?

Jake Stewart
fonte
108
Estou surpreso que haja muitas respostas para essa pergunta, além de uma que foi votada 923 vezes. (Usa foreach ofcourse). Eu diria, ou pelo menos acrescento que, se você precisar percorrer um dicionário, é provável que esteja usá-lo de forma incorreta / inadequada .. Eu tive que fazer esse comentário, porque vi dicionários sendo abusados ​​de maneiras que o IMHO não era apropriado ... Sim, pode haver raras circunstâncias quando você repete o dicionário, em vez de pesquisar, e é isso que Ele foi projetado para .. Lembre-se disso antes de se perguntar como iterar em um dicionário.
Vikas Gupta
74
@VikasGupta O que você sugeriria para fazer algo com uma coleção de pares de valores-chave quando você não sabe quais serão as chaves?
Nasch
26
@displayName Se você deseja fazer algo com cada par de valores-chave, mas não tem uma referência às chaves a serem usadas para procurar valores, iteraria no dicionário, certo? Eu estava apenas apontando que poderia haver momentos em que você desejaria fazer isso, apesar da alegação de Vikas de que esse geralmente é um uso incorreto.
Nasch
34
Dizer que o uso é incorreto implica que há uma alternativa melhor. O que é essa alternativa?
Kyle Delaney
40
O VikasGupta está errado, posso afirmar que, após muitos anos de programação C # e C ++ de alto desempenho em cenários não-teóricos. De fato, há casos frequentes em que se pode criar um dicionário, armazenar pares de valores-chave exclusivos e, em seguida, iterar sobre esses valores, que comprovadamente possuem chaves exclusivas na coleção. Criar outras coleções é uma maneira realmente ineficiente e dispendiosa de evitar a iteração do dicionário. Por favor, forneça uma boa alternativa como resposta à pergunta que esclareça seu ponto de vista, caso contrário, seu comentário não fará sentido.
sɐunıɔ ןɐ qɐp

Respostas:

3714
foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}
Pablo Fernandez
fonte
29
E se eu não souber exatamente o tipo de chave / valor no Dicionário. Usar var entryé melhor nesse caso e, portanto, votei esta resposta em uma segunda olhada em vez da acima.
Ozair Kafray
93
@OzairKafray usando varquando você não sabe o tipo geralmente é uma prática ruim.
Nate
52
Essa resposta é superior porque Pablo não adotou como padrão o uso lento do "codificador" que ofusca o tipo de retorno.
MonkeyWrench
119
@MonkeyWrench: Meh. O Visual Studio sabe qual é o tipo; tudo o que você precisa fazer é passar o mouse sobre a variável para descobrir.
Robert Harvey
31
Pelo que entendi, varsó funciona se o tipo for conhecido em tempo de compilação. Se o Visual Studio souber o tipo, também estará disponível para você descobrir.
Kyle Delaney
866

Se você estiver tentando usar um dicionário genérico em C #, como usaria uma matriz associativa em outro idioma:

foreach(var item in myDictionary)
{
  foo(item.Key);
  bar(item.Value);
}

Ou, se você só precisar iterar sobre a coleção de chaves, use

foreach(var item in myDictionary.Keys)
{
  foo(item);
}

E, por último, se você estiver interessado apenas nos valores:

foreach(var item in myDictionary.Values)
{
  foo(item);
}

(Observe que a varpalavra-chave é um recurso opcional do C # 3.0 e acima; você também pode usar o tipo exato de suas chaves / valores aqui)

Jacob
fonte
11
o recurso var é mais necessário para o seu primeiro bloco de código :)
Nawfal
27
Compreendo que essa resposta mostre que você pode iterar sobre as chaves ou os valores explicitamente.
Rotsiser Mho
11
Eu não gosto do uso de var aqui. Dado que é apenas açúcar sintático, por que usá-lo aqui? Quando alguém está tentando ler o código, terá que pular o código para determinar o tipo de myDictionary(a menos que esse seja o nome real, é claro). Acho que usando var é bom quando o tipo é por exemplo óbvio var x = "some string", mas quando não é imediatamente óbvio que eu acho que é preguiçoso codificação que fere o leitor de código / revisor
James Wierzba
8
vardeve ser usado com moderação, na minha opinião. Particularmente aqui, não é construtivo: o tipo KeyValuePairé provavelmente relevante para a questão.
Sinjai
3
vartem um propósito único e não acredito que seja um açúcar "sintático". Usá-lo propositadamente é uma abordagem apropriada.
21718 Joshua K
159

Em alguns casos, você pode precisar de um contador que pode ser fornecido pela implementação de loop for. Para isso, o LINQ fornece o ElementAtque permite o seguinte:

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}
Maurício Fedatto
fonte
21
Para usar o método '.ElementAt', lembre-se: using System.Linq; Isso não está inclinado em fx. classes de teste geradas automaticamente.
Tinia
14
Este é o caminho a percorrer se você estiver modificando os valores associados às chaves. Caso contrário, uma exceção será lançada ao modificar e usar foreach ().
precisa
27
Não é ElementAtuma operação O (n)?
Arturo Torres Sánchez
162
Esta resposta é completamente indigna de tantos votos positivos. Um dicionário não tem ordem implícita, portanto, a utilização .ElementAtnesse contexto pode levar a erros sutis. Muito mais sério é o argumento de Arturo acima. Você repetirá os dictionary.Count + 1tempos do dicionário que levam à complexidade de O (n ^ 2) para uma operação que deve ser apenas O (n). Se você realmente precisa de um índice (se precisar, provavelmente está usando o tipo de coleção errado), deve iterar dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})e não usar .ElementAtdentro do loop.
spender 02/03
5
ElementAt - o (n) operação! A sério? Este é o exemplo de como você não deve fazê-lo. Esses muitos votos positivos?
Mukesh Adhvaryu
96

Depende se você está atrás das chaves ou dos valores ...

Na Dictionary(TKey, TValue)descrição da classe MSDN :

// When you use foreach to enumerate dictionary elements,
// the elements are retrieved as KeyValuePair objects.
Console.WriteLine();
foreach( KeyValuePair<string, string> kvp in openWith )
{
    Console.WriteLine("Key = {0}, Value = {1}", 
        kvp.Key, kvp.Value);
}

// To get the values alone, use the Values property.
Dictionary<string, string>.ValueCollection valueColl =
    openWith.Values;

// The elements of the ValueCollection are strongly typed
// with the type that was specified for dictionary values.
Console.WriteLine();
foreach( string s in valueColl )
{
    Console.WriteLine("Value = {0}", s);
}

// To get the keys alone, use the Keys property.
Dictionary<string, string>.KeyCollection keyColl =
    openWith.Keys;

// The elements of the KeyCollection are strongly typed
// with the type that was specified for dictionary keys.
Console.WriteLine();
foreach( string s in keyColl )
{
    Console.WriteLine("Key = {0}", s);
}
J Healy
fonte
82

Geralmente, pedir "o melhor caminho" sem um contexto específico é como perguntar qual é a melhor cor ?

Por um lado, há muitas cores e não há melhor cor. Depende da necessidade e, muitas vezes, do gosto também.

Por outro lado, existem muitas maneiras de iterar um Dicionário em C # e não há a melhor maneira. Depende da necessidade e, muitas vezes, do gosto também.

Maneira mais direta

foreach (var kvp in items)
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

Se você precisar apenas do valor (permite chamá-lo item, mais legível que kvp.Value).

foreach (var item in items.Values)
{
    doStuff(item)
}

Se você precisar de uma ordem de classificação específica

Geralmente, os iniciantes ficam surpresos com a ordem de enumeração de um dicionário.

O LINQ fornece uma sintaxe concisa que permite especificar a ordem (e muitas outras coisas), por exemplo:

foreach (var kvp in items.OrderBy(kvp => kvp.Key))
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

Novamente, você pode precisar apenas do valor. O LINQ também fornece uma solução concisa para:

  • itera diretamente no valor (permite chamá-lo item, mais legível que kvp.Value)
  • mas classificado pelas chaves

Aqui está:

foreach (var item in items.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value))
{
    doStuff(item)
}

Existem muitos outros casos de uso do mundo real que você pode fazer com esses exemplos. Se você não precisa de um pedido específico, siga o "caminho mais direto" (veja acima)!

Stéphane Gourichon
fonte
O último deve ser .Valuese não uma cláusula de seleção.
Mafii 23/09/16
@Mafii Você tem certeza? Os valores retornados por OrderBy não são do tipo KeyValuePair, eles não têm Valuecampo. O tipo exato que vejo aqui é IOrderedEnumerable<KeyValuePair<TKey, TValue>>. Talvez você quis dizer outra coisa? Você pode escrever uma linha completa mostrando o que você quer dizer (e testá-lo)?
Stéphane Gourichon 23/09/16
Eu acho que esta resposta contém o que quero dizer: stackoverflow.com/a/141105/5962841 mas me corrija se eu confuso algo
Mafii
1
Leia novamente a minha resposta, explicações entre as seções de código informam o contexto. A resposta que você mencionou é como a segunda seção de código da minha resposta (não é necessário fazer pedido). Lá eu escrevi items.Valuecomo você sugeriu. No caso da quarta seção que você comentou, Select()é uma maneira de fazer foreachenumerar diretamente os valores no dicionário, em vez de pares de valores-chave. Se, de alguma forma, você não gostar do Select()caso, talvez prefira a terceira seção de código. O objetivo da quarta seção é mostrar que é possível pré-processar a coleção com o LINQ.
Stéphane Gourichon 23/09/16
1
Se você fizer .Keys.Orderby()isso, iterará em uma lista de chaves. Se é tudo o que você precisa, tudo bem. Se você precisar de valores, no loop, você precisará consultar o dicionário em cada chave para obter o valor. Em muitos cenários, não fará diferença prática. No cenário de alto desempenho, será. Como escrevi no começo da resposta: "existem muitas maneiras (...) e não há melhor maneira. Depende da necessidade e, muitas vezes, do gosto também".
Stéphane Gourichon 23/09/16
53

Eu diria que foreach é o caminho padrão, embora obviamente dependa do que você está procurando

foreach(var kvp in my_dictionary) {
  ...
}

É isso que você está procurando?

George Mauer
fonte
42
Hum, não é muito confuso nomear o item "valor"? Você normalmente usaria sintaxe como "value.Key" e "value.Value", o que não é muito intuitivo para quem estiver lendo o código, principalmente se não estiver familiarizado com a implementação do Dicionário .Net .
RenniePet
3
@RenniePet kvpé comumente utilizado para nomear casos KeyValuePair quando iteração sobre dicionários e estruturas de dados relacionados: foreach(var kvp in myDictionary){....
Mbx #
37

Você também pode tentar isso em grandes dicionários para processamento multithread.

dictionary
.AsParallel()
.ForAll(pair => 
{ 
    // Process pair.Key and pair.Value here
});
Onur
fonte
8
@WiiMaxx e mais importante se estes itens não dependem uns dos outros
Mafii
36

O C # 7.0 introduziu desconstrutores e, se você estiver usando o .NET Core 2.0+ Application, a estruturaKeyValuePair<>já incluirá umDeconstruct()para você. Então você pode fazer:

var dic = new Dictionary<int, string>() { { 1, "One" }, { 2, "Two" }, { 3, "Three" } };
foreach (var (key, value) in dic) {
    Console.WriteLine($"Item [{key}] = {value}");
}
//Or
foreach (var (_, value) in dic) {
    Console.WriteLine($"Item [NO_ID] = {value}");
}
//Or
foreach ((int key, string value) in dic) {
    Console.WriteLine($"Item [{key}] = {value}");
}

insira a descrição da imagem aqui

Jaider
fonte
5
Se o seu usando .NET Framework, que, pelo menos até 4.7.2 não tem o Deconstruct no KeyValuePair, tente o seguinte:foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
nwsmith
29

Agradeço que esta pergunta já tenha recebido muitas respostas, mas queria lançar uma pequena pesquisa.

A iteração em um dicionário pode ser bastante lenta quando comparada à iteração em algo como uma matriz. Nos meus testes, uma iteração em uma matriz levou 0,015003 segundos, enquanto uma iteração em um dicionário (com o mesmo número de elementos) levou 0,0365073 segundos, 2,4 vezes mais! Embora eu tenha visto diferenças muito maiores. Para comparação, uma lista estava em algum lugar entre 0,00215043 segundos.

No entanto, é como comparar maçãs e laranjas. Meu argumento é que a iteração nos dicionários é lenta.

Os dicionários são otimizados para pesquisas, portanto, com isso em mente, criei dois métodos. Um simplesmente faz um foreach, o outro itera as teclas e depois olha para cima.

public static string Normal(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var kvp in dictionary)
    {
        value = kvp.Value;
        count++;
    }

    return "Normal";
}

Este carrega as chaves e itera sobre elas (tentei puxar as chaves em uma string [], mas a diferença era insignificante.

public static string Keys(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var key in dictionary.Keys)
    {
        value = dictionary[key];
        count++;
    }

    return "Keys";
}

Neste exemplo, o teste foreach normal levou 0,0310062 e a versão das chaves 0,2205441. Carregar todas as chaves e iterar em todas as pesquisas é claramente muito mais lento!

Para um teste final, eu executei minha iteração dez vezes para ver se há algum benefício em usar as chaves aqui (a essa altura eu estava curioso):

Aqui está o método RunTest, se isso ajudar você a visualizar o que está acontecendo.

private static string RunTest<T>(T dictionary, Func<T, string> function)
{            
    DateTime start = DateTime.Now;
    string name = null;
    for (int i = 0; i < 10; i++)
    {
        name = function(dictionary);
    }
    DateTime end = DateTime.Now;
    var duration = end.Subtract(start);
    return string.Format("{0} took {1} seconds", name, duration.TotalSeconds);
}

Aqui, a execução foreach normal levou 0,2820564 segundos (cerca de dez vezes mais do que uma única iteração levou - como seria de esperar). A iteração sobre as chaves levou 2,2249449 segundos.

Editado para adicionar: a leitura de algumas das outras respostas me fez questionar o que aconteceria se eu usasse o Dictionary em vez do Dictionary. Neste exemplo, a matriz levou 0,0120024 segundos, a lista 0,0185037 segundos e o dicionário 0,0465093 segundos. É razoável esperar que o tipo de dados faça a diferença na velocidade do dicionário.

Quais são as minhas conclusões ?

  • Evite iterar em um dicionário, se puder, eles são substancialmente mais lentos do que em um array com os mesmos dados.
  • Se você optar por iterar por um dicionário, não tente ser muito inteligente, embora seja mais lento que você poderia fazer muito pior do que usar o método padrão foreach.
Liath
fonte
11
Você deve medir com algo como StopWatch em vez de DateTime: hanselman.com/blog/…
Even Mien
2
você poderia por favor descreva seu cenário de teste, quantos itens onde em seu dicionário, quantas vezes você executar o seu cenário para calcular o tempo médio, ...
WiiMaxx
3
Curiosamente, você obterá resultados diferentes, dependendo dos dados que você possui no dicionário. Durante a iteração sobre o Dictionary, a função Enumerator deve pular muitos slots vazios no dicionário, o que faz com que seja mais lenta do que a iteração sobre uma matriz. Se o Dicionário estiver cheio, haverá menos espaços vazios para pular do que se estiver meio vazio.
Martin Brown
26

Há muitas opções. Meu favorito pessoal é de KeyValuePair

Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary)
{
     // Do some interesting things
}

Você também pode usar as coleções de chaves e valores

theo
fonte
14

Com .NET Framework 4.7um pode usar a decomposição

var fruits = new Dictionary<string, int>();
...
foreach (var (fruit, number) in fruits)
{
    Console.WriteLine(fruit + ": " + number);
}

Para fazer esse código funcionar em versões C # inferiores, adicione System.ValueTuple NuGet packagee escreva em algum lugar

public static class MyExtensions
{
    public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple,
        out T1 key, out T2 value)
    {
        key = tuple.Key;
        value = tuple.Value;
    }
}
Pavel
fonte
9
Isto está incorreto. O .NET 4.7 simplesmente foi ValueTupleincorporado. Está disponível como um pacote nuget para versões anteriores. Mais importante, o C # 7.0+ é necessário para que o Deconstructmétodo funcione como um desconstrutor var (fruit, number) in fruits.
David Arno
13

A partir do C # 7, você pode desconstruir objetos em variáveis. Acredito que esta seja a melhor maneira de iterar sobre um dicionário.

Exemplo:

Crie um método de extensão KeyValuePair<TKey, TVal>que o desconstrua:

public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey key, out TVal value)
{
   key = pair.Key;
   value = pair.Value;
}

Itere qualquer um Dictionary<TKey, TVal>da seguinte maneira

// Dictionary can be of any types, just using 'int' and 'string' as examples.
Dictionary<int, string> dict = new Dictionary<int, string>();

// Deconstructor gets called here.
foreach (var (key, value) in dict)
{
   Console.WriteLine($"{key} : {value}");
}
Domn Werner
fonte
10

Você sugeriu abaixo para iterar

Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary) {
    //Do some interesting things;
}

Para sua informação, foreachnão funciona se o valor for do tipo objeto.

George Stocker
fonte
4
Por favor, elabore: foreachnão funcionará se qual valor é do tipo object? Caso contrário, isso não faz muito sentido.
27717 Marc
9

Forma mais simples para iterar um dicionário:

foreach(var item in myDictionary)
{ 
    Console.WriteLine(item.Key);
    Console.WriteLine(item.Value);
}
Ron
fonte
9

Usando o C # 7 , adicione este método de extensão a qualquer projeto da sua solução:

public static class IDictionaryExtensions
{
    public static IEnumerable<(TKey, TValue)> Tuples<TKey, TValue>(
        this IDictionary<TKey, TValue> dict)
    {
        foreach (KeyValuePair<TKey, TValue> kvp in dict)
            yield return (kvp.Key, kvp.Value);
    }
}


E use esta sintaxe simples

foreach (var(id, value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


Ou este, se você preferir

foreach ((string id, object value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


No lugar do tradicional

foreach (KeyValuePair<string, object> kvp in dict)
{
    string id = kvp.Key;
    object value = kvp.Value;

    // your code using 'id' and 'value'
}


O método de extensão transforma o KeyValuePairseu IDictionary<TKey, TValue>em um tipo fortemente tipadotuple , permitindo que você use essa nova sintaxe confortável.

Ele converte - apenas - as entradas de dicionário necessárias em tuples, portanto, NÃO converte o dicionário inteiro emtuples , para que não haja problemas de desempenho relacionados a isso.

Existe apenas um custo menor chamando o método de extensão para criar uma tuplecomparação com o uso KeyValuePairdireto, o que NÃO deve ser um problema se você estiver atribuindo as KeyValuePairpropriedades Keye propriedadesValue para novas variáveis ​​de loop de qualquer maneira.

Na prática, essa nova sintaxe se adapta muito bem à maioria dos casos, exceto em cenários de baixo desempenho e alto desempenho, onde você ainda tem a opção de simplesmente não usá-la nesse local específico.

Confira: Blog do MSDN - Novos recursos no C # 7

sɐunıɔ ןɐ qɐp
fonte
1
Qual seria o motivo para preferir tuplas 'confortáveis' a pares de valores-chave? Não vejo ganho aqui. Sua tupla contém uma chave e um valor, o mesmo acontece com o par de valores-chave.
Maarten
4
Olá Maarten, obrigado pela sua pergunta. O principal benefício é a legibilidade do código sem esforço adicional de programação. Com KeyValuePair, é preciso sempre usar o formulário kvp.Keye kvp.Valueusar respectivamente a chave e o valor. Com as tuplas, você tem a flexibilidade de nomear a chave e o valor conforme desejar, sem usar mais declarações de variáveis ​​dentro do bloco foreach. Por exemplo, você pode nomear sua chave como factoryName, e o valor como models, o que é especialmente útil quando você obtém loops aninhados (dicionários de dicionários): a manutenção do código fica muito mais fácil. Apenas tente! ;-)
ןɐunıɔ ןɐ qɐp 10/09/18
7

Sei que essa é uma pergunta muito antiga, mas criei alguns métodos de extensão que podem ser úteis:

    public static void ForEach<T, U>(this Dictionary<T, U> d, Action<KeyValuePair<T, U>> a)
    {
        foreach (KeyValuePair<T, U> p in d) { a(p); }
    }

    public static void ForEach<T, U>(this Dictionary<T, U>.KeyCollection k, Action<T> a)
    {
        foreach (T t in k) { a(t); }
    }

    public static void ForEach<T, U>(this Dictionary<T, U>.ValueCollection v, Action<U> a)
    {
        foreach (U u in v) { a(u); }
    }

Dessa forma, eu posso escrever código como este:

myDictionary.ForEach(pair => Console.Write($"key: {pair.Key}, value: {pair.Value}"));
myDictionary.Keys.ForEach(key => Console.Write(key););
myDictionary.Values.ForEach(value => Console.Write(value););
boca
fonte
6

Às vezes, se você só precisa que os valores sejam enumerados, use a coleção de valores do dicionário:

foreach(var value in dictionary.Values)
{
    // do something with entry.Value only
}

Relatado por este post que afirma que é o método mais rápido: http://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html

ender
fonte
+1 para trazer desempenho. De fato, a iteração no próprio dicionário inclui algumas sobrecargas, se tudo o que você precisa são valores. Isso se deve principalmente à cópia de valores ou referências nas estruturas KeyValuePair.
precisa saber é o seguinte
1
Para sua informação, o último teste nesse link é testar a coisa errada: mede a iteração sobre as chaves, desconsiderando valores, enquanto os dois testes anteriores usam o valor.
Crescent Fresh
5

Encontrei esse método na documentação da classe DictionaryBase no MSDN:

foreach (DictionaryEntry de in myDictionary)
{
     //Do some stuff with de.Value or de.Key
}

Esse foi o único que eu consegui funcionar corretamente em uma classe herdada do DictionaryBase.


fonte
3
Isso parece ao usar a versão não genérica do Dictionary ... ou seja, anterior ao .NET framework 2.0.
precisa saber é o seguinte
@ joed0tnot: é a versão não-genéricos usados para Hashtableobjetos
sɐunıɔ ןɐ qɐp
5

foreaché mais rápido e se você iterar apenas ___.Values, também é mais rápido

insira a descrição da imagem aqui

Pixel_95
fonte
Por que você está ligando ContainsKey()na forversão? Isso adiciona uma sobrecarga extra que não está presente no código com o qual você está comparando. TryGetValue()existe para substituir o padrão exato "se houver chave, obtenha item com chave". Além disso, se dictcontiver um intervalo contíguo de números inteiros de 0até dictCount - 1, você sabe que o indexador não pode falhar; caso contrário, dict.Keysé o que você deve estar iterando. De qualquer maneira, não ContainsKey()/ TryGetValue()necessário. Por fim, não poste capturas de tela do código.
BACON
3

Vou aproveitar o .NET 4.0+ e fornecer uma resposta atualizada para a originalmente aceita:

foreach(var entry in MyDic)
{
    // do something with entry.Value or entry.Key
}
yazanpro
fonte
3

A maneira padrão de iterar em um dicionário, de acordo com a documentação oficial no MSDN é:

foreach (DictionaryEntry entry in myDictionary)
{
     //Read entry.Key and entry.Value here
}
usuario
fonte
2

Eu escrevi uma extensão para fazer um loop sobre um dicionário.

public static class DictionaryExtension
{
    public static void ForEach<T1, T2>(this Dictionary<T1, T2> dictionary, Action<T1, T2> action) {
        foreach(KeyValuePair<T1, T2> keyValue in dictionary) {
            action(keyValue.Key, keyValue.Value);
        }
    }
}

Então você pode ligar

myDictionary.ForEach((x,y) => Console.WriteLine(x + " - " + y));
Steven Delrue
fonte
Você definiu um ForEachmétodo no qual você tem foreach (...) { }... Parece desnecessário.
Maarten
1

Por exemplo, você deseja iterar sobre a coleção de valores por padrão, acredito que você possa implementar IEnumerable <>, onde T é o tipo do objeto de valores no dicionário e "this" é um dicionário.

public new IEnumerator<T> GetEnumerator()
{
   return this.Values.GetEnumerator();
}

fonte
1

Só queria adicionar meus 2 centavos, pois a maioria das respostas está relacionada ao loop foreach. Por favor, dê uma olhada no seguinte código:

Dictionary<String, Double> myProductPrices = new Dictionary<String, Double>();

//Add some entries to the dictionary

myProductPrices.ToList().ForEach(kvP => 
{
    kvP.Value *= 1.15;
    Console.Writeline(String.Format("Product '{0}' has a new price: {1} $", kvp.Key, kvP.Value));
});

Embora isso adicione uma chamada adicional de '.ToList ()', pode haver uma ligeira melhoria no desempenho (conforme indicado aqui para foreach vs someList.Foreach () {} ), espacialmente ao trabalhar com dicionários grandes e executar em paralelo. opção / não terá efeito algum.

Além disso, observe que você não poderá atribuir valores à propriedade 'Value' dentro de um loop foreach. Por outro lado, você também poderá manipular a 'Chave', possivelmente causando problemas em tempo de execução.

Quando você quiser apenas "ler" Chaves e Valores, também poderá usar IEnumerable.Select ().

var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );
Alex
fonte
5
Copiar a coleção inteira sem motivo algum não melhorará o desempenho. Isso reduzirá drasticamente o código, além de duplicar a área de cobertura da memória que deve consumir praticamente nenhuma memória adicional.
Servy
Evito o efeito colateral 'List.ForEach' método: foreach força a visibilidade do efeito colateral para cima, onde ele pertence.
user2864740
1

Como já apontado nesta resposta , KeyValuePair<TKey, TValue>implementa umDeconstruct método que começa no .NET Core 2.0, .NET Standard 2.1 e .NET Framework 5.0 (visualização).

Com isso, é possível iterar através de um dicionário de uma KeyValuePairmaneira agnóstica:

var dictionary = new Dictionary<int, string>();

// ...

foreach (var (key, value) in dictionary)
{
    // ...
}
rucamzu
fonte
0
var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};

var aggregateObjectCollection = dictionary.Select(
    entry => new AggregateObject(entry.Key, entry.Value));
Pixar
fonte
2
Precisa haver mais justificativa / descrição nesta resposta. O que AggregateObjectadiciona a KeyValuePair? Onde está a "iteração", conforme solicitado na pergunta?
27717 Marc
Selecione itera sobre o dicionário e nos permite trabalhar em cada objeto. Não é tão geral quanto foreach, mas eu usei muito. Minha resposta realmente merece voto negativo?
Pixar
Não, Select usa a iteração para efetuar o resultado, mas não é um iterador em si. Os tipos de itens para os quais a iteração ( foreach) é usada - especialmente operações com efeitos colaterais - estão fora do escopo do Linq, inclusive Select. O lambda não será executado até que aggregateObjectCollectionseja realmente enumerado. Se essa resposta for tomada como um "primeiro caminho" (isto é, usado antes de uma reta foreach), incentivará as práticas inadequadas. Situacionalmente, pode haver operações do Linq que sejam úteis antes da iteração de um dicionário, mas que não atendam à pergunta conforme solicitado.
Marc L.
0

Dictionary <TKey, TValue> É uma classe de coleção genérica em c # e armazena os dados no formato do valor da chave. A chave deve ser única e não pode ser nula, enquanto o valor pode ser duplicado e nulo. Como cada item do dicionário é tratado como KeyValuePair <TKey, TValue> estrutura que representa uma chave e seu valor. e, portanto, devemos usar o tipo de elemento KeyValuePair <TKey, TValue> durante a iteração do elemento. Abaixo está o exemplo.

Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1,"One");
dict.Add(2,"Two");
dict.Add(3,"Three");

foreach (KeyValuePair<int, string> item in dict)
{
    Console.WriteLine("Key: {0}, Value: {1}", item.Key, item.Value);
}
Sheo Dayal Singh
fonte
0

Se você deseja usar o loop for, você pode fazer isso:

var keyList=new List<string>(dictionary.Keys);
for (int i = 0; i < keyList.Count; i++)
{
   var key= keyList[i];
   var value = dictionary[key];
 }
Seçkin Durgay
fonte
Qual é o benefício disso, no entanto? É um código mais longo do que um foreachloop e pior desempenho, porque new List<string>(dictionary.Keys)iterará dictionary.Countvezes antes que você tenha a chance de iterá-lo. Deixando de lado que pedir "o melhor caminho" é subjetivo, não vejo como isso se qualificaria como "o melhor caminho" ou "caminho padrão" que a pergunta busca. Para "Se você deseja usar o loop ...", eu diria " Não use um forloop".
BACON
Se você tem uma coleção grande e as operações são lentas no foreach e se sua coleção pode ser alterada quando você itera, ela o protege do erro "coleção alterada" e esta solução tem melhor desempenho do que o ElementAt.
Seçkin Durgay
Concordo que evitar exceções "A coleção foi modificada" é uma das razões para fazer isso, embora esse caso especial não tenha sido declarado na pergunta e sempre possa ser feito foreach (var pair in dictionary.ToArray()) { }. Ainda assim, acho que seria bom deixar claro na resposta o (s) cenário (s) específico (s) em que alguém gostaria de usar esse código e as implicações de fazê-lo.
BACON
sim, você está certo, mas há uma resposta aqui com ElementAt e ele tem uma reputação muito alta e eu
digitei
-1

simples com linq

 Dictionary<int, string> dict = new Dictionary<int, string>();
 dict.Add(1, "American Ipa");
 dict.Add(2, "Weiss");
 dict.ToList().ForEach(x => Console.WriteLine($"key: {x.Key} | value: {x.Value}") );
Luiz Fernando Corrêa Leite
fonte
Vejo que você está ligando ToList()porque ForEach()é definido apenas na List<>classe, mas por que tudo isso em vez de apenas foreach (var pair in dict) { }? Eu diria que é ainda mais simples e não tem as mesmas implicações de memória / desempenho. Esta solução exata já foi proposta nesta resposta de 3,5 anos atrás, de qualquer maneira.
BACON 30/04
Apenas para visualização do código, poderia realmente ser feito da maneira que você indicou, eu só queria apresentá-lo de uma maneira diferente e, na minha visão mais enxuta, peço desculpas por não ter visto a resposta indicada, um abraço
Luiz Fernando Corrêa Leite
-3

além das postagens mais altas, onde há uma discussão entre usar

foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

ou

foreach(var entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

o mais completo é o seguinte, porque você pode ver o tipo de dicionário na inicialização, kvp é KeyValuePair

var myDictionary = new Dictionary<string, string>(x);//fill dictionary with x

foreach(var kvp in myDictionary)//iterate over dictionary
{
    // do something with kvp.Value or kvp.Key
}
Grande chefe
fonte
5
Criar e copiar um segundo dicionário não é uma solução válida para a legibilidade do código. Na verdade, eu argumentaria que isso tornaria o código mais difícil de entender, porque agora você precisa se perguntar: "Por que o último cara criou um segundo dicionário?" Se você quiser ser mais detalhado, use a opção um.
Matthew Goulart
Eu só queria dizer para mostrar-lhe que, quando decl dict antes para cada um, o uso na foreach resulta da declaração
BigChief