coco
coco

Reputation: 3136

Swift: accessing struct variables with a String

I am looking for a better way to access the variables of a struct. A json query fills an array of this struct:

    struct AnimalInfo: Codable {
        var English: String
        var French: String
        var Spanish: String
    }

An example:

    let theDog = AnimalInfo(English: "Dog", French: "Chien", Spanish: "Perro")

The languages are in an array, the current one is always changing. I keep track with a pointer:

    let languageListArray = ["English", "French", "Spanish"]
    let currentLanguage = languageListArray[2]

I'd like to be able to retrieve the value from the struct with this information, something along the lines of:

    let myString = theDog.currentLanguage // = "Perro"

.. but of course that is not possible. The best I've come up with so far is a switch:

    switch currentLanguage {
        case "English":
            return theDog.English
        case "French":
            return theDog.French
        case "Spanish":
            return theDog.Spanish
    }

But this is ugly, and doesn't scale well with many languages! Anyone have a better way?

Upvotes: 0

Views: 79

Answers (2)

Shadowrun
Shadowrun

Reputation: 3857

It's possible without keyPaths too

protocol Translated {
    var english: String { get }
    var french: String { get }
    var spanish: String { get }
}

struct Animal: Codable, Translated {
    let english: String
    let french: String
    let spanish: String
}

let english: (Translated) -> String = { $0.english }
// or you can write that as a key path \.english 
let french: (Translated) -> String = { $0.french }
let spanish: (Translated) -> String = { $0.spanish }


let theDog = Animal(english: "Dog", french: "Chien", spanish: "Perro")

english(theDog) // Dog

french(theDog) // Chien

var currentLanguage: (Translated) -> String = english

currentLanguage(theDog) // Dog

currentLanguage = french

currentLanguage(theDog) // Chien

If you want your dot syntax then:

extension Translated {
    var inCurrentLanguage: String { currentLanguage(self) }
}

theDog.inCurrentLanguage // Chien

You can indeed use key paths to specify your function without having to name it, e.g.

// no need to define/name these functions
// let spanish: (Translated) -> String = { $0.spanish }

currentLanguage = \.spanish

theDog.inCurrentLanguage // Perro

Upvotes: 2

vadian
vadian

Reputation: 285069

but of course that is not possible

Oh, it is possible if you use key paths

struct AnimalInfo: Codable {
    let english: String
    let french: String
    let spanish: String
}

let theDog = AnimalInfo(english: "Dog", french: "Chien", spanish: "Perro")

let languageListArray : [KeyPath<AnimalInfo,String>] = [\.english, \.french, \.spanish]

let currentLanguage = languageListArray[2]

let myString = theDog[keyPath: currentLanguage] // = "Perro"

Upvotes: 2

Related Questions