如何从 Swift 中的集合中获取随机元素?

How to get random element from a set in Swift?

从 Swift 1.2 开始,Apple 引入了 Set 集合类型。

说,我有一组像:

var set = Set<Int>(arrayLiteral: 1, 2, 3, 4, 5)

现在我想从中取出一个随机元素。问题是如何? Set 不像 Array 那样提供 subscript(Int)。相反,它有 subscript(SetIndex<T>)。但首先,SetIndex<T> 没有可访问的初始化器(因此,我不能只用我需要的偏移量创建索引),其次,即使我可以获得集合中第一个元素的索引(var startIndex = set.startIndex) 那么我可以到达第 N 个索引的唯一方法是通过连续调用 successor().

因此,我目前只能看到 2 个选项,既丑又贵:

我是否错过了其他方式?

如果您想要 Set 中的 'random' 元素,那么您可以使用:

/// A member of the set, or `nil` if the set is empty.
var first: T? { get }

获取第 0 个索引或第 1,000,000 个索引没有区别 - 它们都是任意对象。

但是,如果您想要重复调用 每次 return 一个可能不同的元素,那么 first 可能不符合要求。

可能最好的方法是 advance,它会为您走 successor

func randomElementIndex<T>(s: Set<T>) -> T {
    let n = Int(arc4random_uniform(UInt32(s.count)))
    let i = advance(s.startIndex, n)
    return s[i]
}

(编辑:嘿;注意到你实际上更新了问题以包含这个答案,然后我将它添加到我的答案中......好吧,这仍然是一个好主意,我也学到了一些东西。:D)

你也可以遍历集合而不是索引(这是我的第一个想法,但后来我想起来了advance)。

func randomElement<T>(s: Set<T>) -> T {
    let n = Int(arc4random_uniform(UInt32(s.count)))
    for (i, e) in enumerate(s) {
        if i == n { return e }
    }
    fatalError("The above loop must succeed")
}
extension Set {
    func randomElement() -> Element? {
        return count == 0 ? nil : self[advance(self.startIndex, Int(arc4random()) % count)]
    }
}

根据上面的评论重新 Swift 更新,对 Set 的扩展使用了一个小改动:

func randomElement() -> Element?
{
    let randomInt = Int(arc4random_uniform(UInt32(self.count)))
    let index = startIndex.advancedBy(randomInt)
    return count == 0 ? nil: self[index]
}

在swift3

extension Set {
    public func randomObject() -> Element? {
        let n = Int(arc4random_uniform(UInt32(self.count)))
        let index = self.index(self.startIndex, offsetBy: n)
        return self.count > 0 ? self[index] : nil
    }
}

从Swift 4.2开始,可以使用randomElement:

let random = set.randomElement()