我在leetcode上解决一些问题,在用python解决问题时我经常不得不使用python的
collections.Counter
对象。
当我开始用 C# 解决同样的问题时,我找不到等效的方法或类。
我只是想知道有没有?
collections.Counter
collections.Counter
接受一个 iterable 并返回一个 hash-map,其中键是原始 iterable 的所有唯一元素,对应的值是该 key 在原始 iterable 中出现的次数
这是一个真正快速实现的类,它可以完成您正在考虑的事情。请随意添加一些额外的功能:
public class CountedCollection<T> : IEnumerable<KeyValuePair<T, int>>
{
private readonly Dictionary<T, int> _countDictionary;
public CountedCollection()
{
if (!typeof(IEquatable<T>).IsAssignableFrom(typeof(T)))
{
throw new ArgumentException($"The type {typeof(T).Name} must implement IEquatable of <{typeof(T).Name}> to be usable with CountedCollection");
}
_countDictionary = new Dictionary<T, int>();
}
public CountedCollection(IEqualityComparer<T> comparer)
{
_countDictionary = new Dictionary<T, int>(comparer);
}
public void Add(T item)
{
++TotalCount;
if (_countDictionary.TryGetValue(item, out var curCount))
{
_countDictionary[item] = ++curCount;
}
else
{
_countDictionary.Add(item, 1);
}
}
public void Add (IEnumerable<T> items)
{
foreach (var item in items)
{
Add(item);
}
}
public int TotalCount { get; private set; } = 0;
public int DistinctCount => _countDictionary.Count;
public IEnumerator<KeyValuePair<T, int>> GetEnumerator()
{
foreach (var item in _countDictionary)
{
yield return item;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
测试非常简单。
它实现了“集合初始化模式”,并且可以使用现有集合进行初始化,因此这两者都可以工作(第二个使用整数数组来初始化它):
var dict1 = new CountedCollection<string> { "abc", "xyz", "abc", "123" };
var dict2 = new CountedCollection<int> { new[] { 34, 12, 65, 12, 101 } };
该类提供了两个构造函数。如果
T
未实现 IEquatable<T>
,那么您可以提供自己的 IEqualityComparer<T>
实现。这还允许您对实例进行独立于大小写的计数。例如:
var dict3 = new CountedCollection<string>(StringComparer.OrdinalIgnoreCase)
{
"abc",
"xyz",
"ABC",
"123",
"Xyz"
};
该代码将显示两个“abc”实例和两个“xyz”实例。
因为你可以自带
IEqualityComparer<T>
,T
不受实现 IEquatable<T>
的限制,如果 ArgumentException
无法实现 T
,第一个构造函数将抛出 IEquatable<T>
(事物必须以某种方式具有可比性以确保相等) ).
例如,这会抛出:
var dict4 = new CountedCollection<CountedCollection<string>>();
这是
ToCounter
LINQ 运算符的高性能实现,它采用可枚举序列并返回一个以序列元素作为键的字典。生成的字典的 int
值表示每个元素在 source
序列中出现的次数:
public static Dictionary<TSource, int> ToCounter<TSource>(
this IEnumerable<TSource> source,
IEqualityComparer<TSource> comparer = default)
{
ArgumentNullException.ThrowIfNull(source);
Dictionary<TSource, int> dictionary = new(comparer);
foreach (TSource item in source)
CollectionsMarshal.GetValueRefOrAddDefault(dictionary, item, out _)++;
return dictionary;
}
CollectionsMarshal.GetValueRefOrAddDefault
是一种高级 API,允许通过对键的单个 GetHashCode
调用来添加或更新字典。 int
类型的默认值为0
,这使得实现非常简单(无需声明 ref 局部变量)。