2

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?

1
  • if an answer did solve your problem, remember to accept it - other devs may have the same problem, and knowing how you solved it can help a lot. Commented Feb 8, 2015 at 15:30

2 Answers 2

1

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

7 Comments

Note that fixing this for loop won't fix the multiple other issues in the routine, including continuously recreating fileManager which means there will be no continuity, so essentially played will never be true, the same bounds problem is in the second loop, the same (or similar) bounds problem is in the random number selection, etc.
David, thank you very much for the comment! What is your recommendation then?
Also, I've tried to find good reference for built-in filter, but didn't find. I understand the main idea, but what dads all these !$0.4 && $0.6 == KeyNote && $0.3 == Strength mean? should it be ! $0.6 == KeyNote && $0.3 == Strength instead, cause keyNot is the 6th value in the tuple
$0 is the first argument to the closure, in the case of filter it means that the closure will be invoked with $0 set to each of the tuples in the array. After that it's just a simple boolean expression that combines the 3 conditions you had as separate if statements into a single boolean expression that, I think, leaves the same items in the new array. ie., you want to keep the items in the list that have not been played !$0.3 and have the requested keynote $0.6 == KeyNote and strength $0.4 == Strength
And here's the Apple reference page for filter
|
1

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.

1 Comment

Thank you! But the idea is to remove all the instruments that are not corresponding to all the parameters and randomly choose one among the rest.

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.