1

I want to create an motivational app for iOS. The motivation sentences were randomly chosen with this code.

arc4random_uniform(100)

The problem: The code displays for example 2 and that another 2 so the user gets the same sentence again.

I need something that gets a random number from a range for example 100 only once and after every digit the code should repeat itself so that the user can see the motivational sentences again.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Enes
  • 79
  • 1
  • 9
  • 1
    Swap the selected sentence with the sentence at the end of the array and next time reduce your random number range by 1. When your range gets to 1,reset it to the size of the array. – Paulw11 Apr 21 '18 at 12:43
  • https://stackoverflow.com/questions/24026510/how-do-i-shuffle-an-array-in-swift – Martin R Apr 21 '18 at 13:09
  • https://stackoverflow.com/a/27261991/2303865 – Leo Dabus Apr 21 '18 at 17:06

1 Answers1

1

You could use a Set to filter already extracted numbers (Sets contains unique elements), and do something like this

import Foundation

var setOfNumbers: Set<UInt32> = []

func extractUniqueNumber(with treshold: UInt32, alreadyExtracted: inout Set<UInt32>) -> UInt32? {

    guard alreadyExtracted.count < treshold else { return nil }

    let randomNumber = arc4random_uniform(treshold)

    if !alreadyExtracted.contains(randomNumber) {
        alreadyExtracted.insert(randomNumber)
        return randomNumber
    } else {
        return extractUniqueNumber(with: treshold, alreadyExtracted: &alreadyExtracted)
    }
}

// test it out with a number of tries

for _ in 0...100 {
    if let number = extractUniqueNumber(with: 100, alreadyExtracted: &setOfNumbers) {
        print("Your number is: \(number).\n- Previously extracted numbers) are: \(setOfNumbers)")
    } else {
        print("You have extracted all  numbers already")
    }
}

if the order in which numbers are extracted is important, the Set can be replaced with an Array. The price to pay would be a slower search for the already extracted number, but in the range of 100 would most likely be still fine. In this case the code would be

var setOfNumbers: [UInt32] = []

func extractUniqueNumber(with treshold: UInt32, alreadyExtracted: inout [UInt32]) -> UInt32? {

    guard alreadyExtracted.count < treshold else { return nil }

    let randomNumber = arc4random_uniform(treshold)

    if !alreadyExtracted.contains(randomNumber) {
        alreadyExtracted.append(randomNumber)
        return randomNumber
    } else {
        return extractUniqueNumber(with: treshold, alreadyExtracted: &alreadyExtracted)
    }
}
Gi0R
  • 1,387
  • 2
  • 13
  • 15
  • 1
    Note that Set is an unordered collection so you are loosing the original order generated – Leo Dabus Apr 21 '18 at 17:04
  • One problem though. The code displays every Int in that Range but it should only display it once per click on ad button – Enes Apr 21 '18 at 17:49
  • The example extracts 100 numbers in a row. If you call the same code inside the for loop, in the action associated to your button, you will get one number per button press (console prints are only for the sake of clarity in this example, feel free to remove/change them as you may need). – Gi0R Apr 21 '18 at 18:01
  • Thanks, forgot about that :D – Enes Apr 22 '18 at 08:34