我试图编写一个在Array
上运行的通用直方图函数,但我遇到了困难,因为类型'元素'不符合协议' 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
替换为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]
答案 0 :(得分:7)
将 generic where子句: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 }
}
}
答案 1 :(得分:0)
首先,您可以将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)
}
}
}
终于可以了。