0

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?

1
  • Could you make a dictionary and make the keys the languages and the values the translation? Then for the initializer you would just get a dictionary rather than multiple parameters. Commented May 27, 2022 at 18:17

2 Answers 2

2

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"
Sign up to request clarification or add additional context in comments.

4 Comments

Nice use of keyPaths, but is this what the question is getting at? Unless I'm misreading, it still requires the creation of the array which has the same manual/scaling problems of the OP's switch statement. I think the question was asking for a dot syntax approach without having to explicitly define the internal representation.
@flanker I think that key path subscription is pretty close to dot notation. Accessing properties with strings cannot be recommended.
agree with your logic and use of keypaths ... and not using strings! Just not sure it answers the OP, esp the bit referring to it becoming ugly with a long list of languages as creating the language array would suffer in a similar way. Not that I have a better solution! :)
@vadian Thank you, I was finding the docs on KeyPath sparse, and wasn't sure if I could use it. @ flanker, it's true it's not exactly what I was asking for, but it is much better than what I was using before!
2

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

1 Comment

Also very nice variation! Thanks, @Shadowrun

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.