0

I have an app that references a large dataset that I load from an external public site as a comma separated value file. I parse the data to a an array of a model called WaterPointModel. An abbreviated version is:

struct WaterPointModel: Identifiable {

    let id = UUID()

    let STATE: String
    let COUNTY: String
    let AQWFrTo: Double
    let AQWGWSa: Double
    let AQWGWTo: Double
    //many more
}

I then want to summarize(reduce) the data. My function for this is:

func sumOnAttributeUSList(sumField: KeyPath<WaterPointModel,Double>) -> Double {
    return dataStore.waterPointModels.map({ $0[keyPath:sumField] }).reduce(0, +)
}

Next I want to call this to build a report:

let aqWFrTo = sumOnAttributeUSList(sumField: \.AQWFrTo)    

This all works. However there are other reports where I need to pass a string to create that keypath. Say I have a lookup table where I lookup "abc" and get "AQWFrTo". I would like to do something like this in a loop:

let abc = "AQWFrTo"
let aqWFrTo = sumOnAttributeUSList(sumField: \WaterPointModel.abc)

I have not been able to make any version of this work. Any guidance would be appreciated.

Xcode 13.3.1, iOS 15.4

Asperi
  • 228,894
  • 20
  • 464
  • 690
JohnSF
  • 3,736
  • 3
  • 36
  • 72
  • This is just Swift and is not related to SwiftUI, which is a UI framework built on the Swift language. – jnpdx Apr 21 '22 at 06:21
  • It is possible to get key path by string using Mirror ([see this discussion](https://forums.swift.org/t/getting-keypaths-to-members-automatically-using-mirror/21207/4)) and then you can just overload your sum function with String argument. – Asperi Apr 21 '22 at 08:03
  • Here is a result of experiments with different dynamic accesses to property [TestWays4DynamicMemberLookup.swift](https://github.com/Asperi-Demo/4SwiftUI/blob/master/PlayOn_iOS/PlayOn_iOS/Findings/TestWays4DynamicMemberLookup.swift) – Asperi Apr 21 '22 at 10:01
  • Thanks, Asperi. That is very helpful. – JohnSF Apr 22 '22 at 00:38

1 Answers1

1

A simple approach is this:

func toKeypath(_ str: String) -> KeyPath<WaterPointModel,Double>? {  // <-- with or without optional
    switch str {
        case "AQWFrTo": return \.AQWFrTo
        case "AQWGWSa": return \.AQWGWSa
        case "AQWGWTo": return \.AQWGWTo
        // ...
        default: return nil  // <-- or a default Keypath
    }
}

let aqWFrTo = sumOnAttributeUSList(sumField: toKeypath("AQWFrTo"))