Reputation: 63272
I've written this extension to SequenceType
to mimic Python's collections.Counter
.
let input = [
"a", "a", "a", "a", "a",
"b", "b", "b", "b",
"c", "c", "c",
"d", "d",
"e"
]
let counts = input.countRepetitions()
print(counts) //expected result: ["a": 5 , "b" : 4, "c" : 3, "d" : 2, "e" : 1]
Here's the code:
extension SequenceType where Self.Generator.Element : Hashable {
func countRepetitions() -> [Self.Generator.Element : Int] {
return self.reduce([Self.Generator.Element : Int]()) { dict, element in
dict[key: element] = (dict[element] ?? 0) + 1
}
}
}
I get the following error:
Playground execution failed: OS X.playground:26:22: error: type of expression is ambiguous without more context
return self.reduce([Self.Generator.Element : Int]()) { dict, element in
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Upvotes: 1
Views: 439
Reputation: 539745
That seems to be a restriction when defining a variable with a nested type. While
let foo1: [Self.Generator.Element : Int] = [:]
compiles inside your method, this doesn't:
let foo2 = [Self.Generator.Element : Int]()
// error: type of expression is ambiguous without more context
As a workaround, you can define a typealias:
typealias E = Self.Generator.Element
let foo3 = [E : Int]()
Applied to your case:
extension SequenceType where Self.Generator.Element : Hashable {
func countRepetitions() -> [Self.Generator.Element : Int] {
typealias E = Self.Generator.Element
return self.reduce([E : Int]()) { (dict, element) -> [E : Int] in
var dict = dict
dict[element] = (dict[element] ?? 0) + 1
return dict
}
}
}
(Note that closure parameters are constants, so you have to make a mutable copy first. Also the closure must return a value.)
But actually you can avoid the problem and let the compiler infer the type:
extension SequenceType where Self.Generator.Element : Hashable {
func countRepetitions() -> [Self.Generator.Element : Int] {
return self.reduce([:]) { (dict, element) in
var dict = dict
dict[element] = (dict[element] ?? 0) + 1
return dict
}
}
}
Note also that reduce
creates a new dictionary in each iteration
step.
A more efficient solution would be
extension SequenceType where Generator.Element : Hashable {
func countRepetitions() -> [Generator.Element : Int] {
var dict: [Generator.Element: Int] = [:]
self.forEach {
dict[$0] = (dict[$0] ?? 0) + 1
}
return dict
}
}
where I have also omitted the (redundant) Self.
.
Upvotes: 6