La prima cosa da notare è che in realtà non ti importa delle chiavi del dizionario. Il primo passo è quindi ignorarli come irrilevanti per il compito in corso. Lavoreremo con la proprietà Values
del dizionario e il lavoro è molto simile a qualsiasi altra raccolta di numeri interi (o, in effetti, qualsiasi altra enumerabile di qualsiasi altro tipo che possiamo confrontare per l'uguaglianza).
Ci sono due approcci comuni a questo problema, entrambi vale la pena conoscere.
Il primo utilizza un altro dizionario per tenere il conteggio dei valori:
//Start with setting up the dictionary you described.
Dictionary<string, int> dict = new Dictionary<string, int>{
{"key1", 2},
{"key2", 2},
{"key3", 3},
{"key4", 2},
{"key5", 5},
{"key6", 5}
};
//Create a different dictionary to store the counts.
Dictionary<int, int> valCount = new Dictionary<int, int>();
//Iterate through the values, setting count to 1 or incrementing current count.
foreach(int i in dict.Values)
if(valCount.ContainsKey(i))
valCount[i]++;
else
valCount[i] = 1;
//Finally some code to output this and prove it worked:
foreach(KeyValuePair<int, int> kvp in valCount)//note - not sorted, that must be added if needed
Console.WriteLine("{0} - {1}", kvp.Key, kvp.Value);
Speriamo che questo è abbastanza semplice. Un altro approccio è più complicato, ma ha alcuni vantaggi:
//Start with setting up the dictionary you described.
Dictionary<string, int> dict = new Dictionary<string, int>{
{"key1", 2},
{"key2", 2},
{"key3", 3},
{"key4", 2},
{"key5", 5},
{"key6", 5}
};
IEnumerable<IGrouping<int, int>> grp = dict.Values.GroupBy(x => x);
//Two options now. One is to use the results directly such as with the
//equivalent code to output this and prove it worked:
foreach(IGrouping<int, int> item in grp)//note - not sorted, that must be added if needed
Console.WriteLine("{0} - {1}", item.Key, item.Count());
//Alternatively, we can put these results into another collection for later use:
Dictionary<int, int> valCount = grp.ToDictionary(g => g.Key, g => g.Count());
//Finally some code to output this and prove it worked:
foreach(KeyValuePair<int, int> kvp in valCount)//note - not sorted, that must be added if needed
Console.WriteLine("{0} - {1}", kvp.Key, kvp.Value);
(probabilmente avremmo usato var
piuttosto che il verbose IEnumerable<IGrouping<int, int>>
, ma vale la pena di essere preciso in cui spiega il codice).
In un confronto diretto, questa versione è inferiore, sia più complicata da comprendere che meno efficiente. Tuttavia, l'apprendimento di questo approccio consente alcune varianti concise ed efficienti della stessa tecnica, quindi vale la pena esaminare.
GroupBy()
prende un'enumerazione e crea un'altra enumerazione che contiene coppie chiave-valore in cui il valore è anche un'enumerazione. Il lambda x => x
significa che ciò che è raggruppato da se stesso, ma abbiamo la flessibilità per le diverse regole di raggruppamento. Il contenuto di grp
sembra un po 'come:
{
{Key=2, {2, 2, 2}}
{Key=3, {3}}
{Key=5, {5, 5}}
}
Quindi, se si ciclo attraverso questa una per ogni gruppo tiriamo fuori la Key
e chiediamo Count()
sul gruppo, otteniamo i risultati che vogliamo.
Ora, nel primo caso abbiamo accumulato il nostro conteggio in un singolo passaggio O (n), mentre qui costruiamo il gruppo in un passaggio O (n), e quindi otteniamo il conteggio in un secondo O (n) passare, rendendolo molto meno efficiente. È anche un po 'più difficile da capire, quindi perché preoccuparsi di menzionarlo?
Bene, la prima è che una volta che facciamo capirlo possiamo trasformare le linee:
IEnumerable<IGrouping<int, int>> grp = dict.Values.GroupBy(x => x);
foreach(IGrouping<int, int> item in grp)
Console.WriteLine("{0} - {1}", item.Key, item.Count());
Into:
foreach(var item in dict.Values.GroupBy(x => x))
Console.WriteLine("{0} - {1}", item.Key, item.Count());
che è abbastanza conciso, e diventa idiomatica.È particolarmente bello se vogliamo andare avanti e fare qualcosa di più complicato con le coppie conteggio dei valori, dato che possiamo incatenarle in un'altra operazione.
La versione che mette i risultati in un dizionario può essere ancora più conciso ancora:
var valCount = dict.Values.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
Lì, tutta la risposta alla tua domanda in una linea breve, piuttosto che le (commenti di taglio fuori) 6 per la prima versione.
(Alcuni potrebbero preferire per sostituire dict.Values.GroupBy(x => x)
con dict.GroupBy(x => x.Value)
che avrà esattamente gli stessi risultati, una volta corriamo il Count()
su di esso. Se non sei sicuro del perché subito, provare a lavorare fuori).
L'altro vantaggio, è che abbiamo più flessibilità con GroupBy
negli altri casi. Per queste ragioni, le persone che sono abituate a usare GroupBy
sono in grado di iniziare con la concisione su una riga di dict.Values.GroupBy(x => x).ToDictinary(g => g.Key, g => g.Count());
e quindi passare alla forma più prolissa ma più effimera della prima versione (dove incrementiamo i totali in esecuzione nel nuovo dizionario) se ha dimostrato un hotspot di prestazioni.
Un po 'più di informazioni: Sei chiedendo un conteggio dei valori che non vengono ripetuti? Potresti fornirci un esempio di dati e l'output desiderato? – Alan
La verifica dei doppi per l'uguaglianza è una pratica molto discutibile. Potresti voler evitare di menzionarlo se vuoi una risposta. L'uso di Linq's Distinct(). Count() sulla proprietà Values è altrimenti un approccio che corrisponde bene ai tag. –
E come vuoi testare l'uguaglianza dei doppi qui? –