Swift中数组的直方图

我正在尝试编写一个在Array上运行的通用直方图函数,但由于Type’Element’不符合协议’Hashable’ ,我遇到了困难。

 extension Array { func histogram() -> [Array.Element: Int] { return self.reduce([Array.Element: Int]()) { (acc, key) in let value = (acc[key] == nil) ? 1 : (acc[key]! + 1) return acc.dictionaryByUpdatingKey(key: key, value: value) } } } 

其中dictionaryByUpdatingKey(...)改变现有字典,如下所示:

 extension Dictionary { func dictionaryByUpdatingKey(key: Dictionary.Key, value: Dictionary.Value) -> Dictionary { var mutableSelf = self let _ = mutableSelf.updateValue(value, forKey: key) return mutableSelf } } 

我尝试用Array.Element替换Array.Element然后强制key as! AnyHashable key as! AnyHashable ,但这看起来很乱,返回类型最好与Array.Element类型相同,而不是AnyHashable

我希望使用Array扩展,如下所示:

 let names = ["Alex", "Alex", "James"] print(names.histogram()) // ["James": 1, "Alex": 2] 

要么

 let numbers = [2.0, 2.0, 3.0] print(numbers.histogram()) // [3.0: 1, 2.0: 2] 

添加genericswhere子句where Element: Hashable到您的扩展名:

 extension Array where Element: Hashable { func histogram() -> [Array.Element: Int] { return self.reduce([Array.Element: Int]()) { (acc, key) in let value = acc[key, default: 0] + 1 return acc.dictionaryByUpdatingKey(key: key, value: value) } } } 

我还整合了@ MartinR建议使用新的default值进行字典查找。


使用reduce(into:_:)你可以更简单有效地做到这一点:

 extension Array where Element: Hashable { func histogram() -> [Element: Int] { return self.reduce(into: [:]) { counts, elem in counts[elem, default: 0] += 1 } } } 

首先,您可以将Element类型限制为仅Hashable。

 extension Array where Array.Element:Hashable { 

在此之后,您可能会遇到另一个错误,因为swift编译器有点“过度训练”。 试着给他一个提示:

 typealias RT = [Array.Element: Int] 

并在任何地方使用它。 所以:

 extension Array where Array.Element:Hashable { typealias RT = [Array.Element: Int] func histogram() -> RT { return self.reduce(RT()) { (acc, key) in let value = (acc[key] == nil) ? 1 : (acc[key]! + 1) return acc.dictionaryByUpdatingKey(key: key, value: value) } } } 

终于应该工作了。