39

Let’s say I have array of objects that can be identified and I want to create dictionary from it. I can easily get tuples from my array like so:

let tuples = myArray.map { return ($0.id, $0) }

But I can’t see initializer for dictionary to take array of tuples. Am I missing something? Do I have create extension for dictionary for this functionality (in fact it’s not hard but I thought it would be supplied by default) or there is easier way to do that?



There is code for extension

extension Dictionary
{
    public init (_ arrayOfTuples : Array<(Key, Value)>)
    {
        self.init(minimumCapacity: arrayOfTuples.count)

        for tuple in arrayOfTuples
        {
            self[tuple.0] = tuple.1
        }
    }
}
Adamsor
  • 730
  • 1
  • 6
  • 14
  • 4
    Why should it be by default? There's no general way to map array of tuples to the Dictionary. In Dictionary each key must be unique, in array of tuples any number of repeating `keys` is allowed. What to do with them? Rewrite? Ignore? – user28434'mstep Dec 13 '16 at 14:34
  • Yes, @user28434 is right. Btw have a look at this http://stackoverflow.com/a/31447400/4272498 – Irfan Dec 13 '16 at 14:35
  • 1
    Yeah, You're right @user28434 , I wasn't thinking about that. – Adamsor Dec 13 '16 at 14:43
  • Why are you creating an array of tuples in the first place? You can created the dictionary with the data in myArray directly instead. – gnasher729 Apr 25 '17 at 12:37

6 Answers6

64

Swift 4

If your tuples is (Hashable, String) you can use:

let array = [("key1", "value1"), ("key2", "value2"), ("key3", "value3")]
let dictionary = array.reduce(into: [:]) { $0[$1.0] = $1.1 }
print(dictionary) // ["key1": "value1", "key2": "value2", "key3": "value3"]
Serhii Didanov
  • 2,200
  • 1
  • 16
  • 31
22

Swift 4

For creation you can use native Dictionary's init functions:

Dictionary(uniqueKeysWithValues: [("a", 0), ("b", 1)]) 
// ["b": 1, "a": 0]

Dictionary(uniqueKeysWithValues: [("a", 0), ("b", 1), ("b", 2)])
// Fatal error: Duplicate values for key: 'b'

// takes the first match
Dictionary([("a", 0), ("b", 1), ("a", 2)], uniquingKeysWith: { old, _ in old })
// ["b": 1, "a": 0]

// takes the latest match
Dictionary([("a", 0), ("b", 1), ("a", 2)], uniquingKeysWith: { $1 }) 
// ["b": 1, "a": 2]

Also if you want to have the shortcut:

Dictionary([("a", 0), ("b", 1), ("a", 2)]) { $1 } 
// ["b": 1, "a": 2]
dimpiax
  • 12,093
  • 5
  • 62
  • 45
8

Depending on what you want to do, you could:

let tuples = [(0, "0"), (1, "1"), (1, "2")]
var dictionary = [Int: String]()

Option 1: replace existing keys

tuples.forEach {
    dictionary[$0.0] = $0.1
}    
print(dictionary) //prints [0: "0", 1: "2"]

Option 2: Don't allow repeting keys

enum Errors: Error {
    case DuplicatedKeyError
}

do {
    try tuples.forEach {
        guard dictionary.updateValue($0.1, forKey:$0.0) == nil else { throw Errors.DuplicatedKeyError }
    }
    print(dictionary)
} catch {
    print("Error") // prints Error
}
Daniel
  • 20,420
  • 10
  • 92
  • 149
6

A generic approach:

/**
 * Converts tuples to dict
 */
func dict<K,V>(_ tuples:[(K,V)])->[K:V]{
    var dict:[K:V] = [K:V]()
    tuples.forEach {dict[$0.0] = $0.1}
    return dict
}

Functional programming update:

func dict<K,V>(tuples:[(K,V)])->[K:V]{
    return tuples.reduce([:]) {
       var dict:[K:V] = $0
       dict[$1.0] = $1.1   
       return dict
    }
}
Sentry.co
  • 5,355
  • 43
  • 38
  • 1
    @hasen Thats debatable. Since you pass the tuples as an argument it definitely shouldn't be just func. Making it static explicitly says that it can't change the state of the class it resides in – Sentry.co Sep 17 '17 at 11:26
  • 1
    It doesn't need to reside in a class. It's just a function. Swift is not Java. – hasen Sep 19 '17 at 01:07
  • 1
    @hasen I guess it's Opinionated I'll change it to func for the sake of brevity. Thanks for your input. – Sentry.co Sep 19 '17 at 20:26
  • I put this function as a Dictionary extension init. – Daniel T. Oct 06 '17 at 13:02
  • 4
    @DanielT. There is already a native one: init(uniqueKeysWithValues:) (swift 4) – Sentry.co Oct 09 '17 at 18:30
3

Improved @GitSync answer using an extension.

extension Array {
    func toDictionary<K,V>() -> [K:V] where Iterator.Element == (K,V) {
        return self.reduce([:]) {
            var dict:[K:V] = $0
            dict[$1.0] = $1.1
            return dict
        }
    }
}
Stefan
  • 131
  • 1
  • 1
1

Update to @Stefan answer's.

extension Array {

    func toDictionary<K, V>() -> [K: V] where Iterator.Element == (K, V) {
        return Dictionary(uniqueKeysWithValues: self)
    }
}
matteo2191
  • 39
  • 3