How to Get Random Element from a Set in Swift

How to get random element from a set in Swift?

Probably the best approach is advance which walks successor for you:

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

(EDIT: Heh; noticed you actually updated the question to include this answer before I added it to my answer... well, still a good idea and I learned something too. :D)

You can also walk the set rather than the indices (this was my first thought, but then I remembered 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")
}

Pick a random element from an array

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.

How to get random element from CharacterSet in Swift

A CharacterSet is not a Collection. It is a SetAlgebra.

It is not, despite its name, "a Set of Characters." It is a "set" of UnicodeScalars (not Characters) in the mathematical sense: a list of rules that define whether a given UnicodeScalar is contained. There is no direct way to enumerate the values. There are inefficient ways to generate a complete list since UnicodeScalar is over a finite range, but it's quite massive.

I'm curious how you would use this, though. This may include a lot of characters you're not expecting like UNDERTIE (‿), SAMARITAN PUNCTUATION BAU (࠳), and THAI CHARACTER FONGMAN (๏). Are you really looking to pick a random value out all Unicode alphanumerics and punctuation? (There are over 800 punctuation characters, for example, and by my rough count maybe 25k alphanumerics. I haven't counted symbols yet, but there are a lot of them. The chance that you'll get a character on a US keyboard is pretty close to zero.)

I expect this is the code you're really looking for:

let asciiRange = 33...126
let randomCharacter = asciiRange.randomElement()
.flatMap(UnicodeScalar.init)
.flatMap(Character.init)!

This will return a random, printable ASCII character.

Given that your set is such a large proportion of the Unicode space, the following is how you would get a truly random one reasonably quickly:

func randomCharacter() -> Character {
// Drops the control characters and SPACE, the private use areas, tags, and the variation selectors.
// The full range is 0x00...0x10FFFD
let unicodeRange = 0x21...0x2FA1D
let validCharacters: CharacterSet = .alphanumerics.union(.punctuationCharacters).union(.symbols)

repeat {
if let c = unicodeRange.randomElement().flatMap(UnicodeScalar.init),
validCharacters.contains(c) {
return Character(c)
}
} while true
}

I just keeps guessing until it finds one. This will tend to converge as long as the set you're picking from is similar in size to the full set. This is likely more efficient than generating a massive Set<Character> which you could do by walking a similar space.

Get random elements from array in Swift

Xcode 11 • Swift 5.1

extension Collection {
func choose(_ n: Int) -> ArraySlice<Element> { shuffled().prefix(n) }
}

Playground testing

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() // "O", "X", "L", "D", "N", "K", "R", "E", "S", "Z", "I", "T", "H", "C", "U", "B", "W", "M", "Q", "Y", "V", "A", "G", "P", "F", "J"]
let letter = alphabet.randomElement() // "D"
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]

extension RangeReplaceableCollection {
/// Returns a new Collection shuffled
var shuffled: Self { .init(shuffled()) }
/// Shuffles this Collection in place
@discardableResult
mutating func shuffledInPlace() -> Self {
self = shuffled
return self
}
func choose(_ n: Int) -> SubSequence { shuffled.prefix(n) }
}

var alphabetString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
let shuffledAlphabetString = alphabetString.shuffled // "DRGXNSJLFQHPUZTBKVMYAWEICO"
let character = alphabetString.randomElement() // "K"
alphabetString.shuffledInPlace() // mutate it "WYQVBLGZKPFUJTHOXERADMCINS"
alphabetString // "WYQVBLGZKPFUJTHOXERADMCINS"
let pick3Characters = alphabetString.choose(3) // "VYA"

How do I get random element from an array and remove it from there?

Select a random index rather than a random element.

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

See Alexander's comments below if the intention is to remove all the elements. Removing all the elements this way would be O(n^2), and using .shuffled() first would be much faster.

How to get multiple random elements from a Swift Set?

Here's the shortest one-liner I came up with:

let randomTenElements = mySet.lazy.shuffled().prefix(10)

It returns an ArraySlice though so if you need the result as an array or another set you'll have to explicitly cast it to your desired type.

Swift Set - Difference between randomElement and first

While the interface of Set is an unordered collection, internally it has an order that depends on its implementation. This is partially because you cant store anything on a computer truly unordered. It is also because Set conforms to Collection, it has the following property

Iterating over the elements of a collection by their positions yields the same elements in the same order as iterating over that collection using its iterator.

That means that it needs to have some kind of internal ordering to allow the consistency between different methods of iterating.

So while it's defined what value you'll get back from first, it will be consistent until a value is inserted or removed from the Set. randomElement will always return a randomly chosen element independent of what the underlying order is.

How to get random value from struct

If you want to keep it packed in some type you better use enum instead:

enum RandomMessage: String, CaseIterable {
case message1 = "Message1"
case message2 = "Message2"
case message3 = "Message3"
case message4 = "Message4"
case message5 = "Message5"

static var get: String {
return allCases.randomElement()!.rawValue
}
}

This way you will guarantee that it will only have "fields" with messages, and if you get new messages you can just add new case and it will automatically be in allCases array.

To get random message do this:

let message = RandomMessage.get // one of the messages

Get a random unique element from an Array until all elements have been picked in Swift

Copy the array. Shuffle the copy. Now just keep removing the first element until the copy is empty. When it is empty, start over.

Example:

let arr = [1,2,3,4,5]
var copy = [Int]()
for _ in 1...30 { // just to demonstrate what happens
if copy.isEmpty { copy = arr; copy.shuffle() }
let element = copy.removeFirst() ; print(element, terminator:" ")
}
// 4 2 3 5 1 1 5 3 2 4 4 1 2 3 5 1 4 5 2 3 3 5 4 2 1 3 2 4 5 1

Swift return random element from array into text

Since results aren't changing, there's no need for @State (coupled with an initializer on Result so we can easily map a list of scores).
Instead, we want to introduce a new variable to hold a random Result and mark that as a state. So each time we press the button, we set a new random result and trigger a view update.

Please note that I have marked the var as optional (since I do not know the logic behind any initial value) and displaying a 0 if this thing is nil (probably not what you want in your final code - you can decide whatever default value, or conditional view display, makes sense for you)

struct Result: Identifiable {
var id = UUID()
var score: Int
init(_ score: Int) {
self.score = score
}
}

struct ContentView: View {

@State private var showDetails = false

let results = [8, 5, 10].map(Result.init)

@State var randomResult: Result?

var body: some View {
VStack {
Button(action: {
self.randomResult = self.results.randomElement()
}) {
Text("Button title, \(randomResult?.score ?? 0)")
}
}
}
}


Related Topics



Leave a reply



Submit