2

In Swift, is there a Collection function that, similar to map(), transforms a collection not into an array but into a dictionary?

An example that I would like to write:

let collection = [1, 2, 3, 4, 5] // Could also be a dictionary itself
let dictionary: [Int : String] = collection.map{
    $0 : "Number: \($0)"
}

As I suppose that there isn't such a function, how would something similar but elegant look like?

Lars Blumberg
  • 19,326
  • 11
  • 90
  • 127

3 Answers3

2

Here's a sketch of how it might be done. It uses a tuple return from the element closure, rather than your f1($0) : f2($0) pseudo-code. Note that the tuple elements can be any type, so long as the first (which will be used as the key) is hashable.

let collection = [1, 2, 3, 4, 5] 

/* OP's requested form:
let dictionary: [Int : String] = collection.map{
    $0 : "Number: \($0)"
}
*/

// Return a tuple of (key, value) from the closure...
extension CollectionType {
    func mapd<Tk: Hashable, Tv>(elementClosure: (Self.Generator.Element) -> (Tk, Tv) ) -> [Tk : Tv] {
        var returnDict = [Tk : Tv]()
        for i in self {
            let (k, v) = elementClosure(i)
            returnDict[k] = v
        }
        return returnDict
    }
}

let dictionary: [Int : String] = collection.mapd{ ($0, "Number: \($0)") }
print("\(dictionary)") // "[5: "Number: 5", 2: "Number: 2", 3: "Number: 3", 1: "Number:1]"

// Neither of the dictionary's elements need be the type of the collection,
// it's entirely up to what the closure returns.

let dictionary2: [Double : String] = collection.mapd{ (Double($0), "Number: \($0)") }
print("\(dictionary)") // "[5: "Number: 5", 2: "Number: 2", 3: "Number: 3", 1: "Number:1]"
Grimxn
  • 22,115
  • 10
  • 72
  • 85
2

You can use

extension Dictionary {
    init<S: SequenceType where S.Generator.Element == Element>(_ seq: S) {
        self.init()
        for (k,v) in seq {
            self[k] = v
        }
    }
}

from this answer to What's the cleanest way of applying map() to a dictionary in Swift?, which is a quite general way to create a dictionary from a sequence of key-value pairs.

Then your transformation can be done as

let collection = [1, 2, 3, 4, 5]
let dictionary = Dictionary(collection.lazy.map { ($0 , "Number: \($0)") })

print(dictionary)
// [5: "Number: 5", 2: "Number: 2", 3: "Number: 3", 1: "Number: 1", 4: "Number: 4"]

where .lazy – a suggested by @Kametrixom – avoids the creation of an intermediate array.

Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
0

I think you could solve this using a generic function

func toDictionary<T:Equatable>(collection:[T]) -> [T:String] {
  var dict = [T:String]()
  for item in collection{
    print(item)
    dict[item] = "\(item.dynamicType) \(item)"
  }
  return dict
}


let collection = [1,2,3,4,5]


let p = toDictionary(collection)
Justin Levi Winter
  • 2,327
  • 2
  • 17
  • 30