Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS Build a Simple iPhone App with Swift Structs As Data Models Finishing Up Our Model

Generating a Random Number (non repeating)

How can I generate a random non-repeating number? To avoid showing the same fact twice (until all facts have been shown first)

The current code sometimes repeats number (hence repeating duplicate facts before going through the whole array:\

import GameKit // for the GKRandom function

struct QuoteProvider {
    let quoteArray = [quote1, quote2, quote3, quote4, quote5]
    func randomQuote() -> Quote { // Generate a random number to choose from the array
        let randomNumber = GKRandomSource.sharedRandom().nextInt(upperBound: quoteArray.count)
        return quoteArray[randomNumber]
    }
}

3 Answers

Steven Parker
Steven Parker
229,732 Points

I'm not an iOS programmer, but I'm familiar with the strategy for such a task. To avoid repeating, each time you choose a random item from a list, move that item out of that list and add it to a "used items" list. Then, when the list you pick from is completely empty, you can move all the items from the "used items" list back into it. Only then will any item be seen again.

Thank you!

Could you provide a code example please?

Steven Parker
Steven Parker
229,732 Points

As I said, I'm not an iOS/Swift programmer. I've just done similar things in other languages so I am familiar with the concepts.

I would still appreciate anyone proficient with Swift to provide a solution based on this exercise and the code provided.

Xavier D
PLUS
Xavier D
Courses Plus Student 5,840 Points

Hi swiftfun,

I took some advice of what Steven Parker said and decided to create list as a stored property array, and I had to add the mutating keyword to the randomFact method in order to modify the list. To avoid creating an optional, I assigned an arbitrary number to the property. Coupled with a couple of loops and if statements each, as well as changing randomNumber to a variable, I believe that I was able to avoid the same fact from being displayed twice in a row, as well as avoid showing a duplicated fact until all the facts are displayed...

import GameplayKit

struct QuoteProvider
{
    let quoteArray = [
        "Ants stretch when they wake up in the morning.",
        "Ostriches can run faster than horses.",
        "Olympic gold medals are actually made mostly of silver.",
        "You are born with 300 bones; by the time you are an adult you will have 206.",
        "It takes about 8 minutes for light from the Sun to reach Earth.",
        "Some bamboo plants can grow almost a meter in just one day.",
        "The state of Florida is bigger than England.",
        "Some penguins can leap 2-3 meters out of the water.",
        "On average, it takes 66 days to form a new habit.",
        "Mammoths still walked the Earth when the Great Pyramid was being built."
    ]

    var list:[Int]=[999]
    mutating func randomQuote()->String
    {
        var random=GKRandomSource.sharedRandom().nextInt(upperBound: quoteArray.count)
        while  random==list[list.count-1]{random=GKRandomSource.sharedRandom().nextInt(upperBound: quoteArray.count)}
        while list.contains(random){random=GKRandomSource.sharedRandom().nextInt(upperBound: quoteArray.count)}
        list.append(random);if list[0]==999{list.removeFirst()};print(list)
        if list.count==quoteArray.count{list.removeAll();list.append(GKRandomSource.sharedRandom().nextInt(upperBound: quoteArray.count))};return quoteArray[list[list.count-1]]
    }
}

I checked using the print statement on the list and the loops worked well without a counter and infinite looping. I hope that I achieved all objectives.

Xavier D
Xavier D
Courses Plus Student 5,840 Points

BTW...I believe the first while can be omitted without affecting behavior.

XD

Xavier D
Xavier D
Courses Plus Student 5,840 Points

Ahh,

I noticed that I still may get a duplicate. It occurs after all the facts are displayed, the list is refreshes, and the new list starts. It appears whatever fact appears last in the old list, may be the first to appear in the new list, thus appearing twice in a row. Therefore, I created another property to hold that last number and compare it to the first number of the new list, and if they are the same, then generate another number. I used a repeat while so that a number is generated at least once regardless if condition is true, which should avoid an out of bounds error after emptying the list and starting over (I think...)

import GameplayKit

struct QuoteProvider
{
    let quoteArray = [
        "Ants stretch when they wake up in the morning.",
        "Ostriches can run faster than horses.",
        "Olympic gold medals are actually made mostly of silver.",
        "You are born with 300 bones; by the time you are an adult you will have 206.",
        "It takes about 8 minutes for light from the Sun to reach Earth.",
        "Some bamboo plants can grow almost a meter in just one day.",
        "The state of Florida is bigger than England.",
        "Some penguins can leap 2-3 meters out of the water.",
        "On average, it takes 66 days to form a new habit.",
        "Mammoths still walked the Earth when the Great Pyramid was being built."
    ]

    var list=[999];var last=999
    mutating func randomQuote() -> String
    {
        var random=GKRandomSource.sharedRandom().nextInt(upperBound:quoteArray.count)
        while list.contains(random){random = GKRandomSource.sharedRandom().nextInt(upperBound:quoteArray.count)}
        list.append(random)
        if list[0]==999{list.removeFirst()}
        if list.count==quoteArray.count
        {
            print(list);last=list[list.count-1];print(last)
            repeat
            {list.removeAll();list.append(GKRandomSource.sharedRandom().nextInt(upperBound: quoteArray.count))}
                while last==list[0];print(list[0])};return quoteArray[list[list.count-1]]
    }
}

I also modified the test print statements. Now the list only prints if all facts are displayed. I then print the last number of the list before it refreshes as well as print the first number of the next list, to confirm that the pertaining number is not duplicated.

Everything looks okay now...I think...

XD