How do I shuffle an array in Swift?

2018-12-31 00:43发布

How do I randomize or shuffle the elements within an array in Swift? For example, if my array consists of 52 playing cards, I want to shuffle the array in order to shuffle the deck.

25条回答
不流泪的眼
2楼-- · 2018-12-31 01:06

Taking Nate's algorithm I wanted to see how this would look with Swift 2 and protocol extensions.

This is what I came up with.

extension MutableCollectionType where Self.Index == Int {
    mutating func shuffleInPlace() {
        let c = self.count
        for i in 0..<(c - 1) {
            let j = Int(arc4random_uniform(UInt32(c - i))) + i
            swap(&self[i], &self[j])
        }
    }
}

extension MutableCollectionType where Self.Index == Int {
    func shuffle() -> Self {
        var r = self
        let c = self.count
        for i in 0..<(c - 1) {
            let j = Int(arc4random_uniform(UInt32(c - i))) + i
            swap(&r[i], &r[j])
        }
        return r
    }
}

Now, any MutableCollectionType can use these methods given it uses Int as an Index

查看更多
牵手、夕阳
3楼-- · 2018-12-31 01:07
let shuffl = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: arrayObject)
查看更多
冷夜・残月
4楼-- · 2018-12-31 01:10

With Swift 3, if you want to shuffle an array in place or get a new shuffled array from an array, AnyIterator can help you. The idea is to create an array of indices from your array, to shuffle those indices with an AnyIterator instance and swap(_:_:) function and to map each element of this AnyIterator instance with the array's corresponding element.


The following Playground code shows how it works:

import Darwin // required for arc4random_uniform

let array = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]
var indexArray = Array(array.indices)
var index = indexArray.endIndex

let indexIterator: AnyIterator<Int> = AnyIterator {
    guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex)
        else { return nil }

    index = nextIndex
    let randomIndex = Int(arc4random_uniform(UInt32(index)))
    if randomIndex != index {
        swap(&indexArray[randomIndex], &indexArray[index])
    }

    return indexArray[index]
}

let newArray = indexIterator.map { array[$0] }
print(newArray) // may print: ["Jock", "Ellie", "Sue Ellen", "JR", "Pamela", "Bobby"]

You can refactor the previous code and create a shuffled() function inside an Array extension in order to get a new shuffled array from an array:

import Darwin // required for arc4random_uniform

extension Array {

    func shuffled() -> Array<Element> {
        var indexArray = Array<Int>(indices)        
        var index = indexArray.endIndex

        let indexIterator = AnyIterator<Int> {
            guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex)
                else { return nil }

            index = nextIndex                
            let randomIndex = Int(arc4random_uniform(UInt32(index)))
            if randomIndex != index {
                swap(&indexArray[randomIndex], &indexArray[index])
            }

            return indexArray[index]
        }

        return indexIterator.map { self[$0] }
    }

}

Usage:

let array = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]
let newArray = array.shuffled()
print(newArray) // may print: ["Bobby", "Pamela", "Jock", "Ellie", "JR", "Sue Ellen"]
let emptyArray = [String]()
let newEmptyArray = emptyArray.shuffled()
print(newEmptyArray) // prints: []

As an alternative to the previous code, you can create a shuffle() function inside an Array extension in order to shuffle an array in place:

import Darwin // required for arc4random_uniform

extension Array {

    mutating func shuffle() {
        var indexArray = Array<Int>(indices)
        var index = indexArray.endIndex

        let indexIterator = AnyIterator<Int> {
            guard let nextIndex = indexArray.index(index, offsetBy: -1, limitedBy: indexArray.startIndex)
                else { return nil }

            index = nextIndex                
            let randomIndex = Int(arc4random_uniform(UInt32(index)))
            if randomIndex != index {
                swap(&indexArray[randomIndex], &indexArray[index])
            }

            return indexArray[index]
        }

        self = indexIterator.map { self[$0] }
    }

}

Usage:

var mutatingArray = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]
mutatingArray.shuffle()
print(mutatingArray) // may print ["Sue Ellen", "Pamela", "Jock", "Ellie", "Bobby", "JR"]
查看更多
旧人旧事旧时光
5楼-- · 2018-12-31 01:10

Form the article of Fisher–Yates shuffle on Wikipedia

Swift 3.1,4.0

a). Pencil-and-paper method :

func shuffle<T>(_ array:inout [T]){

    var temp = [T]()

    for _  in array{

        /*Generating random number with length*/
        let random = arc4random_uniform(UInt32(array.count))
        /*Take the element from array*/
        let elementTaken = array[Int(random)]
        /*Append it to new tempArray*/
        temp.append(elementTaken)
        /*Remove the element from array*/
        array.remove(at: Int(random))

    }
    /* array = tempArray*/
    array = temp
}

b). Modern method:(Durstenfeld's version)

func shuffle<T>(_ array:inout [T]){

    var length = array.count

    for _  in array{

        /*Generating random number with length*/
        let random = arc4random_uniform(UInt32(length))
        /*Check before index of two elements not same*/
        if length-1 != Int(random){
            swap(&array[length-1], &array[Int(random)])
        }

        length -= 1
    }
}

Extension:

a). Pencil-and-paper method :

extension Array{

    mutating func shuffled(){

        var temp = [Element]()

        for _  in self{

            /*Generating random number with length*/
            let random = arc4random_uniform(UInt32(self.count))
            /*Take the element from array*/
            let elementTaken = self[Int(random)]
            /*Append it to new tempArray*/
            temp.append(elementTaken)
            /*Remove the element from array*/
            self.remove(at: Int(random))

        }
        /* array = tempArray*/
        self = temp
    }
}

b). Modern method:(Durstenfeld's version)

extension Array{

    mutating func shuffled(){

        var length = self.count

        for _  in self{

            /*Generating random number with length*/
            let random = arc4random_uniform(UInt32(length))

            /*Check before index of two elements not same*/

            if length-1 != Int(random){

                /*Swaping elements, If same index then there is no swap*/
               // swap(&self[length-1], &self[Int(random)]) -> Swift 3.0
                self.swapAt(length-1, Int(random)) //-> Swift 4.0

            }

            length -= 1
        }
    }
}

Reference :

/* By using shuffle functions*/
var a = [1,2,3,4,5,6,7,8,9,10]

for _ in 1...10{

    self.shuffle(&a)

    /*For shuffled extension, a.shuffled()*/
    print(a)
}

Note: You can use empty array also.

Output:

[6, 2, 10, 5, 1, 8, 9, 4, 3, 7]

[7, 1, 9, 8, 2, 10, 5, 6, 4, 3]

[8, 9, 6, 10, 5, 2, 7, 4, 3, 1]

[10, 1, 7, 4, 8, 9, 3, 5, 2, 6]

[8, 1, 6, 9, 3, 7, 4, 5, 10, 2]

[4, 3, 7, 9, 1, 5, 8, 6, 10, 2]

[7, 3, 4, 9, 10, 1, 6, 5, 2, 8]

[3, 6, 2, 4, 5, 8, 9, 7, 1, 10]

[5, 1, 2, 10, 6, 9, 7, 3, 8, 4]

[7, 9, 3, 8, 2, 1, 5, 4, 6, 10]

Please let me know if any queries, Other Swift version will be check soon.

查看更多
后来的你喜欢了谁
6楼-- · 2018-12-31 01:11

Here's something possibly a little shorter:

sorted(a) {_, _ in arc4random() % 2 == 0}
查看更多
伤终究还是伤i
7楼-- · 2018-12-31 01:11

This is how its done in a Simplest way.import Gamplaykit to your VC and use the below code. Tested in Xcode 8.

 import GameplayKit

 let array: NSArray = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]

 override func viewDidLoad() {
    super.viewDidLoad()

    print(array.shuffled())  
}

If you want to get a shuffled String from an Array you can use below code..

func suffleString() {

    let ShuffleArray = array.shuffled()

    suffleString.text = ShuffleArray.first as? String

    print(suffleString.text!)

}
查看更多
登录 后发表回答