How to search an array cyclically? - objective-c

This is more a curiosity question than a pressing one. This question is looking for a better way to do the following, meaning without using two for loops.
I have an NSArray *array of NSStrings and a method -(BOOL)isGoodString:(NSString *)string. I want to jump into the array at a random spot and find the first good string, wrapping around the end if necessary. However, it may be that there is no good string, so I need to know that as well. Here's the current implementation:
-(NSString *)randomGoodString {
int N = [array count]
int start = arc4random() % N;
for (int j=start; j<N ; ++j) {
if isGoodString([array objectAtIndex:j]) {
return [array objectAtIndex:j];
}
}
for (j=0; j<start ; ++j) {
if isGoodString([array objectAtIndex:j]) {
return [array objectAtIndex:j];
}
}
return #"";
}
Any suggestions? Efficiency would be nice, but since this really is more for curiosity, anything that works in finite time would be nice to hear about.

Eliminate your second search loop by using a modulus:
-(NSString *)randomGoodString {
int N = [array count]
int start = arc4random() % N;
for (int j=0; j<N ; ++j) {
index = (j+start)%N;
if isGoodString([array objectAtIndex:index]) {
return [array objectAtIndex:index];
}
}
return #"";
}

Related

Merge Sorting in Objective C

I am trying to implement merge sort in objective -C.
This is a similar question asked in the following link , did not find it answered so creating a new question.
Merge sort in Objective-C
This is what I have tried ,
-(NSArray *)mergeSort:(NSArray *)unsortedArray {
if ([unsortedArray count] < 2)
return unsortedArray;
long mid = [unsortedArray count] / 2;
NSRange left = NSMakeRange(0, mid);
NSRange right = NSMakeRange(mid, [unsortedArray count] - mid);
NSArray *rightArray = [unsortedArray subarrayWithRange:right];
NSArray *leftArray = [unsortedArray subarrayWithRange:left];
NSArray *resultArray = [self merge:leftArray andRight:rightArray];
return resultArray;
}
-(NSArray *)merge:(NSArray *)leftArray andRight:(NSArray *)rightArray {
NSMutableArray *result = [NSMutableArray array];
int right = 0;
int left = 0;
while (left < [leftArray count] && right < [rightArray count]) {
NSComparisonResult comparisonResult = [leftArray[left] compare:rightArray[right]];
if (comparisonResult != NSOrderedDescending) {
[result addObject:[leftArray objectAtIndex:left++]];
} else {
[result addObject:[rightArray objectAtIndex:right++]];
}
/*if ([[leftArray objectAtIndex:left] intValue] < [[rightArray objectAtIndex:right] intValue]) {
[result addObject:[leftArray objectAtIndex:left++]];
//left++;
} else {
[result addObject:[rightArray objectAtIndex:right++]];
//right++;
}*/
}
NSRange leftRange = NSMakeRange(left, [leftArray count] - left);
NSRange rightRange = NSMakeRange(right, [rightArray count] - right);
NSArray * newRight = [rightArray subarrayWithRange:rightRange];
NSArray * newLeft = [leftArray subarrayWithRange:leftRange];
newLeft = [result arrayByAddingObjectsFromArray:newLeft];
return [newLeft arrayByAddingObjectsFromArray:newRight];
}
Kindly let me know if anyone has any other approaches for merge sort.
I dont understand why do you people want the long way.. Even though there are already easy way of doing this...
I made one myself hope this will help you..
- (NSArray *)arrayMergeSort:(NSArray *)targetArray
{
if (targetArray.count < 2)
return targetArray;
long midIndex = targetArray.count/2;
NSArray *arrayLeft = [targetArray subarrayWithRange:NSMakeRange(0, midIndex)];
NSArray *arrayRight= [targetArray subarrayWithRange:NSMakeRange(midIndex, targetArray.count - midIndex)];
return [self arrayMerge: [self arrayMergeSort:arrayLeft] : [self arrayMergeSort:arrayRight]];
}
For arrange merge:
- (NSArray *)arrayMerge:(NSArray *)arrayLeft :(NSArray *)arrayRight
{
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
int i = 0, j = 0;
while (i < arrayLeft.count && j < arrayRight.count)
[resultArray addObject:([arrayLeft[i] intValue] < [arrayRight[j] intValue]) ? arrayLeft[i++] : arrayRight[j++]];
while (i < arrayLeft.count)
[resultArray addObject:arrayLeft[i++]];
while (j < arrayRight.count)
[resultArray addObject:arrayRight[j++]];
return resultArray;
}
And using it like:
//Sample array
NSArray *activeArray = #[#101,#201,#301,#121,#11,#123,#21,#14,#32,#76,#89,#987,#65];
NSLog(#"arrayMergeSort %#",[self arrayMergeSort:activeArray]);
Output would be:
And also this bubble sort if you needed this:
- (NSArray *)arrayBubbleSort:(NSArray *)targetArray
{
NSMutableArray *resultArray = [targetArray mutableCopy];
for (int k = 0; k < resultArray.count; k++)
{
for (int l = 0; l < resultArray.count; l++)
{
if ([resultArray[k] intValue] < [resultArray[l] intValue])
{
[resultArray exchangeObjectAtIndex:k withObjectAtIndex:l];
}
}
}
return resultArray;
}
Hope i've helped you.. Cheers..
You've made a simple mistake. Merge sort works my splitting the array, sorting to the two halves, then merging the results.
Your mergeSort: method does the split, doesn't sort the two halves, and then calls merge: to merge the two (unfortunately unsorted) halves.
Before calling merge: you need to make recursive calls to mergeSort: to sort the two halves - this is the simple step you missed out.
I'm guessing this in a learning exercise, so no code, but you're almost there (fix it and it does work).
BTW Once you've fixed it you might want to think about why you don't need to create new arrays for the split part (but its far easier to create a new array for the merges).
HTH

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 :)

Anagram algorithm objective C

i have written the following code to check anagram want to know is this perfect & is there any better way to implement the same in objective C
-(BOOL) findAnagram :(NSString *) string1 :(NSString *) string2
{
int len = string1.length;
if (len != string2.length)
{
return false;
}
for (int i=0; i < len; i++)
{
int h = 0;
int q = 0;
for (int k = 0; k < len ; k ++)
{
if ([string1 characterAtIndex:i] == [string1 characterAtIndex:k])
{
h++;
}
if ([string1 characterAtIndex:i] == [string2 characterAtIndex:k])
{
q++;
}
}
if (h!=q)
{
return false;
}
}
return TRUE;
}
A better performing version than yours, which is a O(n ^ 2) algorithm, is a O(n) algorithm:
BOOL anagrams(NSString *a, NSString *b)
{
if (a.length != b.length)
return NO;
NSCountedSet *aSet = [[NSCountedSet alloc] init];
NSCountedSet *bSet = [[NSCountedSet alloc] init];
for (int i = 0; i < a.length; i++)
{
[aSet addObject:#([a characterAtIndex:i])];
[bSet addObject:#([b characterAtIndex:i])];
}
return [aSet isEqual:bSet];
}
You want to know if two strings contain exactly the same characters? Easiest way would probably be to sort both of them and compare the sorted version.
Another way would be to count the number of appearances of each letter (how many As, how many Bs, and so forth), then compare those counts.
(Note: The second way is just a variation of the first one, it's one efficient way to sort a string)
It looks fine to me. But the code style is slightly odd. I would write it like this:
- (BOOL)isStringAnagram:(NSString *)string1 ofString:(NSString *)string2 {
int len = string1.length;
if (len != string2.length) {
return NO;
}
for (int i=0; i < len; i++) {
int h = 0;
int q = 0;
for (int k = 0; k < len; k++) {
if ([string1 characterAtIndex:i] == [string1 characterAtIndex:k]) {
h++;
}
if ([string1 characterAtIndex:i] == [string2 characterAtIndex:k]) {
q++;
}
}
if (h != q) {
return NO;
}
}
return YES;
}
The main issue I have is with the method name. While it's possible to have parameters that have nothing before them in the name, it is not advisable. i.e. you had findAnagram:: as the name whereas I've used isStringAnagram:ofString:.
This is an implementation on #zmbq suggestion of sorting and comparing.
You should consider the requirements of deleting spaces and being case insensitive.
- (BOOL)isAnagram:(NSString *)leftString and:(NSString *)rightString {
NSString *trimmedLeft = [[leftString stringByReplacingOccurrencesOfString:#" " withString:#""] lowercaseString];
NSString *trimmedRight = [[rightString stringByReplacingOccurrencesOfString:#" " withString:#""] lowercaseString];
return [[self stringToCharArraySorted:trimmedLeft] isEqual:[self stringToCharArraySorted:trimmedRight]];
}
- (NSArray *)stringToCharArraySorted:(NSString *)string {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0 ; i < string.length ; i++) {
[array addObject:#([string characterAtIndex:i])];
}
return [[array sortedArrayUsingSelector:#selector(compare:)] copy];
}
called like this
BOOL isAnagram = [self isAnagram:#"A BC" and:#"cba"];
Check the following method which check Anagram strings.
-(BOOL)checkAnagramString:(NSString*)string1 WithAnotherString:(NSString*)string2{
NSCountedSet *countSet1=[[NSCountedSet alloc]init];
NSCountedSet *countSet2=[[NSCountedSet alloc]init];
if (string1.length!=string2.length) {
NSLog(#"NOT ANAGRAM String");
return NO;
}
for (int i=0; i<string1.length; i++) {
[countSet1 addObject:#([string1 characterAtIndex:i])];
[countSet2 addObject:#([string2 characterAtIndex:i])];
}
if ([countSet1 isEqual:countSet2]) {
NSLog(#"ANAGRAM String");
return YES;
} else {
NSLog(#"NOT ANAGRAM String");
return NO;
}
}
Another run of the mill algorithm:
- (BOOL) testForAnagramWithStrings:(NSString *)stringA andStringB: (NSString *)stringB{
stringA = [stringA lowercaseString];
stringB = [stringB lowercaseString];
int counter = 0;
for (int i=0; i< stringA.length; i++){
for (int j=0; j<stringB.length;j++){
if ([stringA characterAtIndex:i]==[stringB characterAtIndex:j]){
counter++;
}
}
}
if (counter!= stringA.length){
return false;
}
return true;
}

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.

iterate list and remove items from it in objective-c?

Objective-c have a built-in list iterantor via 'for(a in b) syntax that works fine with NSArray and other collections. But is it possible to remove items during such iteration without ugly tricks like
for( int i = 0, i < [array count]; i ++ )
{
if( condition )
{
[array removeItemAtIndex : i];
i --;
}
}
You can iterate array in the reverse order so you won't need to extra adjust index:
for( int i = [array count]-1; i >=0; --i)
{
if( condition )
{
[array removeItemAtIndex : i];
}
}
Or accumulate indexes to delete in indexset while enumerating and then delete all elements at once:
NSMutableIndexSet *indexes = [[NSMutableIndexSet alloc] init];
for( int i = 0, i < [array count]; i ++ )
{
if( condition )
{
[indexes addIndex : i];
}
}
[array removeObjectsAtIndexes:indexes];
[indexes release];
I would choose 2nd option because changing array while enumerating may be not a good style (although it won't give you errors in this particular case)
No, mutating a collection while enumerating it is explicitly forbidden. You could add the objects that should be removed to a second temporary mutable array and then, outside the loop, remove the objects in that array from the original with removeObjectsInArray:.
I wouldn't Call it a dirty trick to iterate backwards through the array.
for(int i=array.count-1;i>=0;i--)
if(condition)
[array removeItemAtIndex:i];
If I need it I copy my array and enumerate one while the other is mutating
NSArray *arr = firstArr;
for( int i = [array count]-1, i >=0; --i)
{
if( condition )
{
[firstArr removeItemAtIndex : i];
}
}
but I think Vladimir's example is the better one ;) (just to have all possibilities)