Mapping a dictionary from an array


(Andrea Altea) #1

A thing that I use a lot to reduce the computation cycles when working with arrays is define an HashTable as a Dictionary from my array. To do this I usually define an extension of Array in my codebases like this one:

    /// returns a dictionary with mapped keys and values based on transformation block.
    /// multiple items with duplicated keys will be overridden, only the last processed will be mantained.
    /// - Parameters:
    ///   - transformBlock: launched for each Element of the array, returns a couple of Key and Value
    /// - Returns: a dictionary with a couple of Key: Value for each Element of the array
    func keyMap<Key: Hashable, Value>(transformBlock: (Element) -> (key: Key, value: Value)) -> [Key: Value] {
        var dictionary: [Key: Value] = [:]
        forEach { element in
            let map = transformBlock(element)
            dictionary[map.key] = map.value
        return dictionary

Could this one be a valid proposal or I'm just doing something wrong?


That would be

let list: [Int] = [1, 2, 3, 4]
let d = Dictionary(uniqueKeysWithValues: { ($0, "\($0)") })

By using Array.lazy we can skip creating a temporary key/value pair array, correct?

(Andrea Altea) #3

This could be a viable solution, but i don't know if is better about performances, (don't know how Dictionary(uniqueKeysWithValues: Sequence) works under the hood).

I usually use this to compare multiple arrays of items,
where you need to recreate the same group of items updating some of them:

    func updateItems(newItems: [Item]) {
        let map = newItems.keyMap(transformBlock: { return (key: $, value: $0) })
        self.referenceItems = { map[$] ?? $0 }

or define the map of changes from the array A to the same array shuffled,
so you need to know the positions of those items in both the arrays...

   struct Change {
        var old: Int
        var new: Int
    func changes(newItems: [Item]) -> [Change] {
        let map = newItems.enumerated().keyMap { (offset, element) -> (key: Item, value: Int) in
            return (key: element, value: offset)
        var changes: [Change] = []
        self.internalItems.enumerated().forEach { (offset, element) in
            if let newPosition = map[element], newPosition != offset {
                changes.append(Change(old: offset, new: newPosition))
        return changes


Looks like it works pretty much like your own implementation.

(Andrea Altea) #5

I've definetly learned something new today,
Thank You