Getting a random object from NSArray without duplication - objective-c

I have an NSArray with 17 objects, something like this:
NSArray *objArray = [[NSArray alloc]initWithObjects: #"1",#"2",#"3",#"4",#"5",#"6"
,#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17", nil];
and an int with a random number as follows:
int random = arc4random()%17+1;
I want to get a random object from this NSArray without it being a duplicate, even if I closed the app (maybe by using NSUserDefaults).
If I've gotten all the objects I want to generate a new random sequence for the same objects.

You could do this by making a mutable copy of the array, and after you make a random selection from that array, remove that same object. When you want to save the array, save the mutable array itself, so can resume where you left off when the app restarts. This little test app does that, and just logs the value of the random pick:
- (void)viewDidLoad {
[super viewDidLoad];
self.objArray = #[#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17"];
self.mut = [self.objArray mutableCopy];
}
-(IBAction)pickNumber:(id)sender {
int index = arc4random_uniform(self.mut.count);
NSLog(#"%#", self.mut[index]);
[self.mut removeObjectAtIndex:index];
if (self.mut.count == 0) {
self.mut = [self.objArray mutableCopy];
NSLog(#"*******************");
}
}

As a starting point, you could shuffle your array:
+ (NSArray *)arrayByShufflingArray:(NSArray *)array
{
// Fisher-Yates algorithm
NSMutableArray *result = [array mutableCopy];
NSUInteger count = [result count];
for (NSInteger i = ((NSInteger) count) - 1; i > 0; i--) {
NSUInteger firstIndex = (NSUInteger)i;
NSUInteger secondIndex = arc4random() % (NSUInteger)(i + 1);
[result exchangeObjectAtIndex:firstIndex withObjectAtIndex:secondIndex];
}
return result;
}
Step through each shuffled element and when you get to the end, reshuffle.
It can still happen that an item is selected twice in a row when the last item of one shuffle is the same as the first item in the next shuffle. If you want to avoid this you'll have to add some additional code.

Just Copy and Paste
-(NSMutableArray*)getRandomValueFromArray:(NSMutableArray*)arrAllData randomDataCount:(NSInteger)count {
NSMutableArray *arrFilterData = [[NSMutableArray alloc]init];
for(int i=0; i<count; i++){
NSInteger index = arc4random() % (NSUInteger)(arrAllData.count);
[arrFilterData addObject:[arrAllData objectAtIndex:index]];
[arrAllData removeObjectAtIndex:index];
}
return arrFilterData;
}
Note: count = number of random values you want to fetch from array.

Related

Iterate through nested arrays to grab the first object of each array, then the second, etc

I have nested Arrays,
For example:
[[1,2,3,4,5], [A,B,C,D,E], [Z,Y,X,W,V]
I want to iterate through this array and create a new array, that looks like this:
[1,A,Z,2,B,Y,3,C,X,4,D,W,5,E,V]
I was initially thinking of using nested For loops, e.g.:
int index = 0;
int stop = [[arrays objectAtIndex:0] count];
NSMutableArray* finalArray = [NSMutableArray new];
while(index < stop)
{
for(id array in images)
{
[finalArray addObject:[array objectAtIndex:index]];
}
index++;
}
What would be the most efficient way of doing this?
I don't believe your code will actually generate what you mean (it won't actually compile because NSArray doesn't have addObject:). What you want is a Zipper:
NSArray *Zip(NSArray *arrays) {
if ([arrays count] == 0) {
return #[];
}
NSMutableArray *result = [NSMutableArray new];
NSInteger minCount = NSIntegerMax;
for (NSArray *array in arrays) {
minCount = MIN(minCount, [array count]);
}
for (NSInteger i = 0; i < minCount; i++) {
for (NSArray *array in arrays) {
[result addObject:array[i]];
}
}
return result;
}
Can't comment as I don't have 50 rep, so has to be as an answer!
Have you tried using loops such as
Dim i As Integer, j As Integer
For i = 1 To 5
For j = 1 To 3
Select Item i from array j
Next j
Next i

Array - find how many times an object repeats consecutively

My array objects are as follows:
10,10,10
20,23,14
10,10,10
10,10,10
10,10,10
32,23,42
32,23,42
10,10,10
32,23,23
32,23,23
How can I go through this array and find out how many times the same object repeats sequentially, then add a , and the number of times it repeats?
Then save a new array with objects like:
10,10,10,1
20,23,14,1
10,10,10,3
32,23,42,2
10,10,10,1
32,23,23,2
Any help would be appreciated.
Thanks!
Try this:
NSMutableArray *outArray = [[NSMutableArray alloc] init];
for (NSUInteger j = 0; j < [theArray count]; j++) {
id object = [theArray objectAtIndex:j];
NSUInteger repeats = 1;
while (j + 1 < [theArray count] && [[theArray objectAtIndex:j + 1] isEqual:object]) {
j++;
repeats++;
}
[outArray addObject:object];
[outArray addObject:[NSNumber numberWithUnsignedInteger:repeats]];
}
return outArray;
This can also be done in place if the input array is mutable. I leave that as an exercise for the reader.
Break up every three integers into its own array (make sure they are strings).
Then iterate through each one of those arrays, and input into an NSMutableDictionary, the key is the string (your number), the value is a counter (if seen once, add 1, etc...)
Keep a pointer to the highest key (if newCount > highestCountPointer, then highestCountPointer=newCount)
At the end of that iteration, add the number that the highestCountPoints to to the end of the array.
I'm not an Objective C programmer, so please pardon any language gaffes. Something like the following should do the job:
NSMutableArray *result = [[NSMutableArray alloc] init];
id pending = nil;
NSUInteger count = 0;
for (NSUInteger i = 0; i < [theArray count]; i++) {
id object = [theArray objectAtIndex:i];
if ([object isEqual:pending]) {
count++;
} else {
if (pending != nil) {
[result addObject:[NSString stringWithFormat:#"%#,%d", pending, count]];
}
pending = object;
count = 1;
}
}
if (pending != nil) {
[result addObject:[NSString stringWithFormat:#"%#,%d", pending, count]];
}
Just run "uniq -c" from command line :)

Selecting array elements using a binary pattern as the selector

I have an NSArray, and I've calculated a list of integers that represent (in binary) the elements I need to pull out of the array into a new array.
For example, I have 7, 11, and 13, whose bit patterns are 000111, 001011, and 001101. I want to grab three arrays, made of elements 0,1,2, then elements 0,1,3, and then 0,2,3 out of the main array.
Construct an NSIndexSet from the bit patterns you have:
#implementation NSIndexSet (NonContiguous)
+ (instancetype)indexSetFromMask:(NSUInteger)mask
{
NSMutableIndexSet * set = [NSMutableIndexSet indexSet];
for( NSUInteger i = 0; i < (sizeof(NSUInteger) * 8); i++ ){
if( mask & (1l << i) ){
[set addIndex:i];
}
}
return set;
}
#end
Then use objectsAtIndexes:
[origArray objectsAtIndexes:[NSIndexSet indexSetFromMask:7]];
// etc.
Assuming you want the output to be in the form [[a,b,c],[a,b,d],[a,c,d]] for the example, you could do something like this:
NSArray *sourceArray = [[NSArray alloc] initWithObjects:#"a",#"b",#"c",#"d",...,nil];
NSArray *grabArray = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:7],[NSNumber numberWithInt:11],[NSNumber numberWithInt:13],...,nil];
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
Then for each element of grabArray, add the correct elements to resultArray:
for (NSNumber num in grabArray) {
int n = [num intValue];
int bit = floor(log2(n)); //get highest bit in the current number
NSMutableArray *curr = [[NSMutableArray alloc] init];
while (n>0) {
if (n>pow(bit,2)) { //if this bit is a 1
[curr addObject:[sourceArray objectAtIndex:bit]];
}
n-=pow(bit,2);
bit-=1;
}
[resultArray addObject:curr];
}
Then resultArray should be the array you are looking for. It will add the objects in reverse order, so if order matters you would want to use [curr insertObject: [sourceArray objectAtIndex:bit] atIndex:0]; instead.

How to generate non repeating random number

I am trying to randomize numbers in an array. I am able to do that using arc4random() % [indexes count]
My problem is - If an array consists of 20 items, every time the array shuffles, in a batch of 5, different number should appear. Example :
first shuffle: 1,4,2,5,6.
second shuffle: 7,12,9,15,3
-(IBAction)randomNumbers:(UIButton *)sender
{
int length = 10; // int length = [yourArray count];
NSMutableArray *indexes = [[NSMutableArray alloc] initWithCapacity:length];
for (int i=0; i<5; i++)
[indexes addObject:[NSNumber numberWithInt:i]];
NSMutableArray *shuffle = [[NSMutableArray alloc] initWithCapacity:length];
while ([indexes count])
{
int index = arc4random() % [indexes count];
[shuffle addObject:[indexes objectAtIndex:index]];
[indexes removeObjectAtIndex:index];
}
// for (int i=0; i<[shuffle count]; i++)
NSLog(#"%#", [shuffle description]);
}
As per your requirement....kindly check this code
Make this a property
#synthesize alreadyGeneratedNumbers;
Add these methods in your .m
-(int)generateRandomNumber{
int TOTAL_NUMBER=20;
int low_bound = 0;
int high_bound = TOTAL_NUMBER;
int width = high_bound - low_bound;
int randomNumber = low_bound + arc4random() % width;
return randomNumber;
}
-(IBAction)randomNumbers:(UIButton *)sender
{
NSMutableArray *shuffle = [[NSMutableArray alloc] initWithCapacity:5];
BOOL contains=YES;
while ([shuffle count]<5) {
NSNumber *generatedNumber=[NSNumber numberWithInt:[self generateRandomNumber]];
//NSLog(#"->%#",generatedNumber);
if (![alreadyGeneratedNumbers containsObject:generatedNumber]) {
[shuffle addObject:generatedNumber];
contains=NO;
[alreadyGeneratedNumbers addObject:generatedNumber];
}
}
NSLog(#"shuffle %#",shuffle);
NSLog(#"Next Batch");
if ([alreadyGeneratedNumbers count] >= TOTAL_NUMBER) {
NSLog(#"\nGame over, Want to play once again?");//or similar kind of thing.
[alreadyGeneratedNumbers removeAllObjects];
}
}
Still I feel you need to some changes like
it will give you correct value, but what if user pressed 5th time?
out of 20 numbers you already picked 4 sets of 5 number, on on 6th time it will be in loop to search for next set of numbers and will become infinite.
So what you can do is, keep the track of shuffle and once it reaches the limit i.e, 20/5=4 disable the random button.
Declare array that contains already generated number in extension or header file
#property (strong, nonatomic)NSMutableArray *existingNums;
#property (assign, nonatomic)NSInteger maxLimit;
#property (assign, nonatomic)NSInteger minLimit;
Then implement given code in implementation file
#synthesize existingNums;
#synthesize maxLimit;
#synthesize minLimit;
- (NSInteger)randomNumber {
if(!existingNums)
existingNums = [NSMutableArray array];
while (YES) {
NSNumber *randonNum = [NSNumber numberWithInteger:minLimit+arc4random()%maxLimit];
if([existingNums containsObject:randonNum]) {
if([existingNums count] == (maxLimit - minLimit))
return -1; // All possible numbers generated in the given range
continue;
}
[existingNums addObject:randonNum];
return [randonNum integerValue];
}
return -1; // return error
}
Hope this will help you :)
This one works for me:
NSMutableArray *numbers = [NSMutableArray new];
BOOL addElement = YES;
int limit = 100; // Range from 0 to 36
int numElem = 10; // Number of elements
do
{
int ranNum = (arc4random() % limit) +1;
if ([numbers count] < numElem) {
for (NSNumber *oneNumber in numbers) {
addElement =([oneNumber intValue] != ranNum) ? YES:NO;
if (!addElement) break;
}
if (addElement) [numbers addObject:[NSNumber numberWithInt:ranNum]];
} else {
break;
}
} while (1);
NSLog(#"%#",numbers);
The problem with all these answers is that you need to review your previous generated random numbers and that takes extra time if you need a large number of random integers.
Another solution is using cryptography:
Generate a random key
Iterate between 0..n
Encrypt each integer and apply modulo the number of alternatives do you want to use to the output of the function.
There are some extra details to take into account that don't matter for your case.

Remove items in a for loop without side effects?

Can I remove items that I am looping through in an Objective-C for loop without side effects?
For example, is this ok?
for (id item in items) {
if ( [item customCheck] ) {
[items removeObject:item]; // Is this ok here?
}
No, you'll get an error if you mutate the array while in a fast enumeration for loop. Make a copy of the array, iterate over it, and remove from your original.
NSArray *itemsCopy = [items copy];
for (id item in itemsCopy) {
if ( [item customCheck] )
[items removeObject:item]; // Is this ok here
}
[itemsCopy release];
Nope:
Enumeration is “safe”—the enumerator has a mutation guard so that if you attempt to modify the collection during enumeration, an exception is raised.
Options for changing an array that you want to enumerate through are given in Using Enumerators: either copy the array and enumerate through, or build up an index set that you use after the loop.
you can remove like this:
//Create array
NSMutableArray* myArray = [[NSMutableArray alloc] init];
//Add some elements
for (int i = 0; i < 10; i++) {
[myArray addObject:[NSString stringWithFormat:#"i = %i", i]];
}
//Remove some elements =}
for (int i = (int)myArray.count - 1; i >= 0 ; i--) {
if(YES){
[myArray removeObjectAtIndex:i];
}
}