Reputation: 5397
Given,
let input = [
"A": ["X": 1, "Y": 2, "Z": 3],
"B": ["X": 7, "Y": 8, "Z": 9],
]
how do you produce,
[
"X": ["A": 1, "B": 7],
"Y": ["A": 2, "B": 8],
"Z": ["A": 3, "B": 9],
]
using functional programming?
Upvotes: 0
Views: 116
Reputation: 12988
You could do something like this:
let input = ["A": ["X": 1, "Y": 2, "Z": 3], "B": ["X": 7, "Y": 8, "Z": 9]]
let output = input.flatMap { okv in
okv.value.map { ikv in
(ikv.key, okv.key, ikv.value)
}
}
.reduce(into: [String:[(String,Int)]]()) { accum, value in
accum[value.0] = (accum[value.0] ?? []) + [(value.1,value.2)]
}
.reduce(into: [String:[String:Int]]()) { accum, value in
accum[value.key] = value.value.reduce(into: [String:Int]()) { iaccum, ivalue in
iaccum[ivalue.0] = ivalue.1
}
}
This works by first expanding the nested dictionaries into an array of tuples [("X", "A", 1), ("Y", "A", 2), ... ]
and then reduces that intermediate array back into a dictionary in two stages: first as a dictionary of arrays and then as a dictionary of dictionaries.
In fact, you can do a little bit better if you take advantage of default dictionary values:
let result = input.flatMap { okv in
okv.value.map { ikv in
(ikv.key, okv.key, ikv.value)
}
}
.reduce(into: [String:[String:Int]]()) { accum,value in
accum[value.0, default: [String:Int]()][value.1] = value.2
}
Upvotes: 4
Reputation: 2882
I cam up with something like below. I found this problem much easier to understand using imperative approach, rather than using functional programming.
Note-: I haven’t covered edge cases.
class Test{
var outputDict:[String:[String:Int]] = [:]
func test(){
let input = ["A": ["X": 1, "Y": 2, "Z": 3], "B": ["X": 7, "Y": 8, "Z": 9]]
for (key,val) in input{
var newDict:[String:Int] = [:]
for (key1,val1) in val{
generateInternalDict(&newDict, val1, key, key1)
}
}
print(outputDict)
}
func generateInternalDict(_ newDict:inout [String:Int],_ value:Int,_ keyOuter:String,_ keyInner:String){
if outputDict[keyInner] == nil{
newDict[keyOuter] = value
outputDict[keyInner] = newDict
}else{
var getDictForKey = outputDict[keyInner]
getDictForKey![keyOuter] = value
outputDict[keyInner] = getDictForKey
}
}
}
Output-:
["X": ["B": 7, "A": 1], "Z": ["B": 9, "A": 3], "Y": ["B": 8, "A": 2]]
Upvotes: 2