0

I want to initialise a list of parameters in Swift by reading text files containing eight parameter values and preserve the parameters in the same order they are read from file. Once initialised parameter values do not change. Ideally they should be accessed efficiently - and be especially clear for anyone maintaining code further down the track.

In the Playground example (below) parameter values are stored in an Array of Arrays of Strings. Raw values from the Enumeration are then used as a set of indices to address the Array. Initially I thought of using Enumeration to store parameter values as raw values to be addressed using either the parameter name or a numerical index. But from this answer I cannot see a way to assign parameter values directly to member names. In fact I'm starting to doubt whether I should be using an enumeration at all.

Can anyone offer a better way to do this ?

(This probably sounds naive as I've never used a Swift Enumeration before).

enum MapParameters: Int {
    case midiChannel                = 0,
    sizeOfRepeatingMapPattern       = 1,
    firstMIDIKey                    = 2,
    lastMIDIKey                     = 3,
    middleKey                       = 4,
    referenceMIDIKey                = 5,
    referenceFrequency              = 6,
    keysPerFormalOctave             = 7
    }

    let sourceArray = ["5", "12", "0", "127", "60", "69", "440.0", "12"]

    let mapIndex = MapParameters.referenceFrequency     //referenceFrequency
    let i = mapIndex.rawValue                           // 6
    let parameterValue  = sourceArray[i]                // "440.0"
Community
  • 1
  • 1
Greg
  • 1,750
  • 2
  • 29
  • 56

2 Answers2

3

Using MapParameters enum for making what's the meaning of each index (making it more readable) of sourceArray is legal, however, that's should be valid iff the sourceArray.count is equals to 8 and its sorting is always the same.

You can use it as:

enum MapParameters: Int {
    case midiChannel                = 0,
    sizeOfRepeatingMapPattern       = 1,
    firstMIDIKey                    = 2,
    lastMIDIKey                     = 3,
    middleKey                       = 4,
    referenceMIDIKey                = 5,
    referenceFrequency              = 6,
    keysPerFormalOctave             = 7
}

let sourceArray = ["5", "12", "0", "127", "60", "69", "440.0", "12"]

let firstMIDKeyFromSource = sourceArray[MapParameters.firstMIDIKey.rawValue] // 0
let middleKeyFromSource = sourceArray[MapParameters.middleKey.rawValue] // 60

In fact, I find the code snippet above code smell bad; There are alternatives that should be more optimized, such as: Receiving these values as a Dictionary, OR if -somehow- you have to get them as an array, you might want to map them via a simple model, similar to:

struct MappedParameters {
    // you might want to give them initial values
    // or you can make them as optionals
    var midiChannel = ""
    var sizeOfRepeatingMapPattern = ""
    var firstMIDIKey = ""
    var lastMIDIKey = ""
    var middleKey = ""
    var referenceMIDIKey = ""
    var referenceFrequency = ""
    var keysPerFormalOctave = ""

    init(sourceArray: [String]) {
        if sourceArray.count  != 8 {
            fatalError("ERROR: sourceArray.count is NOT 8")
        }

        midiChannel = sourceArray[0]
        sizeOfRepeatingMapPattern = sourceArray[1]
        firstMIDIKey = sourceArray[2]
        lastMIDIKey = sourceArray[3]
        middleKey = sourceArray[4]
        referenceMIDIKey = sourceArray[5]
        referenceFrequency = sourceArray[6]
        keysPerFormalOctave = sourceArray[7]
    }
}

Usage:

Somewhere in your code you should implement:

let sourceArray = ["5", "12", "0", "127", "60", "69", "440.0", "12"]
let sourceMapper = MappedParameters(sourceArray: sourceArray)

and simply, use sourceMapper as:

print(sourceMapper.firstMIDIKey) // 0
print(sourceMapper.middleKey) // 60

Hope this helped.

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
  • 1
    I agree with @Ahmad F, you only appear to be using the enumeration as part of the data load, and thereafter it just adds complexity. MUCH better to load the data into a simple structure where you can reference sourceMapper.MySpecificValue – Russell Jan 31 '17 at 09:53
  • Ahmed, this certainly helped once I realised the Structure must be outside the Class in order to initialise. This has to be the accepted solution because it infers String (not Int) values which my project code already converts to Int or Double as required. @Fonix you also brought a lot to the discussion. Much appreciated, both! – Greg Feb 01 '17 at 19:26
  • Glad to help :) – Ahmad F Feb 01 '17 at 20:06
1

An alternative to @Ahmad F's answer could be to use static members

struct MapParameters {
    static let midiChannel                     = 0
    static let sizeOfRepeatingMapPattern       = 1
    static let firstMIDIKey                    = 2
    static let lastMIDIKey                     = 3
    static let middleKey                       = 4
    static let referenceMIDIKey                = 5
    static let referenceFrequency              = 6
    static let keysPerFormalOctave             = 7
}

let sourceArray = ["5", "12", "0", "127", "60", "69", "440.0", "12"]

let parameterValue  = sourceArray[MapParameters.referenceFrequency]
Fonix
  • 11,447
  • 3
  • 45
  • 74
  • That leads to: when you need to get an element from `sourceArray`, you have to mention it with the desired index such as `sourceArray[MapParameters.referenceFrequency]`, it's similar to the current case :) working with an object's parameters is more elegant. – Ahmad F Jan 31 '17 at 10:03
  • I had a look at Structures briefly. Both examples are good. The Enum code seemed seemed a little pointless. Before I accept an answer I need time to consider which of these will be the more useful. I sure appreciate the way you all made sense of my question – Greg Jan 31 '17 at 10:47
  • @AhmadF true, but depends if OP would prefer it to act more like an enum or like an object, maybe the indexes are important for other uses as well, eg comparing an enum value in an if statement. – Fonix Jan 31 '17 at 11:32
  • problem showed up when I ran it in a project. `let parameterValue = sourceArray[MapParameters.referenceFrequency]` sent it into debug. So I declared each parameter type and got - `Cannot subscript a value of type '[String]' with an index of type 'Double’` and `Overloads for 'subscript' exist with these partially matching parameter lists: (Int), (Range), (Range), (ClosedRange), (CountableRange), (CountableClosedRange)`. Happened again with the same code in Playgnd even though PG seemed to have no problem inferring different parameter types – Greg Feb 01 '17 at 10:16
  • maybe try explicitly set the type of the variables eg: `static let midiChannel:Int = 0` because it seems to think you are indexing an array with a double instead of an int – Fonix Feb 01 '17 at 15:22
  • yes, I tried an explicit declaration for each variable but as the Overloads message suggested, indexing only works if it is countable i.e. Int (or something similar) which Ahmed's solution works round because it infers String variables. Your solution is ready to work in a situation where only Int values are required. – Greg Feb 01 '17 at 19:37