How to transform a list of dictionary into a list of structure?

Hello, I'm new to swift and I try to make a side project.

I use Xcode Version 12.5.1 (12E507) and the question is:

How to unpack a dictionary into function or struct input such as python can do with **kwargs ?

How to transform a list of dictionary into a list of structure ?

Each item in list switches should be mapped into the SwitchItem to create an instance, even though some optional attribute is not provided.

I have tried compactMapValues but it just return another dictionary with proper keys and values, such as map function in python.

The following is the simplified example code.

var current: String = "xxx"
var switches: [[String:Any]] = [
    ["model": "x1", "color": Color("x1")],
    ["model": "x2", "color": Color("x2"), "preTravel": 2.0],
    ...
    ...
]

struct SwitchItem: View {
    @Binding var current: Color
    var model: String = ""
    var color: Color = Color("default")
    var preTravel: Double = 0.0

    var body: some View { ... }
}

Here's how to transform a list of dictionaries into a list of SwitchItem. You should create a separate type for your view, as shown below. You should also not use var unless you need to mutate the variable. Always prefer let when possible.

let switches: [[String: Any]] = [
    ["model": "x1", "color": Color("x1")],
    ["model": "x2", "color": Color("x2"), "preTravel": 2.0]
]

struct SwitchItem {   
    let model: String
    let color: Color
    let preTravel: Double?   
}

let switchItems: [SwitchItem] = switches.map { switchDict -> SwitchItem in
    let model = switchDict["model"] as? String ?? ""
    let color = switchDict["color"] as? Color ?? Color("default")
    let preTravel = switchDict["preTravel"] as? Double ?? 0
    return SwitchItem(model: model, color: color, preTravel: preTravel)
}

struct SwitchItemView: View {
    
    @Binding var current: Color
    let switchItem: SwitchItem
    
    var body: some View {
        ...
    }

}
1 Like

@Peter-Schorn thanksssssss a lot !!!
Your suggestion really help me solve most of the problem in my ugly structure : )
I finally seperate itemview and picker view individually and learn about as? operator and use ForEach to generate the switch items successfully !!!
截圖 2021-06-27 下午12.13.45

I’m curious why you make preTravel an Optional Double and then initialize it as a zero when it’s not there in the Dictionary. Why not fail to nil for the missing case?

Speaking of initialization, could you extend Array where Element is SwitchItem to add an init that takes the Dictionary so you can do this?

let switchItems = [SwitchItem](switches)

That was my first thought. Is that not advisable?

Because @alanaudi used 0 as the initial value for SwitchItem.preTravel in his initial post.

This would be a bad idea because not every list of type [[String: Any]] needs to be converted to [SwitchItem]. Are you sure you didn't mean to refer to a list of dictionaries?

1 Like