如何计算Dictionary中唯一值的出现次数?

Pat*_*ryk 8 c# dictionary distinct

我有一个字典,双精度值和字符串作为键.

我想计算这个字典中每个值的出现次数,我想知道这个值(例如重复).

例如:

key1, 2
key2, 2
key3, 3
key4, 2
key5, 5
key6, 5
Run Code Online (Sandbox Code Playgroud)

我想得到一个清单:

2 - 3 (times)
3 - 1 (once)
5 - 2 (twice)
Run Code Online (Sandbox Code Playgroud)

我该怎么做?

Jon*_*nna 11

首先要注意的是,您实际上并不关心字典的键.因此,第一步是忽略它们与手头的任务无关.我们将使用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);
Run Code Online (Sandbox Code Playgroud)

希望这非常简单.另一种方法更复杂但有一些优点:

//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);
Run Code Online (Sandbox Code Playgroud)

(我们可能使用var而不是冗长IEnumerable<IGrouping<int, int>>,但在解释代码时值得精确).

直接比较,这个版本较差 - 理解起来更复杂,效率更低.但是,学习这种方法可以实现相同技术的一些简洁有效的变体,因此值得研究.

GroupBy()获取枚举并创建另一个包含键值对的枚举,其中值也是枚举.lambda x => x意味着它所分组的内容本身,但我们对不同的分组规则具有灵活性.grp看起来有点像的内容:

{
  {Key=2, {2, 2, 2}}
  {Key=3, {3}}
  {Key=5, {5, 5}}
}
Run Code Online (Sandbox Code Playgroud)

因此,如果我们为每个组循环执行此操作,我们会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());
Run Code Online (Sandbox Code Playgroud)

成:

foreach(var item in dict.Values.GroupBy(x => x))
  Console.WriteLine("{0} - {1}", item.Key, item.Count());
Run Code Online (Sandbox Code Playgroud)

这是非常简洁的,并且变得惯用.如果我们希望继续使用值计数对执行更复杂的操作,那将是特别好的,因为我们可以将其链接到另一个操作.

将结果放入字典的版本可以更简洁:

var valCount = dict.Values.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
Run Code Online (Sandbox Code Playgroud)

在那里,您的整个问题在一个简短的行中回答,而不是第一个版本的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());,然后改为第一版的更冗长但更有效的形式(我们在新词典中增加运行总数)它被证明是一个性能热点.