C# Dictionary<TKey, TValue>
Numa List, você encontra as coisas pela posição: o item 0, o item 1, o item 2. Mas muitas vezes você quer encontrar pelo nome: o preço do café, a nota da Ana, o estoque de bananas. Para isso existe o dicionário: Dictionary<TKey, TValue>. Ele liga cada chave a um valor e acha qualquer chave num piscar de olhos, mesmo com um milhão de itens.
- O que são chave e valor, e como criar um
Dictionary. - Guardar e ler com o indexador
[ ], e a diferença para oAdd. - O que acontece quando a chave não existe: a
KeyNotFoundException. - Perguntar antes:
ContainsKey,TryGetValue,GetValueOrDefault,TryAddeRemove. - Percorrer com
foreach,KeyValuePair,KeyseValues. - Contar quantas vezes cada palavra aparece.
- Por que você não deve contar com a ordem e quando usar o
SortedDictionary. - A
InvalidOperationExceptionde mudar o dicionário dentro doforeach.
Todos os programas desta aula rodaram com o .NET 10 na máquina da Ana (usuária ana, computador notebook), na pasta ~/cs, com dotnet run arquivo.cs (veja Rodar com dotnet run). Você pode repetir cada um no editor do guia.
A ideia: um dicionário de papel
Num dicionário de papel, você não procura "a palavra número 3.512": você procura pela palavra e encontra o significado. A palavra é a chave (key); o significado é o valor (value). A tabela de preços da lojinha da Ana é igual:
chave valor "pão" → 0,75 "leite" → 5,49 "café" → 18,90
Duas regras definem um dicionário:
- Cada chave aparece uma vez só. Não existem dois "pão". Guardar "pão" de novo troca o valor que estava lá.
- Os valores podem se repetir. O pão e a banana podem custar o mesmo.
No nome Dictionary<TKey, TValue>, os dois tipos entre < > são escolhidos por você: TKey é o tipo da chave e TValue, o do valor. Um Dictionary<string, decimal> é "um dicionário de texto para dinheiro". Essa escolha de tipos entre < > se chama generics e tem aula própria (Generics). O Dictionary mora no namespace System.Collections.Generic, que o .NET já importa sozinho (o using implícito; veja Namespaces e using).
1. Criar, guardar e ler: o indexador [ ]
var precos = new Dictionary<string, decimal>();
precos["pão"] = 0.75m;
precos["leite"] = 5.49m;
precos["café"] = 18.90m;
Console.WriteLine($"Produtos: {precos.Count}");
Console.WriteLine($"Leite: {precos["leite"]:C}");
precos["leite"] = 5.99m;
Console.WriteLine($"Leite agora: {precos["leite"]:C}");
Console.WriteLine($"Produtos: {precos.Count}");
Console.WriteLine(precos);
- new Dictionary<string, decimal>()
- Cria um dicionário vazio. A chave é
string(o nome do produto) e o valor édecimal(o preço; dinheiro é sempredecimal, veja float, double e decimal). - precos["pão"] = 0.75m;
- O indexador: os colchetes com a chave dentro, em vez de um número de posição. Leia "o preço do pão recebe 0,75". Se a chave não existe, ela é criada; se já existe, o valor é trocado.
- precos.Count
- Count = contagem. Quantos pares chave-valor existem.
- precos["leite"]
- Sem o
=depois, o indexador lê: devolve o valor ligado à chave"leite". - :C
- Formata como moeda (
R$), como em Formatar Números.
$ dotnet run precos.cs Produtos: 3 Leite: R$ 5,49 Leite agora: R$ 5,99 « a mesma chave: o valor foi trocado Produtos: 3 « continua com 3, não criou outro leite System.Collections.Generic.Dictionary`2[System.String,System.Decimal]
Repare na última linha. Diferente de outras linguagens, o C# não mostra o conteúdo do dicionário quando você o passa para o Console.WriteLine: ele mostra só o nome do tipo (o `2 quer dizer "com 2 tipos entre < >"). Para ver o que tem dentro, você percorre o dicionário com foreach, como na seção 4.
O Add: guardar só se a chave for nova
Também dá para guardar com o método Add ("acrescentar"). A diferença aparece quando a chave já existe: o indexador troca o valor em silêncio, mas o Add recusa e lança uma exceção.
var precos = new Dictionary<string, decimal>();
precos.Add("pão", 0.75m);
precos.Add("leite", 5.49m);
Console.WriteLine($"Produtos: {precos.Count}");
precos.Add("pão", 0.80m);
Console.WriteLine("Esta linha não roda.");
$ dotnet run add.cs Produtos: 2 Unhandled exception. System.ArgumentException: An item with the same key has already been added. Key: pão at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior) at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value) at Program.<Main>$(String[] args) in /home/ana/cs/add.cs:line 7 $ echo $? 134
A mensagem, em português: "um item com a mesma chave já foi adicionado. Chave: pão". A última linha do rastro diz onde: add.cs:line 7. Isso não é um defeito do Add, é o trabalho dele: quando uma chave repetida é um erro nos seus dados (dois alunos com a mesma matrícula, por exemplo), o Add avisa na hora em vez de apagar o primeiro sem você perceber.
| Escrita | Chave nova | Chave que já existe |
|---|---|---|
d[chave] = valor; | Cria o par | Troca o valor (sem aviso) |
d.Add(chave, valor); | Cria o par | Lança ArgumentException |
d.TryAdd(chave, valor) | Cria o par e devolve true | Não mexe e devolve false (seção 3) |
Criar já com itens
Em vez de uma linha por item, você pode encher o dicionário na criação, com um inicializador entre chaves. Há duas formas, e as duas dão o mesmo dicionário:
var estoque = new Dictionary<string, int>
{
["maçã"] = 12,
["banana"] = 30,
};
var notas = new Dictionary<string, double>
{
{ "Ana", 9.5 },
{ "Bruno", 7.0 },
};
- ["maçã"] = 12,
- Usa o indexador para cada item. Se a mesma chave aparecer duas vezes, vale a última.
- { "Ana", 9.5 },
- Usa o
Addpara cada item. Uma chave repetida dá aArgumentExceptionque você acabou de ver. - a vírgula depois do último item
- É permitida e comum: facilita acrescentar uma linha depois.
[ ] das listas?
Uma lista pode ser criada com a expressão de coleção List<int> n = [1, 2, 3]; C# 12. Para dicionários isso ainda não existe no C# 14: Dictionary<string, int> d = ["a": 1]; dá error CS1003: Erro de sintaxe, "]" esperado. Use um dos dois inicializadores acima.
Missão: o café subiu. Logo depois do Console.WriteLine do preço antigo, troque o preço do café para 19.50m usando o indexador, e mostre a linha Café: R$ 19,50. O número de produtos tem de continuar 3.
var precos = new Dictionary<string, decimal>
{
["pão"] = 0.75m,
["leite"] = 5.49m,
["café"] = 18.90m,
};
Console.WriteLine($"Café: {precos["café"]:C}");
// troque o preço do café aqui e mostre de novo
Console.WriteLine($"Produtos: {precos.Count}");
var precos = new Dictionary<string, decimal>
{
["pão"] = 0.75m,
["leite"] = 5.49m,
["café"] = 18.90m,
};
Console.WriteLine($"Café: {precos["café"]:C}");
precos["café"] = 19.50m;
Console.WriteLine($"Café: {precos["café"]:C}");
Console.WriteLine($"Produtos: {precos.Count}");
2. A chave que não existe: KeyNotFoundException
Ler com o indexador é direto, mas tem um preço: se a chave não existe, o programa para com uma exceção.
var precos = new Dictionary<string, decimal>();
precos["pão"] = 0.75m;
precos["leite"] = 5.49m;
Console.WriteLine($"Queijo: {precos["queijo"]:C}");
$ dotnet run queijo.cs Unhandled exception. System.Collections.Generic.KeyNotFoundException: The given key 'queijo' was not present in the dictionary. at System.Collections.Generic.Dictionary`2.get_Item(TKey key) at Program.<Main>$(String[] args) in /home/ana/cs/queijo.cs:line 5 $ echo $? 134
- KeyNotFoundException
- "Exceção de chave não encontrada". O nome já diz tudo.
- The given key 'queijo' was not present in the dictionary.
- "A chave 'queijo' não estava presente no dicionário." O .NET mostra qual chave você pediu: confira se ela foi guardada e se está escrita igual (maiúsculas, acentos, espaços).
- get_Item(TKey key)
- É o nome interno do indexador quando ele lê (get = pegar).
- queijo.cs:line 5
- A linha do seu programa que pediu a chave.
Como toda exceção não tratada, ela termina o programa com código de saída 134 (veja Exceções). Você poderia cercar a leitura com try e catch, mas existe um jeito melhor e mais barato: perguntar antes.
3. Perguntar antes: ContainsKey, TryGetValue e companhia
var estoque = new Dictionary<string, int>
{
["maçã"] = 12,
["banana"] = 30,
["uva"] = 0,
};
Console.WriteLine(estoque.ContainsKey("uva"));
Console.WriteLine(estoque.ContainsKey("kiwi"));
if (estoque.TryGetValue("banana", out int bananas))
Console.WriteLine($"Tem {bananas} bananas.");
if (!estoque.TryGetValue("kiwi", out int kiwis))
Console.WriteLine($"Não tem kiwi (kiwis = {kiwis}).");
Console.WriteLine(estoque.GetValueOrDefault("kiwi"));
Console.WriteLine(estoque.GetValueOrDefault("kiwi", -1));
Console.WriteLine(estoque.TryAdd("maçã", 99));
Console.WriteLine(estoque.TryAdd("pera", 7));
Console.WriteLine(estoque.Remove("uva"));
Console.WriteLine(estoque.Remove("uva"));
Console.WriteLine($"Itens: {estoque.Count}");
$ dotnet run perguntar.cs True « a uva existe, mesmo com estoque 0 False Tem 30 bananas. Não tem kiwi (kiwis = 0). « quando falha, o out recebe o valor padrão (0 para int) 0 -1 « o padrão que você escolheu False « TryAdd: a maçã já existia, continua 12 True « a pera entrou com 7 True « Remove: tirou a uva False « a uva já não estava lá: nada a tirar, e sem exceção Itens: 3
| Linha | Tradução | O que faz |
|---|---|---|
ContainsKey(k) | "contém a chave" | true se a chave existe. Muito rápido. |
TryGetValue(k, out int v) | "tentar pegar o valor" | Se a chave existe, põe o valor em v e devolve true. Se não existe, põe o valor padrão do tipo (0, null, false) e devolve false. Nunca lança exceção. O out está em ref, out e in. |
GetValueOrDefault(k) | "pegar o valor ou o padrão" | O valor da chave, ou o padrão do tipo se ela não existe. |
GetValueOrDefault(k, p) | O mesmo, mas quem escolhe o padrão é você (p). | |
TryAdd(k, v) | "tentar acrescentar" | Só guarda se a chave é nova. Devolve true se guardou. |
Remove(k) | "remover" | Tira o par. Devolve true se a chave existia e false se não existia. |
ContainsValue(v) | "contém o valor" | true se algum par tem esse valor. Lento: olha todos os pares, um por um. |
Clear() | "limpar" | Tira todos os pares. |
ContainsKey + indexador, ou TryGetValue?
Este código funciona, mas procura a chave duas vezes:
if (estoque.ContainsKey("banana"))
Console.WriteLine(estoque["banana"]);
O TryGetValue pergunta e pega numa busca só. Quando você vai usar o valor, prefira ele. O ContainsKey fica para quando você só quer saber "existe ou não".
Missão: rode e veja a KeyNotFoundException do kiwi. Depois troque as duas leituras com [ ] por GetValueOrDefault (ou por TryGetValue), para a saída ser maçã: 12 e kiwi: 0, sem exceção.
var estoque = new Dictionary<string, int>
{
["maçã"] = 12,
["banana"] = 30,
};
Console.WriteLine($"maçã: {estoque["maçã"]}");
Console.WriteLine($"kiwi: {estoque["kiwi"]}");
var estoque = new Dictionary<string, int>
{
["maçã"] = 12,
["banana"] = 30,
};
Console.WriteLine($"maçã: {estoque.GetValueOrDefault("maçã")}");
Console.WriteLine($"kiwi: {estoque.GetValueOrDefault("kiwi")}");
4. Percorrer: foreach, KeyValuePair, Keys e Values
O foreach (aula foreach) passa por todos os pares do dicionário. Cada par é um KeyValuePair<TKey, TValue> ("par chave-valor"), com duas propriedades: Key (a chave) e Value (o valor).
var notas = new Dictionary<string, double>
{
["Ana"] = 9.5,
["Bruno"] = 7.0,
["Carla"] = 8.25,
};
foreach (KeyValuePair<string, double> par in notas)
{
Console.WriteLine($"{par.Key} tirou {par.Value}");
}
foreach (var (nome, nota) in notas)
{
Console.WriteLine($"{nome}: {nota}");
}
Console.WriteLine(string.Join(", ", notas.Keys));
Console.WriteLine(notas.Values.Sum() / notas.Count);
foreach (var par in notas)
{
Console.WriteLine(par);
}
$ dotnet run percorrer.cs Ana tirou 9,5 Bruno tirou 7 Carla tirou 8,25 Ana: 9,5 Bruno: 7 Carla: 8,25 Ana, Bruno, Carla 8,25 « a média: (9,5 + 7 + 8,25) / 3 [Ana, 9,5] « um KeyValuePair se mostra assim: [chave, valor] [Bruno, 7] [Carla, 8,25]
- foreach (KeyValuePair<string, double> par in notas)
- "Para cada par em notas". O tipo completo é comprido; na prática quase todo mundo escreve
var par. - par.Key / par.Value
- A chave e o valor do par atual.
- foreach (var (nome, nota) in notas)
- A forma moderna: o par é desmontado em duas variáveis com nomes que você escolhe (a desconstrução, a mesma das Tuplas). Fica mais fácil de ler que
par.Key. - notas.Keys
- Só as chaves. Aqui o
string.Joinas junta com", ". - notas.Values.Sum()
- Só os valores, somados pelo
Sumdo LINQ. - Console.WriteLine(par)
- Mostra o par entre colchetes. Cuidado com a cultura pt-BR: em
[Ana, 9,5], a primeira vírgula separa chave e valor, e a segunda é a vírgula decimal do 9,5.
Quando precisar da chave e do valor, percorra os pares (como nos dois primeiros laços). Fazer foreach (var nome in notas.Keys) e ler notas[nome] lá dentro funciona, mas busca cada chave de novo, sem necessidade.
5. Contar coisas: a frequência das palavras
O uso mais clássico de um dicionário: contar quantas vezes cada coisa aparece. A chave é a coisa (a palavra); o valor é quantas vezes ela apareceu.
string frase = "o rato roeu a roupa do rei de roma e o rato fugiu";
var contagem = new Dictionary<string, int>();
foreach (string palavra in frase.Split(' '))
{
if (contagem.TryGetValue(palavra, out int vezes))
contagem[palavra] = vezes + 1;
else
contagem[palavra] = 1;
}
Console.WriteLine($"Palavras diferentes: {contagem.Count}");
Console.WriteLine($"rato: {contagem["rato"]}");
Console.WriteLine($"o: {contagem["o"]}");
Console.WriteLine($"rei: {contagem["rei"]}");
$ dotnet run contar.cs Palavras diferentes: 11 rato: 2 o: 2 rei: 1
- frase.Split(' ')
- Corta a frase em cada espaço e devolve um array com as 13 palavras (veja Métodos de String).
- if (contagem.TryGetValue(palavra, out int vezes))
- "Essa palavra já foi contada? Se sim, quantas vezes?"
- contagem[palavra] = vezes + 1;
- Já existia: guarda a contagem antiga mais 1.
- contagem[palavra] = 1;
- É a primeira vez: começa com 1.
São 13 palavras, mas só 11 diferentes, porque "o" e "rato" aparecem duas vezes. O mesmo laço fica numa linha só com o GetValueOrDefault: "pegue a contagem atual, ou 0 se é a primeira vez, e some 1":
foreach (string palavra in frase.Split(' '))
contagem[palavra] = contagem.GetValueOrDefault(palavra) + 1;
6. A ordem não é garantida
Por dentro, o Dictionary não guarda os pares numa fila. Ele calcula um número a partir de cada chave (o hash code, uma "impressão digital" numérica; veja A Classe object) e usa esse número para achar a chave depois sem olhar as outras. É por isso que ele é tão rápido.
Num dicionário em que você só acrescenta, o foreach do .NET costuma devolver os pares na ordem em que entraram, como você viu nas notas. Mas isso é um detalhe de como ele foi feito, não uma promessa. Basta uma remoção para a ordem mudar:
var frutas = new Dictionary<string, int>();
frutas["uva"] = 1;
frutas["banana"] = 2;
frutas["maçã"] = 3;
frutas["kiwi"] = 4;
Console.WriteLine(string.Join(", ", frutas.Keys));
frutas.Remove("banana");
frutas["pera"] = 5;
Console.WriteLine(string.Join(", ", frutas.Keys));
var ordenado = new SortedDictionary<string, int>(frutas);
Console.WriteLine(string.Join(", ", ordenado.Keys));
$ dotnet run ordem.cs uva, banana, maçã, kiwi uva, pera, maçã, kiwi « a pera entrou por último, mas ocupou o lugar vago da banana kiwi, maçã, pera, uva « SortedDictionary: chaves em ordem alfabética
A pera foi a última a entrar, mas apareceu em segundo: ela reaproveitou o espaço que a banana deixou. Se o seu programa só "funciona" porque o dicionário por acaso mostrou na ordem certa, ele pode quebrar com a primeira remoção.
Dictionary
Quando a ordem importa, diga isso no código:
7. Mudar o dicionário dentro do foreach
Enquanto o foreach anda pelo dicionário, você não pode acrescentar chaves nele. A Ana quis marcar as frutas que acabaram criando uma chave nova "… (encomendar)" no meio do laço:
var estoque = new Dictionary<string, int>
{
["maçã"] = 12,
["banana"] = 0,
["uva"] = 5,
};
foreach (var (fruta, qtd) in estoque)
{
if (qtd == 0)
estoque.Add($"{fruta} (encomendar)", 50);
}
$ dotnet run modificar.cs Unhandled exception. System.InvalidOperationException: Collection was modified; enumeration operation may not execute. at System.Collections.Generic.Dictionary`2.Enumerator.MoveNext() at Program.<Main>$(String[] args) in /home/ana/cs/modificar.cs:line 8 $ echo $? 134
Em português: "a coleção foi modificada; a operação de enumeração (o percurso) não pode continuar". O Add em si funcionou; quem reclamou foi o foreach, quando tentou ir para o próximo par (MoveNext, "mover para o próximo"). Por isso o rastro aponta a linha 8, a do foreach, e não a do Add. Acrescentar uma chave pode reorganizar o dicionário por dentro, e o percurso perderia o fio.
O conserto é fazer em dois passos: primeiro anote numa lista o que precisa mudar; depois, fora do laço, mude.
var estoque = new Dictionary<string, int>
{
["maçã"] = 12,
["banana"] = 0,
["uva"] = 5,
["kiwi"] = 0,
};
var acabaram = new List<string>();
foreach (var (fruta, qtd) in estoque)
{
if (qtd == 0)
acabaram.Add(fruta);
}
foreach (string fruta in acabaram)
{
estoque.Remove(fruta);
estoque[$"{fruta} (encomendar)"] = 50;
}
Console.WriteLine(string.Join(", ", estoque.Keys.Order()));
$ dotnet run modificar2.cs banana (encomendar), kiwi (encomendar), maçã, uva
O segundo foreach anda pela lista acabaram, não pelo dicionário, então mexer no dicionário ali é seguro. Como houve remoções, o programa ordena as chaves com Order() antes de mostrar (lembre da seção 6).
foreach?
Desde o .NET Core 3.0, o Dictionary deixa você remover a chave atual ou trocar o valor de uma chave que já existe durante o foreach, sem exceção. Só acrescentar chaves é proibido. Mesmo assim, o jeito em dois passos funciona em qualquer coleção (a List, por exemplo, reclama também de remoções) e deixa claro o que o código faz.
8. Maiúsculas e minúsculas na chave
Para o dicionário, "Ana" e "ana" são chaves diferentes. Se você quer que ele ignore a caixa das letras, passe um comparador na criação:
var senhas = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
senhas["Ana"] = "girassol";
Console.WriteLine(senhas.ContainsKey("ana"));
Console.WriteLine(senhas["ANA"]);
var normal = new Dictionary<string, string>();
normal["Ana"] = "girassol";
Console.WriteLine(normal.ContainsKey("ana"));
$ dotnet run caixa.cs True girassol False « sem o comparador, "ana" não é "Ana"
StringComparer.OrdinalIgnoreCase quer dizer "comparar letra por letra, ignorando maiúsculas e minúsculas". É útil para nomes de usuário, comandos digitados e extensões de arquivo.
Qual coleção usar?
| Você quer… | Use |
|---|---|
| Itens em fila, acessados pela posição (0, 1, 2…) | List<T> |
| Achar um valor pela chave, rápido; a ordem não importa | Dictionary<TKey, TValue> |
| Achar pela chave e percorrer em ordem de chave | SortedDictionary<TKey, TValue> |
| Só saber se um item está ou não está, sem valor ligado | HashSet<T> |
Quando dá errado
KeyNotFoundException: The given key 'queijo' was not present in the dictionary.
O que significa: você leu d[chave] com uma chave que não está no dicionário. O programa para com código 134.
Como consertar: confira a grafia da chave (a mensagem mostra exatamente o que foi pedido). Se a chave pode faltar, leia com TryGetValue ou GetValueOrDefault, como na seção 3.
ArgumentException: An item with the same key has already been added. Key: pão
O que significa: um Add (ou um inicializador { "pão", … }) tentou guardar uma chave que já existe.
Como consertar: se trocar o valor é o que você quer, use o indexador: d["pão"] = 0.80m;. Se a chave nova não deve apagar a antiga, use TryAdd e veja o bool que ele devolve. Se a repetição é um erro nos dados, a exceção está certa: conserte os dados.
InvalidOperationException: Collection was modified; enumeration operation may not execute.
O que significa: você acrescentou uma chave enquanto um foreach percorria o mesmo dicionário. O rastro aponta a linha do foreach.
Como consertar: faça em dois passos: anote numa List o que mudar e mude depois do laço (seção 7).
error CS1503: Argumento 1: não é possível converter de "int" para "string"
O que significa: a chave que você passou não é do tipo TKey. Com Dictionary<string, int> estoque, a linha estoque[1] tenta usar um número como chave, como se fosse uma lista:
$ dotnet run tipo.cs /home/ana/cs/tipo.cs(2,27): error CS1503: Argumento 1: não é possível converter de "int" para "string" Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
Como consertar: um dicionário não tem posições, só chaves. Use a chave (estoque["maçã"]). Se você precisa "do primeiro item", talvez a coleção certa seja uma List.
Referência rápida
| Escrita | O que faz |
|---|---|
new Dictionary<TKey, TValue>() | Cria um dicionário vazio |
new Dictionary<string, int> { ["a"] = 1 } | Cria já com itens (indexador) |
new Dictionary<string, int> { { "a", 1 } } | Cria já com itens (Add) |
d[k] = v; | Cria ou troca o valor de k |
d[k] | Lê o valor de k; KeyNotFoundException se não existe |
d.Add(k, v) | Cria; ArgumentException se k já existe |
d.TryAdd(k, v) | Cria só se k é nova; devolve bool |
d.TryGetValue(k, out var v) | Busca sem exceção; devolve bool |
d.GetValueOrDefault(k[, padrão]) | O valor ou um padrão |
d.ContainsKey(k) / d.ContainsValue(v) | Existe a chave? (rápido) / Existe o valor? (lento) |
d.Remove(k) | Tira o par; devolve bool |
d.Count / d.Clear() | Quantos pares / tira todos |
d.Keys / d.Values | Só as chaves / só os valores |
foreach (var (k, v) in d) | Percorre os pares, já desmontados |
new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase) | Chaves de texto sem diferenciar maiúsculas |
SortedDictionary<TKey, TValue> | Mantém as chaves em ordem |
Exercícios
1. Ler sem exceção
Complete para ler o estoque de "kiwi" sem risco de KeyNotFoundException:
2. Os tipos do dicionário
Complete a criação de um dicionário que liga o nome do aluno (texto) à nota (double):
3. O dicionário tem ["pão"] = 0.75m. O que acontece com precos["pão"] = 0.80m;?
4. O que Console.WriteLine(precos); mostra, com precos sendo um Dictionary<string, decimal>?
5. Dentro de um foreach sobre o dicionário d, você chama d.Add("nova", 1). O que acontece?
6. Depois de remover uma chave e acrescentar outra, em que ordem o foreach mostra as chaves de um Dictionary?
Missão final: o caixa conta as frutas
Missão: o caixa da lojinha registrou as vendas do dia no array vendas. Crie um Dictionary<string, int> chamado contagem, conte quantas vezes cada fruta foi vendida e mostre uma linha por fruta no formato banana: 3. No fim, mostre Frutas diferentes: 3 usando o Count. A ordem das linhas não importa.
banana: 3 maçã: 2 uva: 1 Frutas diferentes: 3
string[] vendas = ["banana", "maçã", "banana", "uva", "maçã", "banana"];
// crie o dicionário contagem aqui
foreach (string fruta in vendas)
{
// conte a fruta
}
// mostre cada fruta e o total de frutas diferentes
string[] vendas = ["banana", "maçã", "banana", "uva", "maçã", "banana"];
var contagem = new Dictionary<string, int>();
foreach (string fruta in vendas)
{
contagem[fruta] = contagem.GetValueOrDefault(fruta) + 1;
}
foreach (var (fruta, vezes) in contagem)
{
Console.WriteLine($"{fruta}: {vezes}");
}
Console.WriteLine($"Frutas diferentes: {contagem.Count}");
Resumo
- Um
Dictionary<TKey, TValue>liga cada chave (única) a um valor e acha qualquer chave muito rápido. d[k] = vcria ou troca;d.Add(k, v)só cria e lançaArgumentExceptionse a chave já existe.- Ler
d[k]com uma chave que não existe lançaKeyNotFoundException. Quando a chave pode faltar, useTryGetValueouGetValueOrDefault. foreach (var (chave, valor) in d)percorre os pares;KeyseValuesdão só as chaves ou só os valores.Console.WriteLine(d)mostra só o nome do tipo.- A ordem de um
Dictionarynão é garantida (uma remoção já a muda). Para ordem de chave, useSortedDictionaryou ordene antes de mostrar. - Acrescentar chaves dentro do
foreachdo próprio dicionário dáInvalidOperationException. Anote numa lista e mude depois do laço.