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
Listing All Files in a Folder Recursively with Swift
Swift 5.5: Asynchronously Iterating Line-By-Line Through a File
Retrieve String Value from Function with Closure in Swift
Single-Element Parethesized Expressions/Tuples VS Common Use of Parentheses
What Is the Way to Save Fonts and Sizes in Firebase for Textview Swift
How to Load Image in Swift Using Alamofire
Checking If a Value Is Changed Using Kvo in Swift 3
Swift: How to Create External Interface for Static Library (Public Headers Analog in Objective-C .H)
Swift 3 (Spritekit): Reseting the Gamescene After the Game Ends
Can't Use Storyboard Custom Instantiated Window Controller
Swift, Auto Resize Custom Table View Cells
Non-Strong References Not Working in Playground
Expandable Sections Uitableview Indexpath Swift
Uiscrollview with Embedded Uiimageview; How to Get the Image to Fill the Screen
Nstextfield, Change Text in Swift
How to Conform to Nscopying and Implement Copywithzone in Swift 2
Swift/Uiview/Drawrect - How to Get Drawrect to Update When Required