简体   繁体   中英

Convert Array of Dictionary to custom object swift

I have:

    let countries : [[String : Any]] = [
            [
                "name" : "Afghanistan",
                "dial_code": "+93",
                "code": "AF"
            ],
            [
                "name": "Aland Islands",
                "dial_code": "+358",
                "code": "AX"
            ],
            [
                "name": "Albania",
                "dial_code": "+355",
                "code": "AL"
            ],
            [
                "name": "Algeria",
                "dial_code": "+213",
                "code": "DZ"
            ]
]

I want to add all this array of dictionary to my custom object like

let country:[Country] = countries

My custom object looks like this:

class Country: NSObject {
        let name: String
        let dial_code : String
        let code: String

        init(name: String, dial_code: String, code: String) {
            self.name = name
            self.dial_code = dial_code
            self.code = code
        }
    }

I understand that I need a loop thru the array but idk what is the next step. Would be great to have an example.

You should make your Country conform to Codable protocol, convert your dictionary to JSON data using JSONSerialization and then just decode the Data using JSONDecoder , note that you can set its keyDecodingStrategy property to convertFromSnakeCase auto avoid the need to declare custom coding keys like dial_Code :

struct Country: Codable {
    let name: String
    let dialCode : String
    let code: String
}

do {
    let json = try JSONSerialization.data(withJSONObject: countries)
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let decodedCountries = try decoder.decode([Country].self, from: json)
    decodedCountries.forEach{print($0)}
} catch {
    print(error)
}

Country(name: "Afghanistan", dialCode: "+93", code: "AF")

Country(name: "Aland Islands", dialCode: "+358", code: "AX")

Country(name: "Albania", dialCode: "+355", code: "AL")

Country(name: "Algeria", dialCode: "+213", code: "DZ")

Not related but remove NSObject until you are required

That is very simple thing you just need to think a bit

Create Object like this

var arr = [Country]()

Now Loop your array of dictionary

  for dict in countries {
      // Condition required to check for type safety :)
        guard let name = dict["name"] as? String, 
              let dialCode = dict["dial_code"] as? String, 
              let code = dict["code"] as? String else {
              print("Something is not well")
             continue
         }
        let object = Country(name: name, dial_code:dialCode, code:code)
         arr.append(object)
    }
  

That's it You have converted array of dict to Custom Object

Hope it is helpful to you

You can use flatMap method of a list to produce the result:

countries.flatMap { (v: [String: Any]) -> Country? in
    if let name = v["name"] as? String, 
       let dial = v["dial_code"] as? String, 
       let code = v["code"] as? String {
        return Country(name: name, dial_code: dial, code: code)
    } else {
        return nil
    }
}

A full example would be:

//: Playground - noun: a place where people can play

import UIKit

let countries : [[String : Any]] = [
    [
        "name" : "Afghanistan",
        "dial_code": "+93",
        "code": "AF"
    ],
    [
        "name": "Aland Islands",
        "dial_code": "+358",
        "code": "AX"
    ],
    [
        "name": "Albania",
        "dial_code": "+355",
        "code": "AL"
    ],
    [
        "name": "Algeria",
        "dial_code": "+213",
        "code": "DZ"
    ]
]

class Country: NSObject {
    let name: String
    let dial_code : String
    let code: String

    init(name: String, dial_code: String, code: String) {
        self.name = name
        self.dial_code = dial_code
        self.code = code
    }
}

let cnt = countries.flatMap { (v: [String: Any]) -> Country? in
    if let name = v["name"] as? String, let dial = v["dial_code"] as? String, let code = v["code"] as? String {
        return Country(name: name, dial_code: dial, code: code)
    } else {
        return nil
    }
}

print (cnt)

There are a lot of answers already, but I find that there are short comings with most of them. This is what I would suggest:

extension Country {
    init?(fromDict dict: [String: Any]) {
        guard let name = dict["name"] as? String, 
              let dialCode = dict["dial_code"] as? String, 
              let code = dict["code"] as? String else {
            return nil
        }
        self.init(name: name, dialCode: dialCode, code: code)
    }
}

let countries = countryDictionaries.map { dict -> Country in
    if let country = Country(fromDict: dict) { return Country }
    else {
        preconditionFailure("Tried to convert an invalid dict into a country")
        // TODO: handle error appropriately
    }
}

If you just want to ignore invalid country dictionaries, that's even easier:

let countries = countryDictionaries.flatMap(Country.init(fromDict:))

Very simple and clear solution:

  • Create custom initializer with param json [String : Any] in your class Country .
  • Init all variables of class using loop in custom initializer.

Try this code:

class Country: NSObject {
    var name: String = ""
    var dial_code: String = ""
    var code: String = ""

    // Sol: 1
    init(json: [String : Any]) {
        if let name = json["name"] as? String, let dial_code = json["dial_code"] as? String, let code = json["name"] as? String {
            self.name = name
            self.dial_code = dial_code
            self.code = code
        }
    }

    // or Sol: 2
    init(name: String, dial_code: String, code: String) {
        self.name = name
        self.dial_code = dial_code
        self.code = code
    }
}
  • Create an instance of class Countries using element of array countries and collect the same in separate array arrayOfCountries

Try this code:

let countries : [[String : Any]] = [
    [
        "name" : "Afghanistan",
        "dial_code": "+93",
        "code": "AF"
    ],
    [
        "name": "Aland Islands",
        "dial_code": "+358",
        "code": "AX"
    ],
    [
        "name": "Albania",
        "dial_code": "+355",
        "code": "AL"
    ],
    [
        "name": "Algeria",
        "dial_code": "+213",
        "code": "DZ"
    ]
]

var arrayOfCountries = [Country]()

// Sol: 1
for json in countries {
    let country = Country(json: json)
    print("country name - \(country.name)")
    arrayOfCountries.append(country)
}

// Sol: 2
for json in countries {

    if let name = json["name"] as? String, let dial_code = json["dial_code"] as? String, let code = json["name"] as? String {
        let country = Country(name: name, dial_code: dial_code, code: code)
        print("country name - \(country.name)")
        arrayOfCountries.append(country)
    }

}

Create a custom country class with param json [String : Any]

class Country: NSObject {
    var name: String?
    var dialCode: String?
    var code: String?

    init(json: [String : Any]) {
       self.name = json["name"] as? String
       self.dialCode = json["dial_code"] as? String
       self.code = json["code"] as? String
    }
}

Later you can map the dictionary into the array of country using

let _ = countries.flatMap { Country.init }

Here is a useful extension that infers the type from the pre-defined return type:

extension Dictionary {
    func castToObject<T: Decodable>() -> T? {
        let json = try? JSONSerialization.data(withJSONObject: self)
        return json == nil ? nil : try? JSONDecoder().decode(T.self, from: json!)
    }
}

Usage would be:

struct Person: Decodable {
  let name: String
}

let person: Person? = ["name": "John"].castToObject()
print(person?.name) // Optional("John")

First you need to initialize an empty array type of Country Class

var countryArray = [Country]()
//then you have to loop thru the countries dictionary 
//and after casting them adding it to this empty array with class initializer  

countries.forEach { (dict) in

    countryArray.append(Country(name: dict["name"] as! String, dial_code: dict["dial_code"] as! String, code: dict["code"] as! String))

}
//this is how you reach to properties
countryArray.forEach { (country) in
    print(country.name)
}

You can map the dictionary into the array. As a dictionary always returns an optional value for key (the value is not guaranteed to exist) you need a guard to make sure you proceed only if that is the case.

In this sample solution I throw if any of the values are not there - but this is really up to you to decide.

struct AnError: Error {}

do {
    let countryObjects: [Country] = try countries.map {
        guard let name = $0["name"] as? String,
              let dial_code = $0["dial_code"] as? String,
              let code = $0["code"] as? String else {throw AnError()}

        return Country(name: name, dial_code: dial_code, code: code)
    }
}
catch {
    //something went worng - handle the error
}

you can use Array.foreach like this

countries.forEach{country.append(Country($0))}

and u may change init parameters of Country to [String: Any] , or cast $0 to [String: Any] and read ur values from it and send them to init

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM