11

I have a large NSArray of names, I need to get random 4 records (names) from that array, how can I do that?

Tunyk Pavel
  • 2,473
  • 6
  • 31
  • 46

3 Answers3

21
#include <stdlib.h>

NSArray* names = ...;
NSMutableArray* pickedNames = [NSMutableArray new];

int remaining = 4;

if (names.count >= remaining) {
    while (remaining > 0) {
       id name = names[arc4random_uniform(names.count)];

       if (![pickedNames containsObject:name]) {
           [pickedNames addObject:name];
           remaining--;
       }
    }
}
Andrey Zverev
  • 4,409
  • 1
  • 28
  • 34
Julio Gorgé
  • 10,056
  • 2
  • 45
  • 60
  • 3
    True, I just replaced rand() by arc4random() which is superior and does not need seeding. – Julio Gorgé Apr 30 '11 at 06:03
  • Thank you, it works fine. But, I think that pickedNames should be NSMutableArray – Tunyk Pavel Apr 30 '11 at 06:05
  • Yes, should be NSMutableArray indeed. – Julio Gorgé Apr 30 '11 at 06:06
  • 1
    This could be optimised for some situations by making names be a mutable array, and removing names once they are picked (and you could also then take out the containsObject check). Imagine if you have (for example) a situation where you are picking 10,000 items from an array with 10,001 members. In the current answer the while loop will do many thousands of unsuccessful iterations. With this optimisation it will do zero unsuccessful iterations. (Obviously in my extreme example a more efficient way could be to chose one item and remove it, but it is just for illustration) – narco Apr 17 '16 at 09:13
2

I made a caregory called NSArray+RandomSelection. Just import this category into a project, and then just use

NSArray *things = ...
...
NSArray *randomThings = [things randomSelectionWithCount:4];

Here's the implementation:

NSArray+RandomSelection.h

@interface NSArray (RandomSelection)
    - (NSArray *)randomSelectionWithCount:(NSUInteger)count;
@end

NSArray+RandomSelection.m

@implementation NSArray (RandomSelection)

- (NSArray *)randomSelectionWithCount:(NSUInteger)count {
    if ([self count] < count) {
        return nil;
    } else if ([self count] == count) {
        return self;
    }

    NSMutableSet* selection = [[NSMutableSet alloc] init];

    while ([selection count] < count) {
        id randomObject = [self objectAtIndex: arc4random() % [self count]];
        [selection addObject:randomObject];
    }

    return [selection allObjects];
}

@end
mopsled
  • 8,445
  • 1
  • 38
  • 40
  • 2
    The handling for random selection larger then the array is incorrect. I am using the following instead of the first 5 lines: `if ([self count] < count) { count = [self count]; }` – miho Oct 04 '12 at 09:01
  • if you have less than 'count' unique elements in the array, you'll end up in and endless loop – Pieter Sep 26 '13 at 12:33
2

If you prefer a Swift Framework that also has some more handy features feel free to checkout HandySwift. You can add it to your project via Carthage then use it like this:

import HandySwift    

let names = ["Harry", "Hermione", "Ron", "Albus", "Severus"]
names.sample() // => "Hermione"

There is also an option to get multiple random elements at once:

names.sample(size: 3) // => ["Ron", "Albus", "Harry"]

I hope this helps!

Jeehut
  • 20,202
  • 8
  • 59
  • 80
  • This doesn't answer the question as you need to show how the list of random numbers can be used to select the names in the large array in order to creat the picked names array. – Droppy Jun 09 '16 at 21:13
  • 1
    It seems my answer was misleading. I've updated it to use a names array as an example instead of a numbers array. Works the same, just a different type of Array. I hope it's clear now. – Jeehut Jun 09 '16 at 21:21