Supposons que j'ai un tableau et que je veux choisir un élément au hasard.
Quel serait le moyen le plus simple de faire cela?
La manière évidente serait array[random index]
. Mais peut-être y a-t-il quelque chose comme array.sample
de Ruby? Ou sinon, une telle méthode pourrait-elle être créée en utilisant une extension?
Swift 4.2 et plus
La nouvelle approche recommandée est une méthode intégrée au protocole Collection: randomElement()
. Cela retourne un optionnel pour éviter le cas vide contre lequel j'avais supposé auparavant.
let array = ["Frodo", "Sam", "Wise", "Gamgee"]
print(array.randomElement()!) // Using ! knowing I have array.count > 0
Si vous ne créez pas le tableau et que le nombre de points garantis n'est pas supérieur à 0, procédez comme suit:
if let randomElement = array.randomElement() {
print(randomElement)
}
Swift 4.1 et ci-dessous
Juste pour répondre à votre question, vous pouvez le faire pour obtenir une sélection de tableau aléatoire:
let array = ["Frodo", "sam", "wise", "gamgee"]
let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
print(array[randomIndex])
Les castings sont laids, mais je crois qu’ils sont nécessaires à moins que quelqu'un d’autre ne le fasse autrement.
En reprenant ce que Lucas a dit, vous pouvez créer une extension de la classe Array comme ceci:
extension Array {
func randomItem() -> Element? {
if isEmpty { return nil }
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}
Par exemple:
let myArray = [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16]
let myItem = myArray.randomItem() // Note: myItem is an Optional<Int>
Swift 4 version:
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)))]
}
}
Dans Swift 2.2 cela peut être généralisé de sorte que nous ayons:
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
Tout d'abord, implémenter la propriété statique random
pour UnsignedIntegerType
s:
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)
}
}
Ensuite, pour ClosedInterval
s avec UnsignedIntegerType
bornes:
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))
}
}
Puis (un peu plus impliqué), pour ClosedInterval
s avec SignedIntegerType
bornes (en utilisant les méthodes d'assistance décrites plus loin):
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)
}
}
... où unsignedDistanceTo
, unsignedDistanceFromMin
et plusMinIntMax
méthodes auxiliaires peuvent être implémentées comme suit:
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) }
}
}
Enfin, pour toutes les collections où 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]
}
}
... qui peut être optimisé un peu pour l'entier Range
s:
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
}
}
Vous pouvez également utiliser la fonction random () intégrée de Swift pour l'extension:
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
Une autre suggestion Swift 3
private extension Array {
var randomElement: Element {
let index = Int(arc4random_uniform(UInt32(count)))
return self[index]
}
}
Suivant les autres répondent mais avec Swift 2 support.
extension Array {
func sample() -> T {
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}
extension Array {
func sample() -> Element {
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}
Par exemple.:
let arr = [2, 3, 5, 7, 9, 11, 13, 17, 19, 23, 29, 31]
let randomSample = arr.sample()
Si vous voulez pouvoir extraire plus d'un élément aléatoire de votre tableau avec sans doublon , GameplayKit vous a couvert:
import GameplayKit
let array = ["one", "two", "three", "four"]
let shuffled = GKMersenneTwisterRandomSource.sharedRandom().arrayByShufflingObjects(in: array)
let firstRandom = shuffled[0]
let secondRandom = shuffled[1]
Vous avez plusieurs choix pour le hasard, voir GKRandomSource :
La classe
GKARC4RandomSource
utilise un algorithme similaire à celui utilisé dans la famille arc4random de fonctions C. (Cependant, les instances de cette classe sont indépendantes des appels aux fonctions arc4random.)La classe
GKLinearCongruentialRandomSource
utilise un algorithme plus rapide, mais moins aléatoire, que la classe GKARC4RandomSource. (Plus précisément, les bits bas des nombres générés se répètent plus souvent que les bits hauts.) Utilisez cette source lorsque les performances sont plus importantes que l'imprévisibilité robuste.La classe
GKMersenneTwisterRandomSource
utilise un algorithme plus lent, mais plus aléatoire, que la classe GKARC4RandomSource. Utilisez cette source quand il est important que votre utilisation de nombres aléatoires ne montre pas de répétition et que les performances sont moins préoccupantes.
Swift 3
importer GameKit
func getRandomMessage() -> String {
let messages = ["one", "two", "three"]
let randomNumber = GKRandomSource.sharedRandom().nextInt(upperBound: messages.count)
return messages[randomNumber].description
}
Une autre implémentation fonctionnelle avec vérification du tableau vide.
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])
Swift 3 - simple facile à utiliser.
Créer un tableau
var arrayOfColors = [UIColor.red, UIColor.yellow, UIColor.orange, UIColor.green]
Créer des couleurs aléatoires
let randomColor = arc4random() % UInt32(arrayOfColors.count)
Définissez cette couleur sur votre objet
your item = arrayOfColors[Int(randomColor)]
Voici un exemple tiré d'un projet SpriteKit
mettant à jour un SKLabelNode
avec un String
aléatoire:
let array = ["one","two","three","four","five"]
let randomNumber = arc4random() % UInt32(array.count)
let labelNode = SKLabelNode(text: array[Int(randomNumber)])
Voici une extension sur les tableaux avec un contrôle de tableau vide pour plus de sécurité:
extension Array {
func sample() -> Element? {
if self.isEmpty { return nil }
let randomInt = Int(arc4random_uniform(UInt32(self.count)))
return self[randomInt]
}
}
Vous pouvez l'utiliser aussi simplement que cela :
let digits = Array(0...9)
digits.sample() // => 6
Si vous préférez un Framework qui présente également des fonctionnalités plus pratiques, cliquez sur checkout HandySwift. Vous pouvez l'ajouter à votre projet via Carthage puis l'utiliser exactement comme dans l'exemple ci-dessus:
import HandySwift
let digits = Array(0...9)
digits.sample() // => 8
En outre, il inclut également une option permettant d’obtenir plusieurs éléments aléatoires à la fois :
digits.sample(size: 3) // => [8, 0, 7]
Il y a une méthode intégrée sur Collection
maintenant:
let foods = ["????", "????", "????", "????"]
let myDinner = foods.randomElement()
Si vous voulez extraire jusqu'à n
éléments aléatoires d'une collection, vous pouvez ajouter une extension comme celle-ci:
extension Collection {
func randomElements(_ count: Int) -> [Element] {
var shuffledIterator = shuffled().makeIterator()
return (0..<count).compactMap { _ in shuffledIterator.next() }
}
}
Et si vous voulez qu'ils soient uniques, vous pouvez utiliser un Set
, mais les éléments de la collection doivent être conformes au protocole Hashable
:
extension Collection where Element: Hashable {
func randomUniqueElements(_ count: Int) -> [Element] {
var shuffledIterator = Set(shuffled()).makeIterator()
return (0..<count).compactMap { _ in shuffledIterator.next() }
}
}
Je trouve que l'utilisation de GKRandomSource.sharedRandom () de GameKit fonctionne le mieux pour moi.
import GameKit
let array = ["random1", "random2", "random3"]
func getRandomIndex() -> Int {
let randomNumber = GKRandomSource.sharedRandom().nextIntWithUpperBound(array.count)
return randomNumber
ou vous pouvez retourner l'objet à l'index aléatoire sélectionné. Assurez-vous que la fonction renvoie d'abord une chaîne, puis renvoyez l'index du tableau.
return array[randomNumber]
Court et au point.
dernier code Swift essayez-le
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)])