-1

I need to randomize an array for an app I am trying to build in swift, and to test the function I created I put it into a playground with a sample array. I realize that this question has been asked here: How do I shuffle an array in Swift? but I was just wondering why this specific way is not working. Here is my code:

import UIKit

var arrayOne : [String] = ["", "", "", "", ""]

func randomizeArray(array : [String]) -> [String] {
    var randomizedArray : [String] = []
    var copyOfArray : [String] = array
    repeat {
        let arrayCount : Int = array.count - 1
        let randomElement : Int = Int(arc4random_uniform(UInt32(arrayCount)))
        let arraySlice : String = array[randomElement]
        randomizedArray.append(arraySlice)
        copyOfArray.remove(at : randomElement)
    } while array.count > 0
    return randomizedArray
}

print(randomizeArray(array : arrayOne))

When I try to print the function using array one, it says "Fatal error: index is out of range." Any ideas why this happened?

Community
  • 1
  • 1
Liam Keeley
  • 187
  • 1
  • 10
  • 2
    For an array with 5 elements you could easily *debug* the problem. Single-step the code in Xcode and you'll see quickly where the problem is. – Martin R Nov 01 '16 at 19:33
  • 2
    And then get rid of all the unnecessary type annotations ... – Martin R Nov 01 '16 at 19:37

2 Answers2

2

It looks like there are a few issues.

First, it's a good idea to generally stay away from repeat-while. That paradigm is generally much more confusing than a simple while loop.

Second, your randomizer is leaving out the last item of the array.

Third, You're combining the usage of array and copyOfArray in an unpredictable way

try this:

var arrayOne : [String] = ["", "", "", "", ""]

func randomizeArray(array : [String]) -> [String] {
    var randomizedArray : [String] = []
    var copyOfArray : [String] = array
    while !copyOfArray.isEmpty {
        let arrayCount : Int = copyOfArray.count
        let randomElement : Int = Int(arc4random_uniform(UInt32(arrayCount)))
        let arraySlice : String = copyOfArray[randomElement]
        randomizedArray.append(arraySlice)
        copyOfArray.remove(at : randomElement)
    }
    return randomizedArray
}



print(randomizeArray(array : arrayOne))
GetSwifty
  • 7,568
  • 1
  • 29
  • 46
  • That worked. Might be slower than the for in way of doing it because it has to keep calculating random numbers, but it is easier to understand so from the perspective of a beginning programer like me it might be better. – Liam Keeley Nov 02 '16 at 00:47
  • It will be slower because it is creating a new array rather than swapping items in the original array. – GetSwifty Nov 02 '16 at 19:43
1

When I try to print the function using array one, it says "Fatal error: index is out of range." Any ideas why this happened?

I'll answer this question.

The property arrayCount (namely, array.count) is fixed within the loop: in your example, fixed to 4. This means the loop will never terminate (array.count > 0 always true). Furthermore, since you use this number as an upper bound in your random number generation, you might very well get values of 3. Lets assume in the short analysis that follows that randomElement always has the value 3 (and note that randomIndex would've been a more suitable name).

The initial size of copyOfArray is 5, allowing index access at most at copyOfArray[4]. After two iterations, the size of copyOfArray is 3 (due to the removal of elements at copyOfArray.remove(at: randomElement)), which means, at this point, copyOfArray[3] is would be attempting to access an index out of bounds. In another iteration, even copyOfArray[2] is out of bounds. Since the loop will never terminate, it's deemed to crash by index out of bounds runtime exception sooner or later, as copyOfArray grows smaller (towards an empty array, []), meanwhile copyOfArray.remove(at: randomElement) keeps trying to remove indices in the range 0...3.

Declaration

mutating func remove(at index: Int) -> Element

Parameters

index

The position of the element to remove. index must be a valid index of the array.

dfrib
  • 70,367
  • 12
  • 127
  • 192