How do I shuffle an array in Swift?

前端 未结 25 2179
长发绾君心
长发绾君心 2020-11-21 05:44

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 o

25条回答
  •  逝去的感伤
    2020-11-21 06:22

    Edit: As noted in other answers, Swift 4.2 finally adds random number generation to the standard library, complete with array shuffling.

    However, the GKRandom / GKRandomDistribution suite in GameplayKit can still be useful with the new RandomNumberGenerator protocol — if you add extensions to the GameplayKit RNGs to conform to the new standard library protocol, you can easily get:

    • sendable RNGs (that can reproduce a "random" sequence when needed for testing)
    • RNGs that sacrifice robustness for speed
    • RNGs that produce non-uniform distributions

    ...and still make use of the nice new "native" random APIs in Swift.

    The rest of this answer concerns such RNGs and/or their use in older Swift compilers.


    There are some good answers here already, as well as some good illustrations of why writing your own shuffle can be error-prone if you're not careful.

    In iOS 9, macOS 10.11, and tvOS 9 (or later), you don't have to write your own. There's an efficient, correct implementation of Fisher-Yates in GameplayKit (which, despite the name, is not just for games).

    If you just want a unique shuffle:

    let shuffled = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: array)
    

    If you want to be able to replicate a shuffle or series of shuffles, choose and seed a specific random source; e.g.

    let lcg = GKLinearCongruentialRandomSource(seed: mySeedValue)
    let shuffled = lcg.arrayByShufflingObjects(in: array)
    

    In iOS 10 / macOS 10.12 / tvOS 10, there's also a convenience syntax for shuffling via an extension on NSArray. Of course, that's a little cumbersome when you're using a Swift Array (and it loses its element type on coming back to Swift):

    let shuffled1 = (array as NSArray).shuffled(using: random) // -> [Any]
    let shuffled2 = (array as NSArray).shuffled() // use default random source
    

    But it's pretty easy to make a type-preserving Swift wrapper for it:

    extension Array {
        func shuffled(using source: GKRandomSource) -> [Element] {
            return (self as NSArray).shuffled(using: source) as! [Element]
        }
        func shuffled() -> [Element] {
            return (self as NSArray).shuffled() as! [Element]
        }
    }
    let shuffled3 = array.shuffled(using: random)
    let shuffled4 = array.shuffled()
    

提交回复
热议问题