This question comes up on this board at least once a week. A while back I wrote some code to select random words or phrases from an array, and only select any given word/phrase once. Here is the code you need:
And put these methods (and one C function) into the .m file of the same class:
Code:
//This is the "sort" function that puts the items in random order.
//It uses a random number generator to decide the ordering of items in the array
NSInteger randomize(id num1, id num2, void *context)
{
int rand = arc4random() %2;
if (rand)
return NSOrderedAscending;
else
return NSOrderedDescending;
}
//This function populates "randomArray" with an array of indexes in random order.
//Once the getRandomWord method returns nil, call resetRandomArray to
//repopulate the randomArray with the same list of words in a different random order.
- (void) resetRandomArray;
{
//Copy over our starting "array" containing all our indexes in order.
[randomArray setArray: array ];
//Use a trick to "sort" the array into random order.
//this trick involves using a sort function "randomize" that puts
//the items in random order.
[randomArray sortUsingFunction: randomize context:NULL];
}
-(NSString*) getRandomWord;
{
if ([randomArray count] ==0)
return nil;
NSString* result;
NSInteger randomIndex = [[randomArray lastObject] intValue];;
[randomArray removeLastObject];
result = [words objectAtIndex: randomIndex];
return result;
}
-(void) buildRandomWordArray;
{
NSInteger index;
NSError* theError;
NSString *path = [[NSBundle mainBundle] pathForResource:@"words" ofType:@"txt"];
NSString* text = [NSString stringWithContentsOfFile: path
encoding: NSUTF8StringEncoding
error: &theError];
self.words = [text componentsSeparatedByString: @"\n"];
int arraySize = [words count];
self.array = [NSMutableArray arrayWithCapacity:arraySize];
self.randomArray = [NSMutableArray arrayWithCapacity:arraySize];
//This code fills "array" with index values from 0 to the number of elements in the "words" array.
for (index = 0; index<arraySize; index++)
[array addObject: [NSNumber numberWithInt: index]];
[self resetRandomArray];
// for (index = 0; index<=arraySize; index++)
// NSLog(@"Random word: %@", [self getRandomWord]);
}
To use it:
-Create a file called words.txt containing a list of words or phrases separated by carriage returns.
-Drag that file into the resources portion of your project, so it will be included in the bundle.
-Call [self buildRandomWordArray] to do the initial setup. That reads the file and populates an array of random indexes.
-Call [self getRandomWord] to get a new random word or phrase. That word/phrase will be deleted from the list and not used again. Once the list is exhausted, the getRandomWord will return nil;
-To start over and get the same list of words in a different order, call [self resetRandomArray];
Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.
Just a sidenote of sorts:
If you don't need to reuse this code too much, a (possibly) easier way is to populate an NSMutableArray, shuffle it, extract an element, and remove it. For instance:
Code:
- (id)randomObject { // You could also pass in an array as an argument
self.randomArray = [[self.randomArray shuffledArray] mutableCopy];
NSInteger index = arc4random() % [self.randomArray count] - 1;
return [self.randomArray objectAtIndex:index];
[self.randomArray removeObjectAtIndex:index];
}
Obviously, if you were to reuse this code, you would have to repopulate the array—it's a tradeoff between performance or less code.
__________________
If I have helped you, please consider donating. I use PayPal. It would mean a lot to me!
Hi Duncan !
"Generating random, non-repeating text" is exactly what I would like to do within an app I'm trying to write. I'm an absolute beginner regarding this, so I hope you have some patience with my questions ;-) I have tried to apply your solution in my app, but I don get it to work. I'm using a plist instead of a txt-file, UIPicker and Button. I don't get how to apply your random stuff, so I get it to read my plist randomly and send it to the UIPicker when I press the button via IBAcation ? How do trigger this ?
//J-E
Duncan,
I have 20 UIViews that I want to show randomly via a "next" button. I have it working but it does repeat:-( Can you pls. help me to make it non-repeat?
Also, after doing all 20 views, can it be made to go to a new page or xib?
Thanks in advance,
laslov
(Here's the code)
-(IBAction)next {
int UIView = random() % 20;
switch (UIView) {
You can't just make a UIView an integer and then hope to switch through it. Wonder that it doesn't crash at all.
You'll need to give each view an identifier—either an index, if you're storing them in an NSArray (you don't need to actually create the index…), or a tag (which you do have to assign) if they're all in a XIB. You get the random index with the following code:
Code:
int index = arc4random() % 20;
If you're using tags, you should start numbering from 1 (because the default is zero and you want to avoid conflicts in the tag), so add one to that result. Then grab the view you want:
Just a sidenote of sorts:
If you don't need to reuse this code too much, a (possibly) easier way is to populate an NSMutableArray, shuffle it, extract an element, and remove it. For instance:
Code:
- (id)randomObject { // You could also pass in an array as an argument
self.randomArray = [[self.randomArray shuffledArray] mutableCopy];
NSInteger index = arc4random() % [self.randomArray count] - 1;
return [self.randomArray objectAtIndex:index];
[self.randomArray removeObjectAtIndex:index];
}
Obviously, if you were to reuse this code, you would have to repopulate the array—it's a tradeoff between performance or less code.
In fact, you could make the case that your approach is cleaner and simpler in all cases. Just do the following:
Create a "master" array of objects that you want to shuffle. (one-time setup)
Use mutableCopy to save a mutable copy of the array. Call it randomItems. You could use a retained property to keep track of it:
-(id) getRandomItem
{
if (![randomItems count])
return nil;
int index;
index = arc4random() % [randomItems count];
id item = [randomItems objectAtIndex: index];
[randomItems removeItemAtIndex: index];
return item;
}
When getRandomItem returns nil, just call createRandomArray again to repopulate the random array.
The above code assumes that the masterArray is kept for life of the program. That way, masterArray retains the objects so they don't get deallocated when you remove them from the randomItems array.
Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.
This question comes up on this board at least once a week. A while back I wrote some code to select random words or phrases from an array, and only select any given word/phrase once. Here is the code you need:
And put these methods (and one C function) into the .m file of the same class:
Code:
//This is the "sort" function that puts the items in random order.
//It uses a random number generator to decide the ordering of items in the array
NSInteger randomize(id num1, id num2, void *context)
{
int rand = arc4random() %2;
if (rand)
return NSOrderedAscending;
else
return NSOrderedDescending;
}
//This function populates "randomArray" with an array of indexes in random order.
//Once the getRandomWord method returns nil, call resetRandomArray to
//repopulate the randomArray with the same list of words in a different random order.
- (void) resetRandomArray;
{
//Copy over our starting "array" containing all our indexes in order.
[randomArray setArray: array ];
//Use a trick to "sort" the array into random order.
//this trick involves using a sort function "randomize" that puts
//the items in random order.
[randomArray sortUsingFunction: randomize context:NULL];
}
-(NSString*) getRandomWord;
{
if ([randomArray count] ==0)
return nil;
NSString* result;
NSInteger randomIndex = [[randomArray lastObject] intValue];;
[randomArray removeLastObject];
result = [words objectAtIndex: randomIndex];
return result;
}
-(void) buildRandomWordArray;
{
NSInteger index;
NSError* theError;
NSString *path = [[NSBundle mainBundle] pathForResource:@"words" ofType:@"txt"];
NSString* text = [NSString stringWithContentsOfFile: path
encoding: NSUTF8StringEncoding
error: &theError];
self.words = [text componentsSeparatedByString: @"\n"];
int arraySize = [words count];
self.array = [NSMutableArray arrayWithCapacity:arraySize];
self.randomArray = [NSMutableArray arrayWithCapacity:arraySize];
//This code fills "array" with index values from 0 to the number of elements in the "words" array.
for (index = 0; index<arraySize; index++)
[array addObject: [NSNumber numberWithInt: index]];
[self resetRandomArray];
// for (index = 0; index<=arraySize; index++)
// NSLog(@"Random word: %@", [self getRandomWord]);
}
To use it:
-Create a file called words.txt containing a list of words or phrases separated by carriage returns.
-Drag that file into the resources portion of your project, so it will be included in the bundle.
-Call [self buildRandomWordArray] to do the initial setup. That reads the file and populates an array of random indexes.
-Call [self getRandomWord] to get a new random word or phrase. That word/phrase will be deleted from the list and not used again. Once the list is exhausted, the getRandomWord will return nil;
-To start over and get the same list of words in a different order, call [self resetRandomArray];
Im alittle confused, how do I connect the code to a label so it changes the label into random generated text from a list which won't be repeated? I believe I'll have to connect it by a Outlet or?
Im alittle confused, how do I connect the code to a label so it changes the label into random generated text from a list which won't be repeated? I believe I'll have to connect it by a Outlet or?
It depends on what you want to do and how your app is set up.
When do you want the label to change? On a button click?
If so, you should create an IBAction method that's invoked on the button click and an outlet for your label.
In your action, get the next random string and assign it to the label.
Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.
Hi guys! I need some help with this. I'm not a programmer I'm more of a designer. I'm making a quote app. This seems perfect for what I'm trying to do. I designed the app in Photoshop and I placed the elements in Xcode using Interface Builder.
As I said I designed a simple quote app. Basically is one page, one button. When I push the button the app takes a quote from my .txt file and shows it.
How do I use this code in my app? Can you show me some examples? Where do I start?
Maybe it's a lot to ask. If someone has a few minutes to explain this for me it would be awesome.
Thank You!
*Edit
I got half of it. I did the IBOutlet and the IBAction thing in my .h file. Now I need to figure out the .m file. I inserted the code you gave in there and I have no errors. Any help?
Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.