Picking a Random Object in an NSArray
Objective CCocoaObjective C Problem Overview
Say I have an array with objects, 1, 2, 3 and 4. How would I pick a random object from this array?
Objective C Solutions
Solution 1 - Objective C
@Darryl's answer is correct, but could use some minor tweaks:
NSUInteger randomIndex = arc4random() % theArray.count;
Modifications:
- Using
arc4random()
overrand()
andrandom()
is simpler because it does not require seeding (callingsrand()
orsrandom()
). - The modulo operator (
%
) makes the overall statement shorter, while also making it semantically clearer.
Solution 2 - Objective C
This is the simplest solution I could come up with:
id object = array.count == 0 ? nil : array[arc4random_uniform(array.count)];
It's necessary to check count
because a non-nil
but empty NSArray
will return 0
for count
, and arc4random_uniform(0)
returns 0
. So without the check, you'll go out of bounds on the array.
This solution is tempting but is wrong because it will cause a crash with an empty array:
id object = array[arc4random_uniform(array.count)];
For reference, here's the [documentation][1]:
u_int32_t
arc4random_uniform(u_int32_t upper_bound);
arc4random_uniform() will return a uniformly distributed random number less than upper_bound.
The man page doesn't mention that arc4random_uniform
returns 0
when 0
is passed as upper_bound
.
Also, arc4random_uniform
is defined in <stdlib.h>
, but adding the #import
wasn't necessary in my iOS test program.
[1]: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/arc4random_uniform.3.html "Official Apple Man Page for arc4random"
Solution 3 - Objective C
Perhaps something along the lines of:
NSUInteger randomIndex = (NSUInteger)floor(random()/RAND_MAX * [theArray count]);
Don't forget to initialize the random number generator (srandomdev(), for example).
NOTE: I've updated to use -count instead of dot syntax, per the answer below.
Solution 4 - Objective C
@interface NSArray<ObjectType> (Random)
- (nullable ObjectType)randomObject;
@end
@implementation NSArray (Random)
- (nullable id)randomObject
{
id randomObject = [self count] ? self[arc4random_uniform((u_int32_t)[self count])] : nil;
return randomObject;
}
@end
Edit: Updated for Xcode 7. Generics, nullability
Solution 5 - Objective C
Generate a random number and use it as the index. Example:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
@autoreleasepool {
NSArray *array = [NSArray arrayWithObjects: @"one", @"two", @"three", @"four", nil];
NSUInteger randomNumber;
int fd = open("/dev/random", O_RDONLY);
if (fd != -1) {
read(fd, &randomNumber, sizeof(randomNumber));
close(fd);
} else {
fprintf(stderr, "Unable to open /dev/random: %s\n", strerror(errno));
return -1;
}
double scaledRandomNumber = ((double)randomNumber)/NSUIntegerMax * [array count];
NSUInteger randomIndex = (NSUInteger)floor(scaledRandomNumber);
NSLog(@"random element: %@", [array objectAtIndex: randomIndex]);
}
return 0;
}
Solution 6 - Objective C
srand([[NSDate date] timeIntervalSince1970]);
int inx =rand()%[array count];
inx is the random number.
where srand() can be anywhere in the program before the random picking function.
Solution 7 - Objective C
ObjectType *objectVarName = [array objectAtIndex:arc4random_uniform((int)(array.count - 1))];
if you want to cast that to an int, here's the solution for that (useful for when you need a random int from an array of non-sequential numbers, in the case of randomizing an enum call, etc)
int intVarName = (int)[(NSNumber *)[array objectAtIndex:arc4random_uniform((int)(array.count - 1))] integerValue];
Solution 8 - Objective C
In Swift 4:
let array = ["one","two","three","four"]
let randomNumber = arc4random_uniform(UInt32(array.count))
array[Int(randomNumber)]