Merge sort in Objective-C - ios

I'm trying to figure out what's wrong with this merge sort implementation. I've narrowed it down to when I concatenate what remains of the left and right arrays. In the third loop of the recursion, something goes wrong.
-(NSArray *)mergeSort:(NSArray *)unsortedArray
{
//unsortedArray is 4,2,6,5,3,9
if ([unsortedArray count] < 2)
{
return unsortedArray;
}
int middle = ([unsortedArray count]/2);
NSRange left = NSMakeRange(0, middle);
NSRange right = NSMakeRange(middle, ([unsortedArray count] - middle));
NSArray *rightArr = [unsortedArray subarrayWithRange:right];
NSArray *leftArr = [unsortedArray subarrayWithRange:left];
return [self merge:[self mergeSort:leftArr] andRight:[self mergeSort:rightArr]];
}
-(NSArray *)merge:(NSArray *)leftArr andRight:(NSArray *)rightArr
{
NSMutableArray *result = [[NSMutableArray alloc]init];
int right = 0;
int left = 0;
while (left < [leftArr count] && right < [rightArr count])
{
if ([leftArr objectAtIndex:left] < [rightArr objectAtIndex:right])
{
[result addObject:[leftArr objectAtIndex:left++]];
}
else
{
[result addObject:[rightArr objectAtIndex:right++]];
}
}
NSRange leftRange = NSMakeRange(left, ([leftArr count] - left));
NSRange rightRange = NSMakeRange(right, ([rightArr count] - right));
NSArray *newRight = [rightArr subarrayWithRange:rightRange];
NSArray *newLeft = [leftArr subarrayWithRange:leftRange];
newLeft = [result arrayByAddingObjectsFromArray:newLeft];
return [newLeft arrayByAddingObjectsFromArray:newRight];
}
BTW, this is not homework. I'm a self-taught programmer trying to learn a little CS. Thanks everyone.

You can't use the < (less than) operator to compare two objects. Use the compare: method:
Replace:
if ([leftArr objectAtIndex:left] < [rightArr objectAtIndex:right])
with:
NSComparsionResult result = [leftArr[left] compare:rightArr[right]];
if (result == NSOrderedAscending) // equivalent to <
As "rob" points out, it would be even better to use:
if (result != NSOrderedDescending) // equivalent to <=
BTW - using < with two objects is causing problems because you are comparing the pointer addresses of the two objects. So you end up sorting the objects based on their location in memory and not by their value.
And of course using the compare: method assumes the objects in the array actually implement the compare: method. This is true for things like NSString, NSNumber, and NSDate. If these are custom objects you need to implement an equivalent method.

The issue certainly is the comparison:
Replacing:
if ([leftArr objectAtIndex:left] < [rightArr objectAtIndex:right])
for:
if ([[leftArr objectAtIndex:left] intValue] < [[rightArr objectAtIndex:right] intValue])
will also work.

Related

Insert an object into NSMutable array and shift element one position ahead iOS

I want to insert an object (say 1.5) into NSMutableArray (say array with content: 1,2,3,4) between 1 and 2. The resultant array would be one element greater (say 1,1.5,2,3,4).
How can this be acheived in iOS using NSMutableArray?
Assuming you know the index to insert at, just use NSMutableArray's insertObject:atIndex: (reference). In your case, you want:
[yourMutableArray insertObject:#(1.5) atIndex:1];
If you don't know the index, you can do something like this (copy-pasted because nobody likes broken links):
#implementation NSMutableArray (SelfSorting)
- (void)insertNumberAtSortedLocation:(NSNumber *)aNumber
{
NSUInteger count = [self count];
// if there are no contents yet, simply add it
if (!count)
{
[self addObject:aNumber];
return;
}
NSRange searchRange;
searchRange.location = 0;
searchRange.length = count;
// bubble sort finding of insert point
do
{
NSInteger index = searchRange.location + searchRange.length/2;
NSNumber *testNumber = [self objectAtIndex:index];
switch ([aNumber compare:testNumber])
{
case NSOrderedAscending:
{
//searchRange.length = searchRange.length / 2;
searchRange.length = index - searchRange.location;
break;
}
case NSOrderedDescending:
{
int oldLocation = searchRange.location;
searchRange.location = index+1;
searchRange.length = searchRange.length - (searchRange.location - oldLocation);
break;
}
case NSOrderedSame:
{
searchRange.length = 0;
searchRange.location = index;
break;
}
}
} while (searchRange.length>0);
// insert at found point
[self insertObject:aNumber atIndex:searchRange.location];
}
And then, call:
[yourMutableArray insertNumberAtSortedLocation:#(1.5)];
Two lines of code
Just append the item and then sort or sort at usage time, sorting is actually very cheap, almost O(n) for non pathological cases.
NSMutableArray *a = [#[#1, #2 ,#3 ,#4] mutableCopy];
// Two lines of code:
[a addObject:#(1.5)];
[a sortUsingSelector:#selector(compare:)];
NSLog(#"a: %#", a);
NSLog oputput:
a: (
1,
"1.5",
2,
3,
4
)
The above should be O(log n)
Or if you don't want the entire array sorted, just insert after the first entry that is less than it:
NSUInteger count = [a count];
int index = 0;
while (index < count && [a[index] compare:aNumber] == NSOrderedAscending) {
index += 1;
}
[a insertObject:aNumber atIndex:index];
The above is O(n) as opposed to a binary search which is O(log n) but for most arrays there is not a meaningful time difference.
For the example in your question, you would write [array insertObject:#(1.5) atIndex:1]
NSMutableArray * foo = [[NSMutableArray alloc] init];
[foo insertObject:<#(id)#> atIndex:<#(NSUInteger)#>]

In Objective-C Check an array of Integer Values and see if at least ONE is > 0

I asked a similar question to test an Array of Bool values here but now I have changed it so the values are now Integer values and I want to see if any of them are positive.
Example, I add 10 new objects in a loop like below, then at some point some may change and then I need to do a test. It may be that I do not use NSNumber, as long as I cna get an int out of it.
// I create an array like
NSMutableArray *MyArray = [[NSMutableArray alloc]init];
for (int count = 0; count < 10; count++)
{
[MyArray addObject:[NSNumber numberWithInt:0]];
}
// At some point I change a value or two
[MyArray replaceObjectAtIndex:3 withObject:[NSNumber numberWithInt:5]];
....
....
....
....
[MyArray replaceObjectAtIndex:3 withObject:[NSNumber numberWithInt:7]];
if([MyArray containsPositiveObject])
{
// Get the integer
int MyValue = [MyArray[4]integerValue];
}
EDIT
I have tried this and it works but wondered if there was a faster method.
if([self containsPositiveValue:selectedItems])
{
// Do my stuff
}
And then
-(bool)containsPositiveValue:(NSArray *)numberArray
{
bool result = NO;
for (NSNumber *obj in numberArray)
{
if([obj integerValue])
{
result = YES;
break;
}
}
return result;
}
Try this.
NSArray *array = #[ #(-1) , #(-3) , #(0) , #(-4) , #(2) , #(-1) ]; // zero is not positvie or negitive
NSArray *newArray = [array filteredArrayUsingPredicate: [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return ( [evaluatedObject isKindOfClass:[NSNumber class]] && ([evaluatedObject integerValue] > 0) );
return NO;
}]];
if (newArray.count)
{
NSLog(#"There are positive values");
}
else
{
NSLog(#"There are no positive values") ;
}
Surely the simplest way would be to go:
NSArray * array = #[#(-1), #(-5), #(-5)];
for (NSNumber * number in array) {
if (number.intValue > 0) {
NSLog(#"Positive Values");
// Add custom code here
break;
}
}
This will then check if at least one item in the array is greater than zero and custom code can then be implemented.
I suppose it depends if you want to perform something if there are no positive values and something different if there is?
You could always add something like this:
NSArray * array = #[#(-1), #(-5), #(-5)];
NSInteger largestValue = 0;
for (NSNumber * number in array) {
if (number.intValue > largestValue) {
largestValue = number.intValue;
}
if (largestValue > 0) {
break;
}
}
if (largestValue > 0) {
NSLog(#"Positive Value");
}
else {
NSLog(#"All Negative Values");
}
Anyway it gets more complicated the more complicated you want it to work but it should be incredibly simple

Is there a classical or nice way to apply different instructions to the first or last element of a loop?

Consider a loop. I am also interested by fast enumerations. So, it could be
either a for(id obj in objects)
or a [objects enumerate... ]
I want to know if there is a classical or nice way (in Objective-C) to make a distinction between the first element of the loop and the others. Of course, if there is only a nice way to distinguish the last elements from the others, I am also interested.
Is there a classical or nice way to apply different instructions to the first or last element of a loop?
It's easiest with a classic for loop but since you want fast enumeration you need your own counter:
NSUInteger index = 0;
NSUInteger count = objects.count;
for (id obj in objects) {
if (index == 0) {
// first
} else if (index == count - 1) {
// last
} else {
// all others
}
index++;
}
If objects is an NSArray, you can do:
[objects enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (idx == 0) {
// first
} else if (idx == objects.count - 1) {
// last
} else {
// all others
}
}];
Not sure what nice means, so here is my idea:
[array.firstObject doSomethingWithFirst]; // iOS 7 SDK or custom category
NSRange range = NSMakeRange(1, array.count - 2); //TODO: Check for short arrays.
for (id object in [array subarrayWithRange:range]) {
[object doSomething];
}
[array.lastObject doSomethingWithLast];
It does what you want, you don't bother with counting indexes, uses fast enumeration, it's readable (first and last). The only weird thing on this is the NSRange part.
You may consider this nice.
If you use the common statement of FOR you can do that:
for (int i = 0; i < [myArray count]; i++)
{
if(i == 0)
NSLog("First");
if(i == [myArray count] - 1)
NSLog("Last");
//rest of your code
}
Even i would recommend to use these NSArray method :-
- (void)enumerateObjectsUsingBlock:(void (^)
(id obj, NSUInteger idx, BOOL *stop))block
Keep in mind that enumerateObjectsUsingBlock does not guarantee about looping in ordered manner. A classic example for what you might be looking for:
const NSInteger sizeOfArray = 10;
NSInteger numbers[sizeOfArray] = {10,20,30,40,50,60,70,80,90,100};
NSInteger i = 0;
do {
switch (i) {
case 0:
NSLog(#"First");
break;
case sizeOfArray:
NSLog(#"Last");
break;
default:
NSLog(#"All others");
break;
}
NSLog(#"Number = %i", numbers[i++]);
}while (i < sizeOfArray);

is there a way to check the next object inside NSArray in a for..in loop?

I have this NSArray:
NSArray* temp=[[NSArray alloc]
initWithObjects:#"one",#"five",#"two",nil];
for(NSString* obj in temp){
NSLog(#"current item:%# Next item is:%#",obj, ***blank***);
}
What needs to replace blank? Do I need to know the upcoming object?
This only works if your objects are unique (i. e. there are no identical objects in the array):
id nxt = nil;
int nxtIdx = [temp indexOfObject:idx] + 1;
if (nxtIdx < temp.count) {
nxt = [temp objectAtIndex:nxtIdx];
}
NSLog(#"current item:%# Next item is:%#", obj, nxt);
But in my opinion, this is a hack. Why not use a normal for loop with the index of the object:
for (int i = 0; i < temp.count; i++) {
id obj = [temp objectAtIndex:i];
id next = (i + 1 < temp.count) ? [temp objectAtIndex:i + 1] : nil;
}
Or (recommended) enumerate it using a block
[temp enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id next = nil;
if (idx + 1 < temp.count) {
next = [temp objectAtIndex:idx + 1];
}
}];
Try using an NSEnumerator.
Code:
NSEnumerator *enumerator = [temp objectEnumerator];
id obj;
while (obj = [enumerator nextObject]) {
if ([enumerator nextObject] == nil) {
NSLog(#"This is the last object: %#", obj);
break;
}
NSLog(#"current item:%# Next item is:%#", obj, [enumerator nextObject]);
}
You can't do this with fast enumeration without a few lines of extra code (deriving the index, adding to it, checking its still in bounds).
You could enumerate using a block and add one to the idx parameter, but a better design (where you wouldn't risk or have to check for out of bounds exceptions) would be to remember the previous object instead, which will be nil on your first iteration.
You can get the index of your current object and look at the next with something similar to:
NSArray* temp=[[NSArray alloc] initWithObjects:#"one",#"five",#"two",nil];
for(NSString* obj in temp){
if([temp count] < [temp indexOfObject:obj]+1)
{
NSLog(#"current item:%# Next item is:%#",obj, [temp objectAtIndex:[temp indexOfObject:obj] + 1]);
}
}
To me it can sometimes be easier to do a traditional for loop in this case to have access to your index variable.
Using fast enumeration, this is the only way:
NSInteger index = [temp indexOfObject:obj];
if (index != NSNotFound && index < temp.count)
NSObject nextObject = [temp objectAtIndex:(index + 1)];
Note the if, you'll want to make sure you get a valid index, and that adding one to it doesn't put you out of bounds.

IOS word game. Validate word performance [closed]

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
I'm building a scrabble game, and having some problem with the word dictionary. It contains ~700,000 words, and about 18 MB big.
Right now, I'm loading the whole dict into an array, which takes 12 seconds on an iPhone 4.
wordList = [NSMutableArray arrayWithContentsOfFile: [[self applicationDocumentsDirectory] stringByAppendingString:#"/wordlist.plist"]];
I have two questions:
Is there a better way to load the wordlist faster and/or reduce memory?
It takes about 12 seconds to get all possible words from a set of letters. Is it possible to make it quicker? Here's the code:
-(NSMutableArray *)getValidWords:(NSString *)letters{
NSMutableArray *list = [[NSMutableArray alloc] init];
for (int i = 0, c = [wordList count]; i < c; i++){
if ([self isWordValid: [wordList objectAtIndex: i] forLetters:letters]){
[list addObject:[wordList objectAtIndex: i]];
}
}
return list;
}
- (BOOL)isWordValid:(NSString *)word forLetters:(NSString *)ltrs{
int i, z;
NSRange range;
BOOL found;
static NSMutableString *letters = nil;
if ([word length] < 2) return NO;
if(letters == nil) {
letters = [[NSMutableString alloc] initWithString:ltrs];
}
else {
[letters setString: ltrs];
}
found = NO;
range.length = 1;
for(i = 0; i < [word length]; i++){
for(z = 0; z < [letters length]; z++){
if([word characterAtIndex:i] == [letters characterAtIndex:z]){
range.location = z;
[letters deleteCharactersInRange: range];
found = YES;
break;
}
}
if (found == NO){
return NO;
}
found = NO;
}
return YES;
}
You need to change few things to speed up.
Use fast enumeration in place of old C-style loop.
Avoid a lot of method calls.
Use NSPredicate and/or Regex if possible.
As whenever you write [letters length] a method is called, instead of finding it millions of time (this is inside 3rd level of nested loop), store it in a variable and use it.
Fast enumeration : Instead of for(int i=0; i<[someArrays count];i++) use for(id object in someArrays).
Use this
[NSThread detachNewThreadSelector:#selector(fetchWords:) toTarget:self withObject:data];
Do not do it in main thread
use this code modify it if u need to search words
NSMutableArray *subpredicates = [NSMutableArray array];
for(NSString *term in arryOfWordsToBeSearched) {
NSPredicate *p = [NSPredicate predicateWithFormat:#"self contains[cd] %#",term];
[subpredicates addObject:p];
}
NSPredicate *filter = [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
result = (NSMutableArray*)[arryOfDummyData filteredArrayUsingPredicate: filter];
//result is a array
Just for starters, create a NSCharacterSet from your letters and call this function before calling the lengthy function. This is a faster check to reduce the possibilities and it should improve your computation time.
NSCharacterSet* lettersSet = [NSCharacterSet characterSetWithCharactersInString:letters];
- (BOOL)isWordValid:(NSString*)word forLettersSet:(NSCharacterSet*)lettersSet {
if ([word length] < 2) return NO;
NSCharacterSet* wordLetters = [NSCharacterSet characterSetWithCharactersInString:word];
return [lettersSet isSupersetOfSet:wordLetters];
}
Ideally, your word database should have precomputed the letter count for each word (e.g. every = {e=2, r=1, v=1, y=1} and your should work only with these structures. Note that the order of letters is not significant - using this fact can greatly improve the performance of your algorithm.
You can also try to create a Core Data database - every word will be one record with a number field for every letter. Then you can create a request which will return you the available words VERY fast.
(Of course, the database will probably take bigger amount of space).
EDIT: Now I have found out NSCountedSet class, so let's try to use it:
-(NSCountedSet*)lettersSetFromString:(NSString*)string {
NSCountedSet* letters = [NSCountedSet set];
[string enumerateSubstringsInRange:NSMakeRange(0, self.length)
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[letters addObject:substring];
}];
}
-(NSMutableArray *)validWordsForLetters:(NSString*)letters {
NSCountedSet* lettersSet = [self lettersSetFromString:letters];
NSMutableArray* results = [NSMutableArray array];
for (NSString* word in wordList) {
if ([word length] < 2) {
continue;
}
NSCountedSet* wordLettersSet = [self lettersSetFromString:word];
if ([wordLettersSet isSubsetOfSet:lettersSet]) {
[results addObject:word];
}
}
return results;
}
Generating the counted sets for every word beforehand will help the perfomance a lot. Using a Core Data database will still be faster and better for the OS memory.

Resources