Swift中的数组直方图

5

我正在尝试编写一个通用的直方图函数,它可以操作一个Array,但是我遇到了困难,因为类型'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
    }
}

我试过使用AnyHashable替换Array.Element,并强制转换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]

请查看此链接 https://dev59.com/36bja4cB1Zd3GeqPelS2#46997085 - Prashant Tukadiya
你应该学习关于 reduce(into:updateAccumulatingResult:) 的知识,它在这种情况下更加高效。 - Martin R
2个回答

12

在您的扩展中添加 通用 where 子句:where Element: Hashable

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

我还采纳了@ MartinR的建议,使用字典查找的新default值。


使用reduce(into:_:)可以更简单高效地完成这个操作:

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

谢谢!运行得很好。 - ajrlewis
2
更简单的写法:let value = acc[key, default: 0] + 1 - Martin R
请看您的答案 https://dev59.com/bF0a5IYBdhLWcg3wPWpN#30545629 :) - Martin R
哈哈,我认识@MartinR。这次我专注于手头的问题,没有看其他代码。 - vacawama

1

首先,您可以将元素类型限制为可散列的。

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

最终应该能够运行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接