Search code examples
iosswiftvalue-type

How to convert a class / struct into dictionary with key different as that of property name


I have a struct and I want to convert that into Dictionary with properties as Key and their value as Value but the key should not be the property name but something different i.e. shopping_list_id OR shopping_list_name.

struct AddItemToEvent {
  var key: String
  var shoppingListId: String   //shopping_list_id
  var shoppingListName: String   //shopping_list_name
  var commonName: String
  var storeCode: String
  var sourceType: String
  var sourceId: String
  var isDefaultList: String
  var storeLocation: String

//  shopping_list_id, shopping_list_name, common_name, store_code, source_type (offer, promotion, normal, favourite), source_id, default_list, store_location, client_id 
}

I need to have a method for struct getDictionaryEquivalent() which can result something like ["shopping_list_name":"", "shopping_list_id": "", ......]


Solution

  • You can use a regular expression to convert to snake case and use reflection (Mirror) to convert to a dictionary.

    The regex is rather simple and will not work so well if you for instance have several uppercase letters after each other so this part could be improved if needed.

    func snakeKeyDictionary(_ mirror: Mirror) -> [String: Any] {
        var dictionary = [String: Any]()
        for (key, value) in mirror.children {
            if let key = key {
                let snakeKey = key.replacingOccurrences(of: #"[A-Z]"#, with: "_$0", options: .regularExpression).lowercased()
                dictionary[snakeKey] = value
            }
        }
        return dictionary
    }
    

    Example if usage

    let item = AddItemToEvent(key: "1", shoppingListId: "12", shoppingListName: "List", 
        commonName: "some", storeCode: "ABC", sourceType: "A", sourceId: "fgd", 
        isDefaultList: "yes", storeLocation: "home")
    
    let mirror = Mirror(reflecting: item)
    print(snakeKeyDictionary(mirror))
    

    prints

    ["common_name": "some", "is_default_list": "yes", "store_code": "ABC", "store_location": "home", "key": "1", "shopping_list_name": "List", "source_id": "fgd", "shopping_list_id": "12", "source_type": "A"]

    But of course if the goal is to create json data it is quite simple

    Make the struct conform to Codable and then set the keyEncodingStrategy property when encoding

    let encoder = JSONEncoder()
    encoder.keyEncodingStrategy = .convertToSnakeCase