从数组中选择一个随机元素

问题描述 投票:166回答:15

假设我有一个数组,我想随机选择一个元素。

最简单的方法是什么?

显而易见的方式是array[random index]。但也许有像ruby的array.sample?或者如果没有,可以使用扩展程序创建这样的方法?

arrays swift random
15个回答
291
投票

Swift 4.2及以上版本

新推荐的方法是内置方法:randomElement()。它返回一个可选项以避免我之前假设的空案例。

let array = ["Frodo", "Sam", "Wise", "Gamgee"]
print(array.randomElement()!) // Using ! knowing I have array.count > 0

如果您不创建数组并且不保证计数> 0,您应该执行以下操作:

if let randomElement = array.randomElement() { 
    print(randomElement)
}

Swift 4.1及以下版本

只是回答你的问题,你可以这样做以实现随机数组选择:

let array = ["Frodo", "sam", "wise", "gamgee"]
let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
print(array[randomIndex])

铸件是丑陋的,但我相信它们是必需的,除非别人有另一种方式。


2
投票

斯威夫特3

导入GameKit

func getRandomMessage() -> String {

    let messages = ["one", "two", "three"]

    let randomNumber = GKRandomSource.sharedRandom().nextInt(upperBound: messages.count)

    return messages[randomNumber].description

}

2
投票

Swift 3 - 简单易用。

  1. 创建数组 var arrayOfColors = [UIColor.red, UIColor.yellow, UIColor.orange, UIColor.green]
  2. 创建随机颜色 let randomColor = arc4random() % UInt32(arrayOfColors.count)
  3. 将该颜色设置为您的对象 your item = arrayOfColors[Int(randomColor)]

以下是使用随机SpriteKit更新SKLabelNodeString项目的示例:

    let array = ["one","two","three","four","five"]

    let randomNumber = arc4random() % UInt32(array.count)

    let labelNode = SKLabelNode(text: array[Int(randomNumber)])

2
投票

如果您希望能够从阵列中获得多个随机元素而不重复,GameplayKit可以满足您的需求:

import GameplayKit
let array = ["one", "two", "three", "four"]

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

let firstRandom = shuffled[0]
let secondRandom = shuffled[1]

您有几个随机选择,请参阅GKRandomSource

GKARC4RandomSource类使用类似于arc4random C函数族中使用的算法。 (但是,此类的实例独立于对arc4random函数的调用。)

GKLinearCongruentialRandomSource类使用的算法比GKARC4RandomSource类更快,但随机性更低。 (具体而言,生成数字的低位比高位重复更频繁。)当性能比强大的不可预测性更重要时,请使用此源。

GKMersenneTwisterRandomSource类使用的算法比GKARC4RandomSource类慢,但更随机。当您使用随机数不显示重复模式并且性能不太重要时,请使用此源。


1
投票

我发现使用GameKit的GKRandomSource.sharedRandom()最适合我。

import GameKit

let array = ["random1", "random2", "random3"]

func getRandomIndex() -> Int {
    let randomNumber = GKRandomSource.sharedRandom().nextIntWithUpperBound(array.count)
    return randomNumber

或者您可以在所选的随机索引处返回对象。确保该函数首先返回一个String,然后返回该数组的索引。

    return array[randomNumber]

精炼到位。


1
投票

现在Collection上有一个内置方法:

let foods = ["🍕", "🍔", "🍣", "🍝"]
let myDinner = foods.randomElement()

如果你想从集合中提取最多n随机元素,你可以添加像这样的扩展:

extension Collection {
    func randomUniqueElements(_ count: Int) -> [Element] {
        var shuffledIterator = shuffled().makeIterator()
        return (0..<count).compactMap { _ in shuffledIterator.next() }
    }
}

如果你想要它们是唯一的,你可以使用Set,但集合的元素必须符合Hashable协议:

extension Collection where Element: Hashable {
    func randomUniqueElements(_ count: Int) -> [Element] {
        var shuffledIterator = Set(shuffled()).makeIterator()
        return (0..<count).compactMap { _ in shuffledIterator.next() }
    }
}

0
投票

最新的swift3代码尝试它的工作正常

 let imagesArray = ["image1.png","image2.png","image3.png","image4.png"]

        var randomNum: UInt32 = 0
        randomNum = arc4random_uniform(UInt32(imagesArray.count))
        wheelBackgroundImageView.image = UIImage(named: imagesArray[Int(randomNum)])

-2
投票

我使用Swift 4.2中引入的新功能找到了一种非常不同的方法。

// 👇🏼 - 1 
public func shufflePrintArray(ArrayOfStrings: [String]) -> String {
// - 2 
       let strings = ArrayOfStrings
//- 3
       var stringans =  strings.shuffled()
// - 4
        var countS = Int.random(in: 0..<strings.count)
// - 5
        return stringans[countS] 
}


  1. 我们声明了一个带有参数的函数,它带有一个字符串数组并返回一个String。
  2. 然后我们将ArrayOfStrings放在一个变量中。
  3. 然后我们调用shuffled函数并将其存储在变量中。 (仅在4.2中支持)
  4. 然后我们声明一个变量,它保存String总计数的混洗值。
  5. 最后,我们在countS的索引值处返回混洗字符串。

它基本上是对字符串数组进行混洗,然后随机选择总计数的数量,然后返回混洗数组的随机索引。


136
投票

关于Lucas所说的,你可以像这样创建一个Array类的扩展:

extension Array {
    func randomItem() -> Element? {
        if isEmpty { return nil }
        let index = Int(arc4random_uniform(UInt32(self.count)))
        return self[index]
    }
}

例如:

let myArray = [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16]
let myItem = myArray.randomItem() // Note: myItem is an Optional<Int>

45
投票

Swift 4版本:

extension Collection where Index == Int {

    /**
     Picks a random element of the collection.

     - returns: A random element of the collection.
     */
    func randomElement() -> Iterator.Element? {
        return isEmpty ? nil : self[Int(arc4random_uniform(UInt32(endIndex)))]
    }

}

21
投票

在Swift 2.2中,这可以推广,以便我们有:

UInt.random
UInt8.random
UInt16.random
UInt32.random
UInt64.random
UIntMax.random

// closed intervals:

(-3...3).random
(Int.min...Int.max).random

// and collections, which return optionals since they can be empty:

(1..<4).sample
[1,2,3].sample
"abc".characters.sample
["a": 1, "b": 2, "c": 3].sample

首先,为randoms实现静态UnsignedIntegerType属性:

import Darwin

func sizeof <T> (_: () -> T) -> Int { // sizeof return type without calling
    return sizeof(T.self)
}

let ARC4Foot: Int = sizeof(arc4random)

extension UnsignedIntegerType {
    static var max: Self { // sadly `max` is not required by the protocol
        return ~0
    }
    static var random: Self {
        let foot = sizeof(Self)
        guard foot > ARC4Foot else {
            return numericCast(arc4random() & numericCast(max))
        }
        var r = UIntMax(arc4random())
        for i in 1..<(foot / ARC4Foot) {
            r |= UIntMax(arc4random()) << UIntMax(8 * ARC4Foot * i)
        }
        return numericCast(r)
    }
}

然后,对于ClosedIntervals与UnsignedIntegerType界限:

extension ClosedInterval where Bound : UnsignedIntegerType {
    var random: Bound {
        guard start > 0 || end < Bound.max else { return Bound.random }
        return start + (Bound.random % (end - start + 1))
    }
}

然后(更多参与),对于ClosedIntervals与SignedIntegerType界限(使用下面进一步描述的辅助方法):

extension ClosedInterval where Bound : SignedIntegerType {
    var random: Bound {
        let foot = sizeof(Bound)
        let distance = start.unsignedDistanceTo(end)
        guard foot > 4 else { // optimisation: use UInt32.random if sufficient
            let off: UInt32
            if distance < numericCast(UInt32.max) {
                off = UInt32.random % numericCast(distance + 1)
            } else {
                off = UInt32.random
            }
            return numericCast(start.toIntMax() + numericCast(off))
        }
        guard distance < UIntMax.max else {
            return numericCast(IntMax(bitPattern: UIntMax.random))
        }
        let off = UIntMax.random % (distance + 1)
        let x = (off + start.unsignedDistanceFromMin).plusMinIntMax
        return numericCast(x)
    }
}

...其中unsignedDistanceTounsignedDistanceFromMinplusMinIntMax辅助方法可以实现如下:

extension SignedIntegerType {
    func unsignedDistanceTo(other: Self) -> UIntMax {
        let _self = self.toIntMax()
        let other = other.toIntMax()
        let (start, end) = _self < other ? (_self, other) : (other, _self)
        if start == IntMax.min && end == IntMax.max {
            return UIntMax.max
        }
        if start < 0 && end >= 0 {
            let s = start == IntMax.min ? UIntMax(Int.max) + 1 : UIntMax(-start)
            return s + UIntMax(end)
        }
        return UIntMax(end - start)
    }
    var unsignedDistanceFromMin: UIntMax {
        return IntMax.min.unsignedDistanceTo(self.toIntMax())
    }
}

extension UIntMax {
    var plusMinIntMax: IntMax {
        if self > UIntMax(IntMax.max) { return IntMax(self - UIntMax(IntMax.max) - 1) }
        else { return IntMax.min + IntMax(self) }
    }
}

最后,对于所有Index.Distance == Int集合:

extension CollectionType where Index.Distance == Int {
    var sample: Generator.Element? {
        if isEmpty { return nil }
        let end = UInt(count) - 1
        let add = (0...end).random
        let idx = startIndex.advancedBy(Int(add))
        return self[idx]
    }
}

...可以针对整数Ranges进行优化:

extension Range where Element : SignedIntegerType {
    var sample: Element? {
        guard startIndex < endIndex else { return nil }
        let i: ClosedInterval = startIndex...endIndex.predecessor()
        return i.random
    }
}

extension Range where Element : UnsignedIntegerType {
    var sample: Element? {
        guard startIndex < endIndex else { return nil }
        let i: ClosedInterval = startIndex...endIndex.predecessor()
        return i.random
    }
}

18
投票

你可以使用Swift的内置random()函数来扩展:

extension Array {
    func sample() -> Element {
        let randomIndex = Int(rand()) % count
        return self[randomIndex]
    }
}

let array = [1, 2, 3, 4]

array.sample() // 2
array.sample() // 2
array.sample() // 3
array.sample() // 3

array.sample() // 1
array.sample() // 1
array.sample() // 3
array.sample() // 1

9
投票

另一个Swift 3的建议

private extension Array {
    var randomElement: Element {
        let index = Int(arc4random_uniform(UInt32(count)))
        return self[index]
    }
}

4
投票

跟随其他人回答,但有Swift 2的支持。

Swift 1.x

extension Array {
    func sample() -> T {
        let index = Int(arc4random_uniform(UInt32(self.count)))
        return self[index]
    }
}

Swift 2.x

extension Array {
    func sample() -> Element {
        let index = Int(arc4random_uniform(UInt32(self.count)))
        return self[index]
    }
}

例如:

let arr = [2, 3, 5, 7, 9, 11, 13, 17, 19, 23, 29, 31]
let randomSample = arr.sample()

2
投票

检查空数组的另一种功能实现。

func randomArrayItem<T>(array: [T]) -> T? {
  if array.isEmpty { return nil }
  let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
  return array[randomIndex]
}

randomArrayItem([1,2,3])

2
投票

这是Arrays的扩展,带有空数组检查以提高安全性:

extension Array {
    func sample() -> Element? {
        if self.isEmpty { return nil }
        let randomInt = Int(arc4random_uniform(UInt32(self.count)))
        return self[randomInt]
    }
}

你可以像这样简单地使用它:

let digits = Array(0...9)
digits.sample() // => 6

如果您更喜欢具有更多便利功能的框架,请查看HandySwift。您可以通过Carthage将其添加到项目中,然后使用它与上面的示例完全相同:

import HandySwift    

let digits = Array(0...9)
digits.sample() // => 8

此外,它还包括一次获取多个随机元素的选项:

digits.sample(size: 3) // => [8, 0, 7]
© www.soinside.com 2019 - 2024. All rights reserved.