1

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
                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Alexander
  • 59,041
  • 12
  • 98
  • 151

1 Answers1

6

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..

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Interesting. Do you have any idea why that restriction exists? And hah please excuse the errors you address in the second part, the compiler didn't get to checking those yet xD – Alexander Aug 07 '16 at 20:58
  • @AlexanderMomchliov: Unfortunately I don't know the reason. – Martin R Aug 07 '16 at 21:02
  • By the way, do you know of a way to return a copy of a dictionary which includes a new mapping, all in one expression? The dictionary equivalent to `return array + [newItem]` – Alexander Aug 07 '16 at 21:02
  • @AlexanderMomchliov: I don't know of a simple way. – Martin R Aug 07 '16 at 21:41
  • 1
    @AlexanderMomchliov Unfortunately not, [see this Q&A](http://stackoverflow.com/q/38342626/2976878) – Hamish Aug 07 '16 at 21:54