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 Objective-C Creating a Data Model Finishing Up Our Model

Rohit Das
Rohit Das
4,356 Points

Trying to implement better algorithm for random fact chosen. Please help!

So when my FunFacts app is completed, I don't want people to click "show me a random fact" multiple times and the same fact coincidentally come up. My goal is to show all facts atleast once before re-using any of them. What am i doing wrong? The code runs and compiles but my algorithm isn't being implemented! Here is my code:

FactBook.h :

 #import <Foundation/Foundation.h>



 @interface FactBook : NSObject {

     NSMutableArray *used;

 }


 @property (strong, nonatomic) NSMutableArray *used;

 @property (strong, nonatomic) NSArray *facts;


 - (NSString *) randomFact;


 @end

FactBook.m:

 #import "FactBook.h"

 @implementation FactBook

 - (instancetype)init
 {
     self = [super init];
     if (self) {
         _facts = [[NSArray alloc] initWithObjects:
                   @"Ants stretch when they wake up.",

                   @"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.",

                   nil];

         _used = [[NSMutableArray alloc] init];

     }
     return self;
 }

 - (NSString *) randomFact {

     if (used.count == self.facts.count) {

         for (int j = 0; j < self.facts.count; j++) {

             [used removeObjectAtIndex:j];

         }

         int random = arc4random_uniform((int)self.facts.count);

         if ([used containsObject: [NSNumber numberWithInt: random]])
         {
             [self randomFact];
         } 
         else {
            [used addObject:[NSNumber numberWithInt: random]];
        }
        return [self.facts objectAtIndex:random];

    }

    else {

        int random = arc4random_uniform((int)self.facts.count);

        if ([used containsObject: [NSNumber numberWithInt: random]])
        {
            [self randomFact];
        }

        else {
             [used addObject:[NSNumber numberWithInt: random]];
         }

         return [self.facts objectAtIndex:random];
     }

 }



 @end

Please help!!!!!! Pasan Premaratne Amit Bijlani

Sorry my code isnt showing up as code!! not sure what to do about that!

William Li
William Li
Courses Plus Student 26,868 Points

fixed the code highlighting for you.

For future reference, this is how you post with Markdown code block.

Code block on Treehouse

Rohit Das
Rohit Das
4,356 Points

Thank you William Li for the tip!

1 Answer

Pasan Premaratne
STAFF
Pasan Premaratne
Treehouse Teacher

Rohit Das,

I didn't run the code to actually debug anything but I can see at least one point where it will crash:

for (int j = 0; j < self.facts.count; j++) {
    [used removeObjectAtIndex:j];
}

Your iterator increases up to a point where j = 10 but your used array is decreasing in size as you remove objects. Once you're past the mid way point, the used array will have a count of 5 (max index of 4) and j will equal 5. Crash!

The other issue is that for each fact you're doing a lot of work - When a random number is generated, the used array is checked and if the number exists, a random number is generated again. This process continues until you have a unique number. In a somewhat worst case scenario, for each time you push the button, the number generator will have to call randomFact 10 times before you get a unique value. For an array this small it's trivial...what if your array was 100 facts, or a thousand. It's a lot of mostly unnecessary work.

Potential easier implementation.

  • Create a mutableArray, let's call it tempFacts, and initialize it with the facts from the facts array.
  • Generate a random number using tempFacts.count as the bounds.
  • Display the fact you get using that number as an index, and then subsequently remove that fact from the tempFacts array.

This way you're guaranteed not to get the same fact until you wind down that array and use all the facts.

  • When tempFacts.count == 0, add all the facts back in.
Rohit Das
Rohit Das
4,356 Points

Thanks! That does seem like a much simpler implementation, however I did run into another issue, could you please take a look?

I created tempFacts in the exact same way you created facts, but after a while of clicking "show another fact" (AND the implementation not working, it was showing the same fact multiple times), I got an error saying this:

FunFacts[809:155176] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'

- (NSString *) randomFact {
    int random = arc4random_uniform((int)self.tempFacts.count);
    [self.tempFacts removeObjectAtIndex:random];
    if (self.tempFacts.count == 0){
        self.tempFacts = self.facts;
    }
    return [self.facts objectAtIndex:random];

}

Pasan Premaratne Amit Bijlani Thanks!

Pasan Premaratne
Pasan Premaratne
Treehouse Teacher
- (NSString *)randomFact {
    if (tempArray.count <= 0) {
        tempArray = [NSMutableArray arrayWithArray:_facts];
    }

    int random = arc4random_uniform((int)tempArray.count);
    NSString *fact = [tempArray objectAtIndex:random];
    [tempArray removeObjectAtIndex:random];
    return fact;
}