如何从数组中获取随机元素并将其删除?

3
我知道有一个函数randomElement()可以获取随机元素并返回它。
var array = ["a", "b", "c", "d", "e"]
var result = array.randomElement()
print(array) //["a", "b", "c", "d", "e"]
print(result) //Optional("c")

所以它运行得很好,但所有的数组都保持不变。我该如何从数组中删除那个随机元素?有没有一种快速的方法可以在不迭代、查找和删除的情况下完成?

3个回答

5

选择一个随机索引而不是随机元素。

if let index = array.indices.randomElement() {
    let value = array.remove(at: index)
    // ...
}

如果意图是删除所有元素,请参见以下Alexander的评论。 以这种方式删除所有元素会是O(n^2),使用.shuffled()方法先进行混排将会更快。

3
如果您需要经常这样做,最好先使用.shuffle(),然后再反复使用removeLast() - undefined
亚历山大,有性能差异吗?我想要一些澄清,为什么它更好。 - undefined
1
@ParkerGibson Alexander关于性能影响是正确的。我的答案是O(n^2),因为它必须不断移动数组中的元素。Alexander的答案将是O(n log n)。虽然Alexander的答案略有不同(因为它修改了未被删除的元素的顺序),但这是一个自然的下一个问题。 - undefined

2
extension Array {
    mutating func removeRandom() -> Element? {
        if let index = indices.randomElement() {
            return remove(at: index)
        }
        return nil
    }
}

我喜欢这个解决方案(已投票)。不过,我会使用名为extractRandom的函数名称,因为remove暗示“丢弃”,而“extract”则意味着“取出”。也许可以将函数结果设置为可丢弃的,这样你就可以忽略它而不会收到编译器警告? - undefined
我将它制作得类似于函数removeFirst()removeLast() - undefined
@DuncanC 大多数集合上的remove*方法会返回它们所移除的元素。我对此的一个改变是添加@discardableResult - undefined
@RobNapier 请随意添加这个;) - undefined
此外,大多数的remove方法认为空集合是一个编程错误(它们会崩溃而不是返回一个可选项;通常使用"pop"一词来表示可选地移除)。但是,如果对您的使用不方便,那么自定义的便利方法并不一定要完全匹配现有的方法。 - undefined
最好扩展RangeReplaceableCollection。这样它也能支持字符串。我还会添加@discardableResult - undefined

1
你可以试试这个。
var array = ["a", "b", "c", "d", "e"]
var result = array.randomElement()
if let index = array.firstIndex(of: result ?? "")  {
  array.remove(at: index)
}
print(array) //["a", "b", "c", "d", "e"]
print(result) //Optional("c")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接