Creating a dictionary from an EnumeratedSequence

(The question is motivated by How to Convert array to Dictionary (without Loop) on Stack Overflow.)

I wonder why the following code does not compile:

let array = ["zero", "one", "two", "three"]
let dict = Dictionary(uniqueKeysWithValues: array.enumerated())
// Generic parameter 'Key' could not be inferred

Specifying key and value type does not help:

let array = ["zero", "one", "two", "three"]
let dict = Dictionary<Int, String>(uniqueKeysWithValues: array.enumerated())
// Generic parameter 'S' could not be inferred

As far as I can tell, enumerated() here returns a sequence of (offset: Int, element: String) pairs, so I would have expected that the above can be used to create a [Int: String] dictionary. Is that because the tuples are labeled?

Creating an intermediate array makes it compile and work:

let array = ["zero", "one", "two", "three"]
let dict = Dictionary<Int, String>(uniqueKeysWithValues: Array(array.enumerated()))
print(dict) // [2: "two", 0: "zero", 1: "one", 3: "three"]

I am also aware that there are alternative solutions, such as

let dict = Dictionary(uniqueKeysWithValues: zip(array.indices, array))

but I am curious why the first approach does not work.

Regards, Martin

1 Like

I’m going to go out on a limb and call this a bug (-: The sticking point seems to be the labels on the tuple element of the sequence returned by enumerated(). If you dummy up a sequence with labels (I apologies for the horrible code), it fails in the same way.

let sLabelled = sequence(state: (i: (0...).makeIterator(), s: array.makeIterator())) { (state) -> (a: Int, b: String)? in
    guard let n = state.s.next() else { return nil }
    return (state.i.next()!, n)
}
//  ^ error: generic parameter 'Key' could not be inferred

However, if you simply remove those labels (a and b in the above example) it compiles.

let sUnlabelled = sequence(state: (i: (0...).makeIterator(), s: array.makeIterator())) { (state) -> (Int, String)? in
    guard let n = state.s.next() else { return nil }
    return (state.i.next()!, n)
}
_ = Dictionary(uniqueKeysWithValues: sUnlabelled)

I’d appreciate you filing a bug about this; please post your bug number, just for the record.

Finally, you wrote:

I am also aware that there are alternative solutions, such as

let dict = Dictionary(uniqueKeysWithValues: zip(array.indices, array))

Be aware that this is not the same, because indices doesn’t necessary start at 0. The droid you’re looking for is:

let dict = Dictionary(uniqueKeysWithValues: zip(0..., array))
print(dict)

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"

2 Likes

Filed as https://bugs.swift.org/browse/SR-6905

Terms of Service

Privacy Policy

Cookie Policy