.shuffle()和.shuffled()是Swift的一部分


原历史问题:

我如何随机或洗牌在Swift数组中的元素?例如,如果我的数组包含52张扑克牌,我想要洗牌数组以洗牌牌组。


当前回答

当我将xCode版本升级到7.4 beta时,它停止在“swap(&self[i], &self[j])”。 致命错误:不支持与自身交换位置

我找到了I = j的原因(交换函数将爆炸)

所以我添加了一个条件,如下所示

if (i != j){
    swap(&list[i], &list[j])
}

丫!对我来说没问题。

其他回答

这是一些在playground上运行的代码。你不需要在实际的Xcode项目中导入Darwin。

import darwin

var a = [1,2,3,4,5,6,7]

func shuffle<ItemType>(item1: ItemType, item2: ItemType) -> Bool {
    return drand48() > 0.5
}

sort(a, shuffle)

println(a)

这是最简单的方法。导入Gamplaykit到你的VC中,使用下面的代码。在Xcode 8中测试。

 import GameplayKit

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

 override func viewDidLoad() {
    super.viewDidLoad()

    print(array.shuffled())  
}

如果你想从数组中获得一个打乱的字符串,你可以使用下面的代码..

func suffleString() {

    let ShuffleArray = array.shuffled()

    suffleString.text = ShuffleArray.first as? String

    print(suffleString.text!)

}

SWIFT 4

func createShuffledSequenceOfNumbers(max:UInt)->[UInt] {

    var array:[UInt]! = []
    var myArray:[UInt]! = []
    for i in 1...max {
        myArray.append(i)
    }
    for i in 1...max {
        array.append(i)
    }
    var tempArray:[Int]! = []
    for index in 0...(myArray.count - 1) {

        var isNotFinded:Bool = true
        while(isNotFinded){

            let randomNumber = arc4random_uniform(UInt32(myArray.count))
            let randomIndex = Int(randomNumber)

            if(!tempArray.contains(randomIndex)){
                tempArray.append(randomIndex)

                array[randomIndex] = myArray[index]
                isNotFinded = false
            }
        }
    }

    return array
}

这是Nate为Swift 4实现的Fisher-Yates shuffle的一个版本 (Xcode 9)。

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)
        }
    }
}

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

这些变化是:

约束indexes . iterator . element == Index现在是部分 的收集协议,并且不需要强加于 扩展了。 交换元素必须通过调用集合上的swapAt()来完成, 添加mutableccollection . swapat(_:_:)。 Element是Iterator.Element的别名。

在Swift 3中,如果你想洗牌一个数组,或者从一个数组中获得一个新的洗牌数组,AnyIterator可以帮助你。其思想是从数组中创建一个索引数组,用AnyIterator实例和swap(_:_:)函数洗牌这些索引,并将AnyIterator实例中的每个元素映射到数组的相应元素。


下面的Playground代码展示了它是如何工作的:

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"]

你可以重构前面的代码,并在Array扩展中创建一个shuffled()函数,以便从数组中获得一个新的洗牌数组:

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] }
    }

}

用法:

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: []

作为前面代码的替代方案,您可以在Array扩展中创建一个shuffle()函数,以便在适当的位置洗牌数组:

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] }
    }

}

用法:

var mutatingArray = ["Jock", "Ellie", "Sue Ellen", "Bobby", "JR", "Pamela"]
mutatingArray.shuffle()
print(mutatingArray) // may print ["Sue Ellen", "Pamela", "Jock", "Ellie", "Bobby", "JR"]