Obito
Obito

Reputation: 79

index(where:) method in swift is producing the wrong index

I have received an array index out of range error. I have two arrays cardsCurrentlyInPlay and currentCardsSelected. Every card that is in this game has a unique ID. I am attempting to find find the index of the card in cardsCurrentlyInPlay whose cardID matches the cardID of the card in currentCardsSelected. I am doing this by using the index(where:) method that takes a closure. My closure just checks if the IDs match, they obviously match because I am using the ! to unwrap them and the app does not crash there. It seems as though the index(where:) method is returning the wrong index. I have looked at this for hours and I do not understand whats going on.

Heres the code:

    let indexOfFirstCard = cardsCurrentlyInPlay.index(where: ({($0?.cardID == currentCardsSelected[0].cardID)}))!
    let indexOfSecondCard = cardsCurrentlyInPlay.index(where: ({($0?.cardID == currentCardsSelected[1].cardID)}))!
    let indexOfThirdCard = cardsCurrentlyInPlay.index(where: ({($0?.cardID == currentCardsSelected[2].cardID)}))!

    if deck.isEmpty && selectedCardsMakeASet() {

        /* Remove the old cards */
        cardsCurrentlyInPlay.remove(at: indexOfFirstCard)
        cardsCurrentlyInPlay.remove(at: indexOfSecondCard)
        cardsCurrentlyInPlay.remove(at: indexOfThirdCard) // where code is blowing up

        currentCardsSelected.removeAll()

        /* Return indicies of cards to clear from the UI */
        return .deckIsEmpty(indexOfFirstCard, indexOfSecondCard, indexOfThirdCard)

    }

Here is where I am getting an index out of range error

Each card has a unique ID, so my second card in <code>currentCardsSelected</code> has an ID of 47. I base the closure that I pass into the index function (see first picture) based off this ID. Basically, find the index of the card in <code>cardsCurrentlyInPlay</code> whose cardID matches the cardID of the card in <code>currentCardsSelected</code>. Either I have completely lost my mind or the index(where:) method is returning the wrong index.

Upvotes: 0

Views: 298

Answers (2)

Gavin Tsang
Gavin Tsang

Reputation: 79

This is because of out of bounds.

After the first two code excuted. cardsCurrentlyInPlay.remove(at: indexOfFirstCard) & cardsCurrentlyInPlay.remove(at: indexOfSecondCard) there is only one element in the cardsCurrentlyInPlay . Then if you excute cardsCurrentlyInPlay.remove(at: indexOfThirdCard), the program will crash.

Upvotes: 0

Paul Cantrell
Paul Cantrell

Reputation: 9324

The index you’re getting is correct when your get it, but it becomes wrong when you remove other cards. Consider:

var a = ["x", "y", "z"]
let indexOfX = a.index(of: "x")!  // returns 0
let indexOfZ = a.index(of: "z")!  // returns 2
a.remove(at: indexOfX)  // removes "x"; now a = ["y", "z"]
a.remove(at: indexOfZ)  // index 2 is now out of bounds

You could interleave the calls to index(of:) and remove(at:), but a better approach would be to remove all three cards in a single pass, something like this:

let selectedCardIDs = currentCardsSelected.map { $0.cardID }
cardsCurrentlyInPlay = cardsCurrentlyInPlay.filter { card in
    !selectedCardIDs.contains(card.cardID)
}

Note that this has the added benefit of avoiding the force unwrap, a sign of sounder logic.

Upvotes: 3

Related Questions