Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS

How to sort an NSMutableArray using NSSortDescriptor

Hello all, I am trying to reorder the objects that are in the homeworkItemsArray (self.delegate.homeworkItemsArray) by using the property baseRankNumber to make the homeworkItems be in descending order of baseRankNumber. Can anyone show me what is wrong about this code sample?

NSSortDescriptor* sortHomeworkItems = [[NSSortDescriptor alloc]initWithKey:@"baseRankNumber" ascending:NO];

NSMutableArray *sortedArray = (NSMutableArray *)[self.delegate.homeworkItemsArray sortedArrayUsingDescriptors: [NSArray arrayWithObject:sortHomeworkItems]];

[self.delegate.homeworkItemsArray removeAllObjects];

[self.delegate.homeworkItemsArray addObjectsFromArray:sortedArray];

10 Answers

Thomas Nilsen
Thomas Nilsen
14,957 Points

https://www.dropbox.com/s/yjzmmh6gd2sl7jf/SortingExample.zip

Have a look at that. Hope that helps and you finally figure it out :) One thing I forgot to point out is that when using NSSortDiscriptor, the array you're sorting has to be MUTABLE (NSMutableArray).

Thomas Nilsen
Thomas Nilsen
14,957 Points

Here is a short example: Say you have an array called 'tempArray' filled with objects. Each object contains a name, location and distance property. We want to sort these objects based on distance.

NSSortDescriptor *distanceSortDiscriptor = [NSSortDescriptor sortDescriptorWithKey:@"distance"
                                                                                     ascending:YES
                                                                                      selector:@selector(localizedStandardCompare:)];

[tempArray sortUsingDescriptors:@[distanceSortDiscriptor]];
NSArray *sortedArray = [NSArray arrayWithArray:tempArray];
Thomas Nilsen
Thomas Nilsen
14,957 Points
NSArray* sortedArray = [unsortedArray sortedArrayUsingDescriptors:sortDescriptors];

It weird you don't get an error when writing that, because you are essentially assigning a void function to an array. Try to write it like this instead:

NSSortDescriptor* descriptorForSorting = [[NSSortDescriptor alloc]initWithKey:@"baseRankNumber" ascending:NO];
NSArray* unsortedArray = [[NSArray alloc]initWithArray:self.delegate.homeworkItemsArray];
NSArray* sortDescriptors = @[descriptorForSorting];
[unsortedArray sortedArrayUsingDescriptors:sortDescriptors];
NSArray* sortedArray = [NSArray arrayWithArray: unsortedArray];

okay, so I tried again using the following method and it still didn't work, what should i do?

NSSortDescriptor* descriptorForSorting = [[NSSortDescriptor alloc]initWithKey:@"baseRankNumber" ascending:NO];

NSArray* unsortedArray = [[NSArray alloc]initWithArray:self.delegate.homeworkItemsArray];

NSArray* sortDescriptors = @[descriptorForSorting];

NSArray* sortedArray = [unsortedArray sortedArrayUsingDescriptors:sortDescriptors];
Thomas Nilsen
Thomas Nilsen
14,957 Points

Have you tried NSLogging out the 'unsortedArray' of yours just to check wether or not it's nil? Also, 'self.delegate.homeworkItemsArray' did not make much sense to me?

Yes I checked and the objects are in the unsortedArray and the self.delegete.homeworkItemsArray' is a a call to a global variable in the appDelegate and the delegate keyword is the name of the property that refers to the appDelegate which allows you to use the homeworkItemsArray. So how do I proceed from here since the unsortedArray has the items?

I tried this and this time it doesn't crash the app but its also doesn't reorder the array. Do you know how else i could reorder the objects besides using this method (like sorting using a for loop)?

Thomas Nilsen
Thomas Nilsen
14,957 Points

I'll make a full example for you a little later. I think your problem is somewhere else though, because I've tested it out in the app I'm working on, and it's no problem there.

This all makes sense except for your selector argument, since your comparing strings your using localizedStandardCompare but since I'm comparing NSInteger, what selector do I use?

Thomas Nilsen
Thomas Nilsen
14,957 Points

If you want to use NSIntegers instead, you have to make your own method. Check out NSComparisonResult (that's what the localizedStandardCompare method is based on)

Okay never mind the above problem, I have figured out how to do everything manually except to the part where I insert the homeworkItem object, and that too is only a syntactical error which is what I need help on now.

if ([testHomeworkItems count]>0) { homeworkObject* itemWhichWillBeComparedTo = [testHomeworkItems objectAtIndex:0];

        NSInteger indexVal = 0;
        Boolean stop = false;

        while ((stop == false) && (indexVal<[testHomeworkItems count])) //gives you the first item which has the old destination class
        {
            homeworkObject* homeworkItem = [testHomeworkItems objectAtIndex:indexVal];

            if ([homeworkItem.classType isEqualToString:initialDestinationClass])
            {
                itemWhichWillBeComparedTo = homeworkItem;
                stop = true;
            }

            indexVal++;
        }

        for (NSInteger i = 0; i<[testHomeworkItems count]; i++)
        {
            if (i!= [testHomeworkItems indexOfObject:itemWhichWillBeComparedTo])
            {
                homeworkObject* homeworkItemTryingToBeBiggest = [testHomeworkItems objectAtIndex:i];

                if((homeworkItemTryingToBeBiggest.baseRankNumber>itemWhichWillBeComparedTo.baseRankNumber) && (i>[testHomeworkItems indexOfObject:itemWhichWillBeComparedTo]))
                {
                    [testHomeworkItems insertObject:homeworkItemTryingToBeBiggest atIndex:[testHomeworkItems indexOfObject:itemWhichWillBeComparedTo]];

                }
            }
        }
    }
}

What this basically does is that it finds the homeworkItem that will be the basis of comparison (itemWhichWillBeComparedTo) and then it finds homeworkItems which are bigger then it (homeworkItemTryingToBeBiggest) and then if they are bigger it inserts them at the index of itemWhichWillBeComparedTo. However when I try to insert the homeworkItemTryingToBeTheBiggest at the index of itemWhichWillBeComparedTo, the app crashes and I don't understand why. What should I change?