Reputation: 101
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
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
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