Shuffle array swift 3

swift, swift3

Solution

`count` returns an `IndexDistance` which is the type describing the distance between two collection indices. `IndexDistance` is required to be a `SignedInteger`, but need not be an `Int` and can be different from `Index`. Therefore it is not possible to create the range `0..<count - 1`.

A solution is to use `startIndex` and `endIndex` instead of `0` and `count`:

extension MutableCollection where Index == Int {
    /// Shuffle the elements of `self` in-place.
    mutating func shuffle() {
        // empty and single-element collections don't shuffle
        if count < 2 { return }

        for i in startIndex ..< endIndex - 1 {
            let j = Int(arc4random_uniform(UInt32(endIndex - i))) + i
            if i != j {
                swap(&self[i], &self[j])
            }
        }
    }
}

Another advantage is that this also works correctly with array slices (where the index of the first element is not necessarily zero).

Note that according to the new "Swift API Design Guidelines", `shuffle()` is the "proper" name for a mutating shuffle method, and `shuffled()` for the non-mutating counterpart which returns an array:

extension Collection {
    /// Return a copy of `self` with its elements shuffled
    func shuffled() -> [Iterator.Element] {
        var list = Array(self)
        list.shuffle()
        return list
    }
}

Update: A (even more general) Swift 3 version has been added to How do I shuffle an array in Swift? in the meantime.

For Swift 4 (Xcode 9) one has to replace the call to the `swap()` function by a call to the `swapAt()` method of the collection. Also the restriction on the `Index` type is no longer needed:

extension MutableCollection {
    /// Shuffle the elements of `self` in-place.
    mutating func shuffle() {
        for i in indices.dropLast() {
            let diff = distance(from: i, to: endIndex)
            let j = index(i, offsetBy: numericCast(arc4random_uniform(numericCast(diff))))
            swapAt(i, j)
        }
    }
}

See SE-0173 Add `MutableCollection.swapAt(_:_:)` for more information about `swapAt`.

As of Swift 4.2 (Xcode 10, currently in beta), with the implementation of SE-0202 Random Unification, `shuffle()` and `shuffled()` are part of the Swift standard library.

Problem

How can I convert the function below to to `swift 3`? Currently getting a `Binary operator '..<' cannot be applied to operands of type 'Int' and 'Self.IndexDistance'` error. ``` extension MutableCollection where Index == Int { /// Shuffle the elements of `self` in-place. mutating func shuffleInPlace() { // empty and single-element collections don't shuffle if count < 2 { return } for i in 0..<count - 1 { //error takes place here let j = Int(arc4random_uniform(UInt32(count - i))) + i guard i != j else { continue } swap(&self[i], &self[j]) } } } ``` reference: https://stackoverflow.com/a/24029847/5222077

Original source

Related problems