Obtener elementos aleatorios de la matriz en swift

Tengo una matriz como:

var names: String = [ "Peter", "Steve", "Max", "Sandra", "Roman", "Julia" ] 

Me gustaría obtener 3 elementos aleatorios de esa matriz. Vengo de C #, pero rápidamente no estoy seguro de dónde empezar. Creo que debería barajar el conjunto primero y luego elegir los primeros 3 elementos, por ejemplo.

Intenté mezclarlo con la siguiente extensión:

 extension Array { mutating func shuffle() { for _ in 0..<10 { sort { (_,_) in arc4random() < arc4random() } } } } 

pero luego dice “‘()’ no es convertible a ‘[Int]'” en la ubicación de “shuffle ()”.

Para elegir una cantidad de elementos que uso:

 var randomPicks = names[0..<4]; 

que se ve bien hasta ahora.

Cómo barajar? ¿O alguien tiene una solución mejor / más elegante para esto?

Xcode 9 • Swift 4

 extension Array { /// Returns an array containing this sequence shuffled var shuffled: Array { var elements = self return elements.shuffle() } /// Shuffles this sequence in place @discardableResult mutating func shuffle() -> Array { let count = self.count indices.lazy.dropLast().forEach { swapAt($0, Int(arc4random_uniform(UInt32(count - $0))) + $0) } return self } var chooseOne: Element { return self[Int(arc4random_uniform(UInt32(count)))] } func choose(_ n: Int) -> Array { return Array(shuffled.prefix(n)) } } 

Prueba de patio de recreo

 var alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"] let shuffledAlphabet = alphabet.shuffled let letter = alphabet.chooseOne var numbers = Array(0...9) let shuffledNumbers = numbers.shuffled shuffledNumbers // [8, 9, 3, 6, 0, 1, 4, 2, 5, 7] numbers // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] numbers.shuffle() // mutate it [6, 0, 2, 3, 9, 1, 5, 7, 4, 8] numbers // [6, 0, 2, 3, 9, 1, 5, 7, 4, 8] let pick3numbers = numbers.choose(3) // [8, 9, 2] 

Xcode 8.3.1 • Swift 3.1

 import UIKit extension Array { /// Returns an array containing this sequence shuffled var shuffled: Array { var elements = self return elements.shuffle() } /// Shuffles this sequence in place @discardableResult mutating func shuffle() -> Array { let count = self.count indices.lazy.dropLast().forEach { guard case let index = Int(arc4random_uniform(UInt32(count - $0))) + $0, index != $0 else { return } swap(&self[$0], &self[index]) } return self } var chooseOne: Element { return self[Int(arc4random_uniform(UInt32(count)))] } func choose(_ n: Int) -> Array { return Array(shuffled.prefix(n)) } } 

¿O alguien tiene una solución mejor / más elegante para esto?

Hago. Algorítmicamente mejor que la respuesta aceptada, que cuenta-1 operaciones arc4random_uniform para un barajado completo, simplemente podemos elegir n valores en n operaciones arc4random_uniform .

Y, de hecho, tengo dos formas de hacerlo mejor que la respuesta aceptada:

Mejor solución

 extension Array { /// Picks `n` random elements (straightforward approach) subscript (randomPick n: Int) -> [Element] { var indices = [Int](0.. 

Mejor solución

La siguiente solución es dos veces más rápida que la anterior.

para Swift 3.0 y 3.1

 extension Array { /// Picks `n` random elements (partial Fisher-Yates shuffle approach) subscript (randomPick n: Int) -> [Element] { var copy = self for i in stride(from: count - 1, to: count - n - 1, by: -1) { let j = Int(arc4random_uniform(UInt32(i + 1))) if j != i { swap(&copy[i], &copy[j]) } } return Array(copy.suffix(n)) } } 

para Swift 3.2 y 4.x

 extension Array { /// Picks `n` random elements (partial Fisher-Yates shuffle approach) subscript (randomPick n: Int) -> [Element] { var copy = self for i in stride(from: count - 1, to: count - n - 1, by: -1) { copy.swapAt(i, Int(arc4random_uniform(UInt32(i + 1)))) } return Array(copy.suffix(n)) } } 

Uso:

 let digits = Array(0...9) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] let pick3digits = digits[randomPick: 3] // [8, 9, 0] 

También podría usar arc4random () para simplemente elegir tres elementos de la matriz. Algo como esto:

 extension Array { func getRandomElements() -> (T, T, T) { return (self[Int(arc4random()) % Int(count)], self[Int(arc4random()) % Int(count)], self[Int(arc4random()) % Int(count)]) } } let names = ["Peter", "Steve", "Max", "Sandra", "Roman", "Julia"] names.getRandomElements() 

Esto es solo un ejemplo, también podría incluir lógica en la función para obtener un nombre diferente para cada uno.

Swift 4.1 y abajo

 let playlist = ["Nothing Else Matters", "Stairway to Heaven", "I Want to Break Free", "Yesterday"] let index = Int(arc4random_uniform(UInt32(playlist.count))) let song = playlist[index] 

Swift 4.2 y superior

 if let song = playlist.randomElement() { print(song) } else { print("Empty playlist.") } 
Intereting Posts