richc
richc

Reputation: 1678

RemoveAtIndex crash from swift array

I have an array of letters, and want to match the characters against the letters and then do something to that letter (in this case turn it yellow) and then remove that matched character from the characters array.

If I have a word1 like "recruitment" and specialLetters like "ment" the removeAtIndex works fine, but in the below example which includes 2 s's in [ness] I get this crash:

fatal error: Array index out of range

Reading other posts on here suggest it is dangerous to remove items from an array when in use, but how come it works ok with some words and not others? I thought enumerating the array would only give each letter one index? And any suggestion on how to fix it so it works for all types of characters?

var letters = Array(word1) // [r,a,n,d,o,m,n,e,s,s]
var characters = Array(specialLetters) // [n,e,s,s]

// delete the special letters
for (index, element) in enumerate(characters) {
    if letter == element {
        tile.letterLabel.textColor = UIColor.yellowColor()

        // remove that character from the array so can't be matched twice
        characters.removeAtIndex(index)
    }
}

Upvotes: 1

Views: 2541

Answers (5)

Ivan
Ivan

Reputation: 515

For this purpose (removing elements of an array), you may want to use an indexing generator. Let me give an example:

var array = [1, 2, 3, 4]
var ig = array.generate()
var index = 0
var element = ig.next()
while element != nil {
    if element == 2 {
        array.removeAtIndex(index)
    } else {
        index = index + 1
    }
    element = ig.next()
}

Upvotes: 0

richc
richc

Reputation: 1678

just fixed it after reading the SDs post here: Swift buttons hidden fatal error: Array index out of range

I updated this question in case it helps anyone else.

I added an if statement to check the index was still in range. Not really sure why this works but it does :-)

var letters = Array(word1) // [r,a,n,d,o,m,n,e,s,s]
var characters = Array(specialLetters) // [n,e,s,s]

// delete the special letters
for (index, element) in enumerate(characters) {
     if letter == element {
           tile.letterLabel.textColor = UIColor.yellowColor()

           // remove that character from the array so can't be matched twice.  
           // first check that the index is still in range. 
           if index < characters.count { // added this if statement
                characters.removeAtIndex(index)
           }
     }
}

Upvotes: 0

vadian
vadian

Reputation: 285072

Yes it is very dangerous to modify the number of items in an array while being enumerated.

Imagine you have an array of three items, the range is 0...2. In the first iteration you delete the first item, then array[2] is now array[1] and there is no item at index 2 any more.

Either you enumerate the array backwards, so the index of the removed item is always equal to or higher than the current index or you use a variable to collect the indexes to be deleted and delete the characters by range.

Of course Swift has more reliable functions to accomplish this task as mentioned by the others therefore it's not needed to use enumerate In this case.

Upvotes: 1

Luca Angeletti
Luca Angeletti

Reputation: 59496

So you have an array of Character(s):

let letters = Array("randomness")

An array of special Character(s)

let specials = Array("ness")

And you want to remove from letters, the specials right?

Here it is:

let set = Set(specials)
let filtered = letters.filter { !set.contains($0) }

filtered // ["r", "a", "d", "o", "m"]

Update

This version keep also consider the occurrences of a char

let letters = Array("randomness")
let specials = Array("ness")

var occurrencies = specials.reduce([Character:Int]()) { (var dict, char) in
    dict[char] = (dict[char] ?? 0) + 1
    return dict
}

let filtered = letters.filter {
    if let num = occurrencies[$0] where num > 0 {
        occurrencies[$0] = num - 1
        return false
    } else {
        return true
    }
}

filtered // ["r", "a", "d", "o", "m", "n"]

Upvotes: 1

Korpel
Korpel

Reputation: 2458

May i suggest a different approach where instead of removing from the initial array you make a new one, from which you can exclude the characters you don't want to appear in your array in the first place.

PS: keep in mind the following code works with Swift 2.0 Xcode 7 beta 6

var letters : [Character] = ["a","b", "c"]

var lettersToBeRemoved : [Character] = ["a"]

var newLetters : [Character] = []

for i in letters
{
    for j in lettersToBeRemoved
    {
        if i == j
        {
            continue
        }
            newLetters.append(i)
    }

}
print(newLetters)

Upvotes: 0

Related Questions