Eu já vi algumas maneiras diferentes de percorrer um dicionário em c #. Existe uma maneira padrão?
Eu já vi algumas maneiras diferentes de percorrer um dicionário em c #. Existe uma maneira padrão?
Respostas:
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
var entry
é melhor nesse caso e, portanto, votei esta resposta em uma segunda olhada em vez da acima.
var
quando você não sabe o tipo geralmente é uma prática ruim.
var
só 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.
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 var
palavra-chave é um recurso opcional do C # 3.0 e acima; você também pode usar o tipo exato de suas chaves / valores aqui)
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
var
deve ser usado com moderação, na minha opinião. Particularmente aqui, não é construtivo: o tipo KeyValuePair
é provavelmente relevante para a questão.
var
tem um propósito único e não acredito que seja um açúcar "sintático". Usá-lo propositadamente é uma abordagem apropriada.
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 ElementAt
que permite o seguinte:
for (int index = 0; index < dictionary.Count; index++) {
var item = dictionary.ElementAt(index);
var itemKey = item.Key;
var itemValue = item.Value;
}
ElementAt
uma operação O (n)?
.ElementAt
nesse contexto pode levar a erros sutis. Muito mais sério é o argumento de Arturo acima. Você repetirá os dictionary.Count + 1
tempos 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 .ElementAt
dentro do loop.
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);
}
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.
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)
}
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:
item
, mais legível que kvp.Value
)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)!
.Values
e não uma cláusula de seleção.
Value
campo. 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)?
items.Value
como você sugeriu. No caso da quarta seção que você comentou, Select()
é uma maneira de fazer foreach
enumerar 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.
.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".
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?
kvp
é comumente utilizado para nomear casos KeyValuePair quando iteração sobre dicionários e estruturas de dados relacionados: foreach(var kvp in myDictionary){...
.
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}");
}
foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
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 ?
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
Com .NET Framework 4.7
um 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 package
e 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;
}
}
ValueTuple
incorporado. Está disponível como um pacote nuget para versões anteriores. Mais importante, o C # 7.0+ é necessário para que o Deconstruct
método funcione como um desconstrutor var (fruit, number) in fruits
.
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}");
}
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, foreach
não funciona se o valor for do tipo objeto.
foreach
não funcionará se qual valor é do tipo object
? Caso contrário, isso não faz muito sentido.
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 KeyValuePair
seu 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 tuple
comparação com o uso KeyValuePair
direto, o que NÃO deve ser um problema se você estiver atribuindo as KeyValuePair
propriedades Key
e 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.
kvp.Key
e kvp.Value
usar 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! ;-)
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););
À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
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.
Hashtable
objetos
ContainsKey()
na for
versã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 dict
contiver um intervalo contíguo de números inteiros de 0
até 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.
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));
ForEach
método no qual você tem foreach (...) { }
... Parece desnecessário.
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();
}
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 } );
foreach
força a visibilidade do efeito colateral para cima, onde ele pertence.
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 KeyValuePair
maneira agnóstica:
var dictionary = new Dictionary<int, string>();
// ...
foreach (var (key, value) in dictionary)
{
// ...
}
var dictionary = new Dictionary<string, int>
{
{ "Key", 12 }
};
var aggregateObjectCollection = dictionary.Select(
entry => new AggregateObject(entry.Key, entry.Value));
AggregateObject
adiciona a KeyValuePair
? Onde está a "iteração", conforme solicitado na pergunta?
foreach
, mas eu usei muito. Minha resposta realmente merece voto negativo?
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 aggregateObjectCollection
seja 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.
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);
}
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];
}
foreach
loop e pior desempenho, porque new List<string>(dictionary.Keys)
iterará dictionary.Count
vezes 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 for
loop".
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.
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}") );
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.
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
}