207

Suppose I have an array and I want to pick one element at random.

What would be the simplest way to do this?

The obvious way would be array[random index]. But perhaps there is something like ruby's array.sample? Or if not could such a method be created by using an extension?

smci
  • 32,567
  • 20
  • 113
  • 146
Fela
  • 26,730
  • 8
  • 26
  • 24
  • 1
    Have you tried any different methods yet? – ford prefect Jun 02 '14 at 21:00
  • I would try `array[random number from 0 to length-1]`, but I can't find how to generate a random int in swift, I would ask it on stack overflow if I weren't blocked :) I didn't want to pollute the question with half solutions when maybe there is something like ruby's `array.sample` – Fela Jun 02 '14 at 21:22
  • 1
    You use arc4random() like you would in Obj-C – Arbitur Jun 03 '14 at 15:22
  • No explanation for why your question didn't receive the same feedback as the JQuery counterpart. But in general, you should follow these guidelines when posting a question. [How to ask a good question?](http://stackoverflow.com/help/how-to-ask). Make it look like you put a little effort into figuring out a solution before asking someone else for help. When I google "choose random number swift", the first page is filled with answers suggesting arc4random_uniform. Also, RTFD..."read the f'ing documentation". It's surprising how many questions can be answered this way. – Austin A Sep 27 '14 at 21:17
  • Thank you for your kind feedback. Yep, I guess I should have answered the question myself, but it seemed easy enough that it was nice to give somebody else the almost free reputation points. And I wrote it when not even the official Apple swift docs were public, there definitively were no Google results at that time. But the question was once at -12, so I'm pretty confident it will be positive eventually :) – Fela Sep 28 '14 at 12:13
  • Regarding using the `RandomNumberGenerator` protocol: GamePlayKit has a seeded random generator, see [this answer](https://stackoverflow.com/a/54849689/78336). – neoneye Sep 17 '19 at 20:51

16 Answers16

358

Swift 4.2 and above

The new recommended approach is a built-in method on the Collection protocol: randomElement(). It returns an optional to avoid the empty case I assumed against previously.

let array = ["Frodo", "Samwise", "Merry", "Pippin"]
print(array.randomElement()!) // Using ! knowing I have array.count > 0

If you don't create the array and aren't guaranteed count > 0, you should do something like:

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

Swift 4.1 and below

Just to answer your question, you can do this to achieve random array selection:

let array = ["Frodo", "Samwise", "Merry", "Pippin"]
let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
print(array[randomIndex])

The castings are ugly, but I believe they're required unless someone else has another way.

Lucas Derraugh
  • 6,929
  • 3
  • 27
  • 43
  • 4
    Why doesn't Swift offer a random number generator that returns an Int? This 2nd line seems very verbose just to return randomly chosen Int. Is there some computational/syntactical advantage to returning a UInt32 as opposed to an Int? Also, why doesn't Swift offer an Int alternative to this function or allow a user to specify what type of integer they would like returned? – Austin A Sep 27 '14 at 21:10
  • To add a note, this random number generator method could prevent "modulo bias". Refer `man arc4random` and http://stackoverflow.com/questions/10984974/why-do-people-say-there-is-modulo-bias-when-using-a-random-number-generator – Kent Liau Feb 12 '15 at 10:01
  • 2
    @AustinA, Swift 4.2 DOES have a native random number generator function that's implemented on all the scalar data types you might want expect: Int, Double, Float, UInt32, etc. And it lets you provide target ranges for the values. Very handy. You can use array[Int.random(0.. – Duncan C Sep 19 '18 at 18:30
  • I wish Swift 4.2 implemented a `removeRandomElement()` function in addition to `randomElement()`. It would be modeled on `removeFirst()`, but remove an object at a random index. – Duncan C Sep 19 '18 at 18:32
  • @DuncanC You should avoid `0.. – Alexander Aug 05 '19 at 14:01
138

Riffing on what Lucas said, you could create an extension to the Array class like this:

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

For example:

let myArray = [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16]
let myItem = myArray.randomItem() // Note: myItem is an Optional<Int>
Berik
  • 7,816
  • 2
  • 32
  • 40
Phae Deepsky
  • 1,431
  • 2
  • 11
  • 8
  • 2
    In swift 2 `T` has been renamed to `Element`. – GDanger Oct 15 '15 at 22:02
  • 25
    Notice that an empty array will cause a crash here – Berik Dec 05 '15 at 18:26
  • 1
    @Berik Well you could return an optional Element and then always do a `guard` check to see if the array is empty and then return `nil`. – Harish Jul 28 '17 at 23:20
  • 1
    Agreed. Arrays crash on out-of-bound so that they can be performant. Calling into `arc4random` makes any performance gains completely insignificant. I've updated the answer. – Berik Aug 04 '17 at 00:11
46

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)))]
    }

}
Andrey Gordeev
  • 30,606
  • 13
  • 135
  • 162
  • This can crash with an index out of bounds on collections where `startIndex != 0` – dan Apr 26 '18 at 15:45
21

In Swift 2.2 this can be generalised so that we have:

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

First, implementing static random property for UnsignedIntegerTypes:

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)
    }
}

Then, for ClosedIntervals with UnsignedIntegerType bounds:

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))
    }
}

Then (a little more involved), for ClosedIntervals with SignedIntegerType bounds (using helper methods described further below):

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)
    }
}

... where unsignedDistanceTo, unsignedDistanceFromMin and plusMinIntMax helper methods can be implemented as follows:

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) }
    }
}

Finally, for all collections where 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]
    }
}

... which can be optimised a little for integer 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
    }
}
Milos
  • 2,728
  • 22
  • 24
18

You can use Swift's built-in random() function as well for the 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
highlycaffeinated
  • 19,729
  • 9
  • 60
  • 91
NatashaTheRobot
  • 6,879
  • 4
  • 32
  • 27
9

Another Swift 3 suggestion

private extension Array {
    var randomElement: Element {
        let index = Int(arc4random_uniform(UInt32(count)))
        return self[index]
    }
}
slammer
  • 278
  • 3
  • 9
4

Following others answer but with Swift 2 support.

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]
    }
}

E.g.:

let arr = [2, 3, 5, 7, 9, 11, 13, 17, 19, 23, 29, 31]
let randomSample = arr.sample()
Aidan Gomez
  • 8,167
  • 5
  • 28
  • 51
2

An alternative functional implementation with check for empty array.

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])
Evgenii
  • 36,389
  • 27
  • 134
  • 170
2

Here's an extension on Arrays with an empty array check for more safety:

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

You can use it as simple as this:

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

If you prefer a Framework that also has some more handy features then checkout HandySwift. You can add it to your project via Carthage then use it exactly like in the example above:

import HandySwift    

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

Additionally it also includes an option to get multiple random elements at once:

digits.sample(size: 3) // => [8, 0, 7]
Jeehut
  • 20,202
  • 8
  • 59
  • 80
2

Swift 3

import GameKit

func getRandomMessage() -> String {

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

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

    return messages[randomNumber].description

}
Dazzle
  • 2,880
  • 3
  • 25
  • 52
2

Swift 3 - simple easy to use.

  1. Create Array

    var arrayOfColors = [UIColor.red, UIColor.yellow, UIColor.orange, UIColor.green]
    
  2. Create Random Color

    let randomColor = arc4random() % UInt32(arrayOfColors.count)
    
  3. Set that color to your object

    your item = arrayOfColors[Int(randomColor)]
    

Here is an example from a SpriteKit project updating a SKLabelNode with a random String:

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

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

    let labelNode = SKLabelNode(text: array[Int(randomNumber)])
Timmy Sorensen
  • 570
  • 6
  • 16
2

If you want to be able to get more than one random element out of your array with no duplicates, GameplayKit has you covered:

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

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

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

You have a couple choices for randomness, see GKRandomSource:

The GKARC4RandomSource class uses an algorithm similar to that employed in arc4random family of C functions. (However, instances of this class are independent from calls to the arc4random functions.)

The GKLinearCongruentialRandomSource class uses an algorithm that is faster, but less random, than the GKARC4RandomSource class. (Specifically, the low bits of generated numbers repeat more often than the high bits.) Use this source when performance is more important than robust unpredictability.

The GKMersenneTwisterRandomSource class uses an algorithm that is slower, but more random, than the GKARC4RandomSource class. Use this source when it’s important that your use of random numbers not show repeating patterns and performance is of less concern.

Community
  • 1
  • 1
bcattle
  • 12,115
  • 6
  • 62
  • 82
1

I find using GameKit's GKRandomSource.sharedRandom() works best for me.

import GameKit

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

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

or you could return the object at the random index selected. Make sure the function returns a String first, and then return the index of the array.

    return array[randomNumber]

Short and to the point.

djames04
  • 11
  • 2
1

There is a built-in method on Collection now:

let foods = ["", "", "", ""]
let myDinner = foods.randomElement()

If you want to extract up to n random elements from a collection you can add an extension like this one:

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

And if you want them to be unique you can use a Set, but the elements of the collection must conform to the Hashable protocol:

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

Latest swift3 code try it its working fine

 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

I figured out a very different way to do so using the new features introduced in 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. we declared a function with parameters taking an array of Strings and returning a String.

  2. Then we take the ArrayOfStrings in a variable.

  3. Then we call the shuffled function and store that in a variable. (Only supported in 4.2)
  4. Then we declare a variable which saves a shuffled value of total count of the String.
  5. Lastly we return the shuffled string at the index value of countS.

It is basically shuffling the array of strings and then also have a random pick of number of the total number of count and then returning the random index of the shuffled array.