Ho visto diversi modi per scorrere su un dizionario in C #. C'è un modo standard?
Ho visto diversi modi per scorrere su un dizionario in C #. C'è un modo standard?
Risposte:
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
var entry
è meglio in quel caso, e quindi ho votato questa risposta su una seconda occhiata invece di quella sopra.
var
quando non si conosce il tipo è generalmente una cattiva pratica.
var
funziona solo se il tipo è noto al momento della compilazione. Se Visual Studio conosce il tipo, è anche possibile scoprirlo.
Se stai cercando di utilizzare un dizionario generico in C # come se utilizzassi un array associativo in un'altra lingua:
foreach(var item in myDictionary)
{
foo(item.Key);
bar(item.Value);
}
Oppure, se hai solo bisogno di scorrere la raccolta di chiavi, usa
foreach(var item in myDictionary.Keys)
{
foo(item);
}
E infine, se sei interessato solo ai valori:
foreach(var item in myDictionary.Values)
{
foo(item);
}
(Tieni presente che la var
parola chiave è una funzione C # 3.0 e superiore opzionale, puoi anche utilizzare qui il tipo esatto di chiavi / valori)
myDictionary
(a meno che questo non sia il nome effettivo ovviamente). Penso che usare var sia buono quando il tipo è ovvio, ad esempio var x = "some string"
ma quando non è immediatamente ovvio penso che sia la codifica pigra che fa male al lettore / revisore del codice
var
secondo me dovrebbe essere usato con parsimonia. Soprattutto qui, non è costruttivo: il tipo KeyValuePair
è probabilmente rilevante per la domanda.
var
ha uno scopo unico e non credo che sia zucchero "sintattico". Usarlo intenzionalmente è un approccio appropriato.
In alcuni casi potrebbe essere necessario un contatore che può essere fornito dall'implementazione for-loop. A tale scopo, LINQ fornisce ciò ElementAt
che consente quanto segue:
for (int index = 0; index < dictionary.Count; index++) {
var item = dictionary.ElementAt(index);
var itemKey = item.Key;
var itemValue = item.Value;
}
ElementAt
un'operazione O (n)?
.ElementAt
in questo contesto potrebbe portare a bug sottili. Molto più grave è il punto sopra di Arturo. Stai ripetendo i dictionary.Count + 1
tempi del dizionario che portano alla complessità O (n ^ 2) per un'operazione che dovrebbe essere solo O (n). Se hai davvero bisogno di un indice (se lo fai, probabilmente stai usando il tipo di raccolta errato in primo luogo), dovresti dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})
invece eseguire l'iterazione e non utilizzare .ElementAt
all'interno del ciclo.
Dipende dal fatto che tu stia cercando le chiavi o i valori ...
Dalla Dictionary(TKey, TValue)
descrizione della 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);
}
Generalmente, chiedere "il modo migliore" senza un contesto specifico è come chiedere qual è il colore migliore ?
Da un lato, ci sono molti colori e non esiste il colore migliore. Dipende dalla necessità e spesso anche dal gusto.
D'altra parte, ci sono molti modi per scorrere su un dizionario in C # e non esiste un modo migliore. Dipende dalla necessità e spesso anche dal gusto.
foreach (var kvp in items)
{
// key is kvp.Key
doStuff(kvp.Value)
}
Se hai bisogno solo del valore (permette di chiamarlo item
, più leggibile di kvp.Value
).
foreach (var item in items.Values)
{
doStuff(item)
}
Generalmente, i principianti sono sorpresi dall'ordine di enumerazione di un dizionario.
LINQ fornisce una sintassi concisa che consente di specificare l'ordine (e molte altre cose), ad esempio:
foreach (var kvp in items.OrderBy(kvp => kvp.Key))
{
// key is kvp.Key
doStuff(kvp.Value)
}
Ancora una volta potresti aver bisogno solo del valore. LINQ offre anche una soluzione concisa per:
item
, più leggibile di kvp.Value
)Ecco qui:
foreach (var item in items.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value))
{
doStuff(item)
}
Ci sono molti altri casi d'uso reali che puoi fare da questi esempi. Se non hai bisogno di un ordine specifico, segui il "modo più semplice" (vedi sopra)!
.Values
e non una clausola select.
Value
campi. Il tipo esatto che vedo qui è IOrderedEnumerable<KeyValuePair<TKey, TValue>>
. Forse intendevi qualcos'altro? Puoi scrivere una riga completa che mostri cosa intendi (e testarlo)?
items.Value
come hai suggerito. Nel caso della quarta sezione che hai commentato, Select()
è un modo per far foreach
elencare direttamente i valori nel dizionario anziché le coppie chiave-valore. Se in qualche modo non ti piace Select()
in questo caso, potresti preferire la terza sezione di codice. Il punto della quarta sezione è mostrare che è possibile pre-elaborare la raccolta con LINQ.
.Keys.Orderby()
, ripeterai un elenco di chiavi. Se è tutto ciò di cui hai bisogno, va bene. Se hai bisogno di valori, nel ciclo dovresti interrogare il dizionario su ogni chiave per ottenere il valore. In molti scenari non farà alcuna differenza pratica. In uno scenario ad alte prestazioni, lo farà. Come ho scritto all'inizio della risposta: "ci sono molti modi (...) e non esiste un modo migliore. Dipende dalla necessità e spesso anche dal gusto".
Direi che foreach è il modo standard, anche se ovviamente dipende da cosa stai cercando
foreach(var kvp in my_dictionary) {
...
}
È quello che stai cercando?
kvp
è comunemente utilizzato per denominare le istanze KeyValuePair quando l'iterazione su dizionari e le strutture di dati correlati: foreach(var kvp in myDictionary){...
.
C # 7.0 ha introdotto Deconstructors e se si utilizza l'applicazione .NET Core 2.0+ , la strutturaKeyValuePair<>
include già unaDeconstruct()
per voi. Quindi puoi fare:
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)))
Apprezzo che questa domanda abbia già avuto molte risposte, ma ho voluto gettare una piccola ricerca.
L'iterazione su un dizionario può essere piuttosto lenta se confrontata con l'iterazione su qualcosa come un array. Nei miei test un'iterazione su un array ha richiesto 0,015003 secondi, mentre un'iterazione su un dizionario (con lo stesso numero di elementi) ha richiesto 0,0365073 secondi che è 2,4 volte più lungo! Anche se ho visto differenze molto più grandi. Per fare un confronto, una lista si trovava da qualche parte tra 0,00215043 secondi.
Tuttavia, è come confrontare mele e arance. Il mio punto è che l'iterazione sui dizionari è lenta.
I dizionari sono ottimizzati per le ricerche, quindi con questo in mente ho creato due metodi. Uno fa semplicemente una foreach, l'altro scorre le chiavi e poi alza lo sguardo.
public static string Normal(Dictionary<string, string> dictionary)
{
string value;
int count = 0;
foreach (var kvp in dictionary)
{
value = kvp.Value;
count++;
}
return "Normal";
}
Questo carica le chiavi e le scorre invece su di esse (ho anche provato a tirare le chiavi in una stringa [] ma la differenza era trascurabile.
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";
}
Con questo esempio il normale test foreach ha richiesto 0,0310062 e la versione chiavi ha richiesto 0,2205441. Il caricamento di tutti i tasti e l'iterazione su tutte le ricerche è chiaramente MOLTO più lento!
Per un test finale ho eseguito la mia iterazione dieci volte per vedere se ci sono dei vantaggi nell'usare i tasti qui (a questo punto ero solo curioso):
Ecco il metodo RunTest se ti aiuta a visualizzare cosa sta succedendo.
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);
}
Qui la normale corsa foreach ha richiesto 0,2820564 secondi (circa dieci volte più a lungo di una singola iterazione, come previsto). L'iterazione sui tasti ha richiesto 2,2249449 secondi.
Modificato per aggiungere: la lettura di alcune delle altre risposte mi ha fatto mettere in dubbio cosa succederebbe se usassi Dizionario invece di Dizionario. In questo esempio l'array ha impiegato 0,0120024 secondi, l'elenco 0,0185037 secondi e il dizionario 0,0465093 secondi. È ragionevole aspettarsi che il tipo di dati faccia la differenza su quanto più lento è il dizionario.
Quali sono le mie conclusioni ?
Ci sono molte opzioni. Il mio preferito è di KeyValuePair
Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here
foreach (KeyValuePair<string,object> kvp in myDictionary)
{
// Do some interesting things
}
Puoi anche utilizzare le raccolte di chiavi e valori
Con .NET Framework 4.7
uno si può usare la decomposizione
var fruits = new Dictionary<string, int>();
...
foreach (var (fruit, number) in fruits)
{
Console.WriteLine(fruit + ": " + number);
}
Per far funzionare questo codice su versioni C # inferiori, aggiungi System.ValueTuple NuGet package
e scrivi da qualche parte
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
integrato. È disponibile come pacchetto nuget per le versioni precedenti. Ancora più importante, è necessario C # 7.0+ per Deconstruct
far funzionare il metodo come decostruttore var (fruit, number) in fruits
.
A partire da C # 7, puoi decostruire gli oggetti in variabili. Credo che questo sia il modo migliore per scorrere su un dizionario.
Esempio:
Crea un metodo di estensione KeyValuePair<TKey, TVal>
che lo decostruisce:
public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey key, out TVal value)
{
key = pair.Key;
value = pair.Value;
}
Scorrere su qualsiasi Dictionary<TKey, TVal>
nel modo seguente
// 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}");
}
Di seguito hai suggerito di iterare
Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here
foreach (KeyValuePair<string,object> kvp in myDictionary) {
//Do some interesting things;
}
Cordiali saluti, foreach
non funziona se il valore è di tipo oggetto.
foreach
non funzionerà se quale valore è di tipo object
? Altrimenti questo non ha molto senso.
Utilizzando C # 7 , aggiungi questo metodo di estensione a qualsiasi progetto della tua soluzione:
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 usa questa semplice sintassi
foreach (var(id, value) in dict.Tuples())
{
// your code using 'id' and 'value'
}
O questo, se preferisci
foreach ((string id, object value) in dict.Tuples())
{
// your code using 'id' and 'value'
}
Al posto del tradizionale
foreach (KeyValuePair<string, object> kvp in dict)
{
string id = kvp.Key;
object value = kvp.Value;
// your code using 'id' and 'value'
}
Il metodo di estensione trasforma il KeyValuePair
tuo IDictionary<TKey, TValue>
in un fortemente tipizzatotuple
, permettendoti di usare questa nuova sintassi comoda.
Converte -just- le voci del dizionario richieste in tuples
, quindi NON converte l'intero dizionario in tuples
, quindi non ci sono problemi di prestazioni correlati a quello.
C'è solo un piccolo costo per chiamare il metodo di estensione per la creazione di un tuple
confronto con l'uso KeyValuePair
diretto, che NON dovrebbe essere un problema se si stanno assegnando le KeyValuePair
proprietàKey
e Value
comunque le nuove variabili del ciclo.
In pratica, questa nuova sintassi si adatta molto bene alla maggior parte dei casi, ad eccezione degli scenari di prestazioni ultra-alte di basso livello, in cui hai ancora la possibilità di non usarlo in quel punto specifico.
Dai un'occhiata a: Blog MSDN - Nuove funzionalità in C # 7
kvp.Key
e kvp.Value
per utilizzare rispettivamente chiave e valore. Con le tuple hai la flessibilità di nominare la chiave e il valore come desideri, senza usare ulteriori dichiarazioni variabili all'interno del blocco foreach. Ad esempio, è possibile nominare la chiave come factoryName
e il valore come models
, che è particolarmente utile quando si ottengono cicli nidificati (dizionari di dizionari): la manutenzione del codice diventa molto più semplice. Basta fare un tentativo! ;-)
So che questa è una domanda molto vecchia, ma ho creato alcuni metodi di estensione che potrebbero essere utili:
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); }
}
In questo modo posso scrivere codice in questo modo:
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););
A volte se hai solo bisogno di enumerare i valori, usa la raccolta valori del dizionario:
foreach(var value in dictionary.Values)
{
// do something with entry.Value only
}
Segnalato da questo post che afferma che è il metodo più veloce: http://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html
Ho trovato questo metodo nella documentazione per la classe DictionaryBase su MSDN:
foreach (DictionaryEntry de in myDictionary)
{
//Do some stuff with de.Value or de.Key
}
Questo è stato l'unico che sono riuscito a far funzionare correttamente in una classe ereditata da DictionaryBase.
Hashtable
ContainsKey()
nella for
versione? Ciò aggiunge un sovraccarico extra che non è presente nel codice con cui stai confrontando. TryGetValue()
esiste per sostituire l'esatto modello "se esiste la chiave, ottieni l'elemento con chiave". Inoltre, se dict
contiene un intervallo contiguo di numeri interi da 0
a dictCount - 1
, sai che l'indicizzatore non può fallire; altrimenti, dict.Keys
è ciò che dovresti ripetere. In entrambi i casi, no ContainsKey()
/ TryGetValue()
necessario. Infine, per favore non pubblicare schermate di codice.
Ho scritto un'estensione per scorrere su un dizionario.
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);
}
}
}
Quindi puoi chiamare
myDictionary.ForEach((x,y) => Console.WriteLine(x + " - " + y));
ForEach
metodo in cui hai foreach (...) { }
... Sembra inutile.
Se si dice, si desidera scorrere la raccolta di valori per impostazione predefinita, credo che sia possibile implementare IEnumerable <>, dove T è il tipo di oggetto valore nel dizionario e "this" è un dizionario.
public new IEnumerator<T> GetEnumerator()
{
return this.Values.GetEnumerator();
}
Come già sottolineato in questa risposta , KeyValuePair<TKey, TValue>
implementa un Deconstruct
metodo che inizia su .NET Core 2.0, .NET Standard 2.1 e .NET Framework 5.0 (anteprima).
Con questo, è possibile scorrere un dizionario in KeyValuePair
modo agnostico:
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
aggiunge KeyValuePair
? Dov'è la "iterazione", come richiesto nella domanda?
foreach
, ma l'ho usato molto. La mia risposta meritava davvero un voto negativo?
Select
utilizza l' iterazione per effettuare il risultato, ma non è un iteratore stesso. I tipi di cose per cui foreach
è usato iteration ( ) - specialmente le operazioni con effetti collaterali - sono al di fuori dell'ambito di Linq, incluso Select
. La lambda non verrà eseguita fino a quando non aggregateObjectCollection
viene effettivamente elencata. Se questa risposta è presa come un "primo percorso" (cioè, usato prima di una scala foreach
) incoraggia le cattive pratiche. A seconda della situazione, potrebbero esserci operazioni Linq che sono utili prima di ripetere un dizionario, ma che non rispondono alla domanda come posta.
Volevo solo aggiungere il mio 2 centesimi, poiché la maggior parte delle risposte si riferisce a foreach-loop. Per favore, dai un'occhiata al seguente codice:
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));
});
Anche se questo aggiunge un ulteriore richiamo di '.ToList ()', potrebbe esserci un leggero miglioramento delle prestazioni (come sottolineato qui foreach vs someList.Foreach () {} ), soprattutto quando si lavora con dizionari di grandi dimensioni e l'esecuzione in parallelo non è no opzione / non avrà alcun effetto.
Inoltre, tieni presente che non sarai in grado di assegnare valori alla proprietà 'Value' all'interno di un ciclo foreach. D'altra parte, sarai anche in grado di manipolare la 'Chiave', mettendoti probabilmente nei guai in fase di esecuzione.
Quando vuoi semplicemente "leggere" chiavi e valori, puoi anche usare IEnumerable.Select ().
var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );
foreach
effetti collaterali: aumenta la visibilità degli effetti collaterali, a cui appartiene.
Dizionario <TKey, TValue> È una classe di raccolta generica in c # e memorizza i dati nel formato del valore chiave. Il tasto deve essere univoco e non può essere nullo mentre il valore può essere duplicato e nullo. Come ogni elemento nel dizionario è trattato come KeyValuePair <TKey, TValue> struttura che rappresenta una chiave e il suo valore. e quindi dovremmo prendere il tipo di elemento KeyValuePair <TKey, TValue> durante l'iterazione dell'elemento. Di seguito è riportato l'esempio.
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 si desidera utilizzare per il ciclo, è possibile effettuare ciò:
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 prestazioni peggiori perché new List<string>(dictionary.Keys)
ripeterà i dictionary.Count
tempi prima ancora che tu abbia la possibilità di iterarlo da solo. Mettendo da parte il fatto che chiedere "il modo migliore" è soggettivo, non vedo come questo si qualificherebbe né come "modo migliore" o "modo standard" che la domanda cerca. A "Se si desidera utilizzare per il loop ..." Contrasterei con " Non utilizzare un for
loop".
foreach (var pair in dictionary.ToArray()) { }
. Tuttavia, penso che sarebbe bene chiarire nella risposta gli scenari specifici in cui si vorrebbe usare questo codice e le implicazioni nel farlo.
semplice con 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()
perché ForEach()
è definito solo sulla List<>
classe, ma perché fare tutto questo invece che solo foreach (var pair in dict) { }
? Direi che è ancora più semplice e non ha le stesse implicazioni di memoria / prestazioni. Questa soluzione esatta era già stata proposta in questa risposta da 3,5 anni fa, comunque.
oltre ai post più alti in cui vi è una discussione tra l'utilizzo
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
o
foreach(var entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
il più completo è il seguente perché è possibile visualizzare il tipo di dizionario dall'inizializzazione, 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
}