Anton Zimin
Anton Zimin

Reputation: 101

Tuple array in SWIFT as a parameter of function

I need to pass the tuple arrays as parameters for a function that selects the random tuple from the arrays considering also two other parameters) This function returns the index, that I will use then to play this random file. And this function is in the class fileManager()

class fileManager  {
    var drums = [
        ("drum", "drum", 8, 2, false, 153, "C", 1),
        ("drum", "drum2", 6, 3, false, 153, "C", 1),
        ("drum", "drum3", 8, 2, false, 153, "C", 1),
        ("drum", "drum4", 4, 2, false, 153, "C", 1),
        ("drum", "drum5", 8, 1, false, 153, "C", 1)  ]

    var piano = [
        ("piano", "piano", 8, 2, false, 153, "C", 1),
        ("piano", "piano2", 8, 3, false, 153, "C", 1),
        ("piano", "piano3", 8, 1, false, 153, "C", 1)
    ]

    //instrumentArray will be any instrument, Strength - we need to find, KeyNote - we need to find in Array.

    func randomizeTheNextInstrument (instrumentArrayInput: [(String,String,Int,Int,Bool,Int,String, Int)], Strength: Int , KeyNote: String) -> Int {
        var instrumentArray = instrumentArrayInput
        var indexInstrument: Int!
        for index in 0...instrumentArray.count {
            if instrumentArray[index].4 == true { //check if played
                instrumentArray.removeAtIndex(index)
            }
            if instrumentArray[index].6 != KeyNote { // check keyNote
                instrumentArray.removeAtIndex(index)
            }
            if instrumentArray[index].3 != Strength { // check strength
                instrumentArray.removeAtIndex(index)
            }
        }

        var indexToChoose: Int = Int(arc4random_uniform(UInt32(instrumentArray.count)))
        for index in 0...instrumentArrayInput.count {
            if instrumentArrayInput[index].1 == instrumentArray[indexToChoose].1 { // finds what one in ArrayInpu equals the randomized one in Array that chosen
                indexInstrument = index
            }
        }
        return indexInstrument
    }
}

But when I call the function from another class by doing this.

indexToPlay = Int(fileManager().randomizeTheNextInstrument(fileManager().drums, Strength: drumStrength, KeyNote: "C"))

It gives me fatal error: "Array index out of range (lldb)" It writes me that the array instrumentArrayInput has 5 values, but values are 'none' and instrumentArray has 3 values, but values are 'none'. The next thing, which looks strange is indexToChoose = 140734791422096. And it also writes me EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, sub code=0x0) for the string with:

if instrumentArray[index].4 == true { 

What's I doing wrong?

Upvotes: 2

Views: 2928

Answers (2)

David Berry
David Berry

Reputation: 41246

This:

for index in 0...instrumentArray.count {

should be:

for index in 0..<instrumentArray.count {

Since arrays are zero-indexed, the last valid index is one less than the count of items in the array.

Anthony actually points out several more issues with your function, all of which would fixed by just using the built-in filter function:

instrumentArray = instrumentArrayInput.filter {
    return !$0.4 && $0.6 == KeyNote && $0.3 == Strength
}

to replace the entire for loop.

To fix more of the problems, I'd go with something like this, although there's more changes to be recommended (like using a struct or class instead of a tuple):

class FileManager  {
    typealias Instrument = (String,String,Int,Int,Bool,Int,String, Int)

    var drums : [Instrument] = [
        ("drum", "drum", 8, 2, false, 153, "C", 1),
        ("drum", "drum2", 6, 3, false, 153, "C", 1),
        ("drum", "drum3", 8, 2, false, 153, "C", 1),
        ("drum", "drum4", 4, 2, false, 153, "C", 1),
        ("drum", "drum5", 8, 1, false, 153, "C", 1)  
    ]

    var piano : [Instrument] = [
        ("piano", "piano", 8, 2, false, 153, "C", 1),
        ("piano", "piano2", 8, 3, false, 153, "C", 1),
        ("piano", "piano3", 8, 1, false, 153, "C", 1)
    ]

    func randomizeTheNextInstrument(instrumentArrayInput: [Instrument], Strength: Int, KeyNote: String) -> Int? {
        let instrumentArray = instrumentArrayInput.filter {
            return !$0.4 && $0.6 == KeyNote && $0.3 == Strength
        }

        if instrumentArray.count <= 0 {
            return nil
        }

        let indexToChoose: Int = Int(arc4random_uniform(UInt32(instrumentArray.count - 1)))
        let name = instrumentArray[indexToChoose].1

        for index in 0 ..< instrumentArrayInput.count {
            if name == instrumentArrayInput[index].1 {
                return index
            }
        }

        return nil
    }
}

let fileManager = FileManager()

if let indexToPlay = fileManager.randomizeTheNextInstrument(fileManager.drums, Strength: 3, KeyNote: "C") {
    println("\(fileManager.drums[indexToPlay])")
    fileManager.drums[indexToPlay].4 = true
}

if let indexToPlay = fileManager.randomizeTheNextInstrument(fileManager.drums, Strength: 3, KeyNote: "C") {
    println("\(fileManager.drums[indexToPlay])")
    fileManager.drums[indexToPlay].4 = true
}

Upvotes: 1

Antonio
Antonio

Reputation: 72810

In the loop you are removing elements from the array, so its length decreases. Moreover, there are 3 independent if statements, which can cause up to 3 elements to be removed in the same iteration. I would either add a break in each if body (to skip the current iteration and move to the next), or reorganize them as if/else if/else.

However to fix the exception, rather than removing elements from the array I would suggest adding the elements to keep in a 2nd array, or just their indexes.

Last, note that the loop is wrong, it should be for index in 0..<instrumentArray.count and not for index in 0...instrumentArray.count


Update

If you still need to keep the original idea of removing elements from the array, then you should modify the loop to traverse it in reverse order, and use if/elseif rather than standalone if. For instance:

for var index = instrumentArray.count - 1; index >= 0; --index {
    if instrumentArray[index].4 == true { //check if played
        instrumentArray.removeAtIndex(index)
    } else if instrumentArray[index].6 != KeyNote { // check keyNote
        instrumentArray.removeAtIndex(index)
    } else if instrumentArray[index].3 != Strength { // check strength
        instrumentArray.removeAtIndex(index)
    }
}

The loop in reverse order is needed because when an element is removed, all elements after it will be shifted back by one position, whereas all preceding it won't be affected. This way you are sure that all elements still to process won't have their index changed.

Upvotes: 1

Related Questions