I have an array of Person
\'s objects:
class Person {
let name:String
let position:Int
}
and the array is:
You may write custom initializer for Dictionary
type, for example from tuples:
extension Dictionary {
public init(keyValuePairs: [(Key, Value)]) {
self.init()
for pair in keyValuePairs {
self[pair.0] = pair.1
}
}
}
and then use map
for your array of Person
:
var myDictionary = Dictionary(keyValuePairs: myArray.map{($0.position, $0.name)})
extension Array {
func mapToDict<T>(by block: (Element) -> T ) -> [T: Element] where T: Hashable {
var map = [T: Element]()
self.forEach{ map[block($0)] = $0 }
return map
}
}
Maybe something like this?
myArray.forEach({ myDictionary[$0.position] = $0.name })
This is what I have been using
struct Person {
let name:String
let position:Int
}
let persons = [Person(name: "Franz", position: 1),
Person(name: "Heinz", position: 2),
Person(name: "Hans", position: 3)]
var peopleByPosition = [Int: Person]()
persons.forEach{peopleByPosition[$0.position] = $0}
Would be nice if there was a way to combine the last 2 lines so that peopleByPosition
could be a let
.
We could make an extension to Array that does that!
extension Array {
func mapToDict<T>(by block: (Element) -> T ) -> [T: Element] where T: Hashable {
var map = [T: Element]()
self.forEach{ map[block($0)] = $0 }
return map
}
}
Then we can just do
let peopleByPosition = persons.mapToDict(by: {$0.position})
How about a KeyPath based solution?
extension Array {
func dictionary<Key, Value>(withKey key: KeyPath<Element, Key>, value: KeyPath<Element, Value>) -> [Key: Value] {
return reduce(into: [:]) { dictionary, element in
let key = element[keyPath: key]
let value = element[keyPath: value]
dictionary[key] = value
}
}
}
This is how you will used it:
struct HTTPHeader {
let field: String, value: String
}
let headers = [
HTTPHeader(field: "Accept", value: "application/json"),
HTTPHeader(field: "User-Agent", value: "Safari"),
]
let allHTTPHeaderFields = headers.dictionary(withKey: \.field, value: \.value)
// allHTTPHeaderFields == ["Accept": "application/json", "User-Agent": "Safari"]
Since Swift 4
you can do @Tj3n's approach more cleanly and efficiently using the into
version of reduce
It gets rid of the temporary dictionary and the return value so it is faster and easier to read.
Sample code setup:
struct Person {
let name: String
let position: Int
}
let myArray = [Person(name:"h", position: 0), Person(name:"b", position:4), Person(name:"c", position:2)]
Into
parameter is passed empty dictionary of result type:
let myDict = myArray.reduce(into: [Int: String]()) {
$0[$1.position] = $1.name
}
Directly returns a dictionary of the type passed in into
:
print(myDict) // [2: "c", 0: "h", 4: "b"]