Первое, что нужно отметить, это то, что вам на самом деле не нужны ключи словаря. Следовательно, первый шаг - игнорировать их как не относящиеся к поставленной задаче. Мы собираемся работать со свойством Values
словаря, и работа практически такая же, как и для любого другого набора целых чисел (или любого другого перечислимого любого другого типа, который мы можем сравнить на равенство).
Существует два распространенных подхода к этой проблеме, оба из которых стоит знать.
Первый использует другой словарь для хранения количества значений:
//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);
Надеюсь, это довольно просто. Другой подход более сложный, но имеет свои плюсы:
//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);
(Мы, вероятно, будем использовать var
вместо многословного IEnumerable<IGrouping<int, int>>
, но при объяснении кода стоит быть точным).
В прямом сравнении эта версия уступает - и более сложна для понимания, и менее эффективна. Однако изучение этого подхода допускает несколько кратких и эффективных вариантов одного и того же метода, поэтому его стоит изучить.
GroupBy()
принимает перечисление и создает другое перечисление, которое содержит пары ключ-значение, где значение также является перечислением. Лямбда x => x
означает, что она группируется сама по себе, но у нас есть гибкость для других правил группировки, чем эта. Содержимое grp
выглядит примерно так:
{
{Key=2, {2, 2, 2}}
{Key=3, {3}}
{Key=5, {5, 5}}
}
Итак, если мы пройдем через это an для каждой группы, вытащим Key
и вызовем Count()
для группы, мы получим желаемые результаты.
Теперь, в первом случае мы построили наш счет за один проход O (n), в то время как здесь мы построили группу за проход O (n), а затем получили счет за второй O (n) пройти, что делает его гораздо менее эффективным. Это также немного сложнее понять, так зачем говорить об этом?
Ну, во-первых, как только мы поймем это, мы можем повернуть линии:
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());
В
foreach(var item in dict.Values.GroupBy(x => x))
Console.WriteLine("{0} - {1}", item.Key, item.Count());
Что довольно лаконично и становится идиоматичным. Особенно хорошо, если мы захотим затем сделать что-то более сложное с парами счетчик значений, поскольку мы можем связать это в другую операцию.
Версия, которая помещает результаты в словарь, может быть еще более краткой:
var valCount = dict.Values.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
Там, на весь ваш вопрос ответили в одной короткой строке, а не 6 (вырезать комментарии) для первой версии.
(Некоторые могут предпочесть заменить dict.Values.GroupBy(x => x)
на dict.GroupBy(x => x.Value)
, что будет иметь точно такие же результаты, как только мы запустим Count()
. Если вы не уверены, почему, попробуйте решить это).
Другое преимущество заключается в том, что у нас больше гибкости с GroupBy
в других случаях. По этим причинам люди, которые привыкли использовать GroupBy
, вполне могут начать с краткого однострочного dict.Values.GroupBy(x => x).ToDictinary(g => g.Key, g => g.Count());
, а затем перейти к более подробной, но более эффективной форме первой версии (где мы увеличиваем промежуточные итоги). в новом словаре), если это доказало эффективность точки доступа.