How to use map/flatmap for conversion?

I'm stumped so posting here.


I have some vendor code that supplies a dictionary: [String: [String: Obj]], where Obj is a ObjC object with a "tag" and "value", both are strings. I'd love to use a higher level Swift library method to do the following:


        var dict: [String: String] = [:]
        input.forEach() { (key, obj) in
            dict[obj.tag] = obj.value
        }


Can I use map for this? [I tried to get it but no luck.]

Accepted Reply

Then you can use `mapValues` instead of `map` or `flatMap`.

let input1: [String: Obj] = [
    "tag1": Obj(tag: "tag1", value: "value1"),
    "tag2": Obj(tag: "tag2", value: "value2"),
    "tag3": Obj(tag: "tag3", value: "value3"),
]

let dict1 = input1.mapValues {$0.value}
print(dict1)

Output:

["tag3": "value3", "tag1": "value1", "tag2": "value2"]


But this is equivalent to something like this:

    var dict1: [String: String] = [:]
    input1.forEach() { (key, obj) in
        dict1[key] = obj.value //Not `dict[obj.tag]`
    }
    print(dict1)


If key and tag do not make duplicate and you want to get exactly the same result as in your code of the opening post,

you may need to combine some Dictionary initializer with `map`. (Usual `map` creates an Array, not a Dictionary.)


Anyway this is worth reading:

Dictionary and Set Improvements in Swift 4.0

Replies

It's not clear what is the type of `input` in your code. If it is ` [String: [String: Obj]]`, your `forEach` does not work.


Please show the type of `input` and some sample data with expected result.

Sigh. I had THOUGHT that was the format originally - its actually [String: Obj] - sorry for the confusion. The Obj also contains the tag in this array, which is just so odd - its total duplication.

Then your data, logically, looks like this?


let input1: [String: Obj] = [
    "tag1": Obj(tag: "tag1", value: "value1"),
    "tag2": Obj(tag: "tag2", value: "value2"),
    "tag3": Obj(tag: "tag3", value: "value3"),
]

YES!

Then you can use `mapValues` instead of `map` or `flatMap`.

let input1: [String: Obj] = [
    "tag1": Obj(tag: "tag1", value: "value1"),
    "tag2": Obj(tag: "tag2", value: "value2"),
    "tag3": Obj(tag: "tag3", value: "value3"),
]

let dict1 = input1.mapValues {$0.value}
print(dict1)

Output:

["tag3": "value3", "tag1": "value1", "tag2": "value2"]


But this is equivalent to something like this:

    var dict1: [String: String] = [:]
    input1.forEach() { (key, obj) in
        dict1[key] = obj.value //Not `dict[obj.tag]`
    }
    print(dict1)


If key and tag do not make duplicate and you want to get exactly the same result as in your code of the opening post,

you may need to combine some Dictionary initializer with `map`. (Usual `map` creates an Array, not a Dictionary.)


Anyway this is worth reading:

Dictionary and Set Improvements in Swift 4.0