Swift中数组的直方图

时间:2018-04-23 10:43:59

标签: ios arrays swift histogram

我试图编写一个在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]

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)
        }
    }
}

终于可以了。