如何计算字典中唯一值的出现次数

本文关键字:唯一 何计算 计算 字典 | 更新日期: 2023-09-27 18:17:23

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

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

例如:

key1, 2
key2, 2
key3, 3
key4, 2
key5, 5
key6, 5

我想要得到一个列表:

2 - 3 (times)
3 - 1 (once)
5 - 2 (twice)

我该怎么做?

如何计算字典中唯一值的出现次数

首先要注意的是,您实际上并不关心字典的键。因此,第一步是忽略它们,因为它们与手头的任务无关。我们将使用字典的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()接受一个枚举并创建另一个包含键值对的枚举,其中值也是枚举。lambda x => x意味着它被自己分组,但我们可以灵活地使用不同的分组规则。grp的内容看起来有点像:

{
  {Key=2, {2, 2, 2}}
  {Key=3, {3}}
  {Key=5, {5, 5}}
}

因此,如果我们对每个组进行循环,我们取出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.GroupBy(x => x.Value)替换dict.Values.GroupBy(x => x),一旦我们在其上运行Count(),结果将完全相同。)如果你不能马上确定原因,试着找出原因。

另一个优点是,我们在其他情况下使用GroupBy具有更大的灵活性。由于这些原因,习惯于使用GroupBy的人很可能从dict.Values.GroupBy(x => x).ToDictinary(g => g.Key, g => g.Count());的一行简洁开始,然后如果证明它是性能热点,就会更改为更冗长但更有效的第一个版本(我们在新字典中增加运行总数)。

更简单的是:

Private Function CountOccurenceOfValue(dictionary As Dictionary(Of Integer, Integer), valueToFind As Integer) As Integer
    Return (From temp In dictionary Where temp.Value.Equals(valueToFind) Select temp).Count()
End Function

(是的,它是在VB中。. NET,但是转换成c#应该不会有太大的麻烦:-))