Objective-C Recipes On Sale NOW! Plus Free Chapter!

Matthew Campbell, July 25, 2012

Today is launch day for Objective-C Recipes. Woh, this has been 9 months in the making and I must say that writing a book as been one of the biggest challenges that I’ve faced so far with Mobile App Mastery. But, I think the result is worth it because it gives Objective-C the No-BS treatment that I think is the best approach to learning a technology subject.

How to Get Your Copy of Objective-C Recipes

The book is available in two flavors: the traditional paper book and an eBook.

Both versions will get you access to all the content and the source code. You can literally piece together your first app for the Mac or iOS just by using these recipes.

So, click right on this link to get your copy immediately!

Here is a sample chapter along with the source code so that you can see for yourself what the format of this book is all about.


3.5 Sorting an Array

Problem

You are using arrays to group your custom objects and you would like the objects to appear in lists sorted by the values of the object’s properties.

Solution

Create one NSSortDescriptor object for each property that you want to use to sort your array. Put all these NSSortDescriptor objects into an array, which you will use as a parameter in the next step. Use the NSArray sortedArrayUsingDescriptors: method and pass the array of NSSortDescriptor objects as a parameter to return an array sorted by the properties that you specified.

How It Works

This recipe uses Person objects. See Listing 3-5 for the class definition of a Person object. While you can use this recipe to sort objects like strings or numbers, you can really see the power behind NSSortDescriptor when you use it with custom objects.

Your custom class is named Person and has three properties: firstName, lastName, and age. Your Person class also has two methods: reportState and initWithFirstName:lastName:andAge, which is a custom constructor.

First, create an array of Person objects.

//Instantiate Person objects and add them all to an array:
Person *p1 = [[Person alloc] initWithFirstName:@"Rebecca"
                                      lastName:@"Smith"
                                        andAge:33];
Person *p2 = [[Person alloc] initWithFirstName:@"Albert"
                                      lastName:@"Case"
                                        andAge:24];
Person *p3 = [[Person alloc] initWithFirstName:@"Anton"
                                      lastName:@"Belfey"
                                        andAge:45];
Person *p4 = [[Person alloc] initWithFirstName:@"Tom"
                                      lastName:@"Gun"
                                        andAge:17];
Person *p5 = [[Person alloc] initWithFirstName:@"Cindy"
                                      lastName:@"Lou"
                                        andAge:6];
Person *p6 = [[Person alloc] initWithFirstName:@"Yanno"
                                      lastName:@"Dirst"
                                        andAge:76];

NSArray *listOfObjects = [NSArray arrayWithObjects:p1, p2, p3, p4, p5, p6,  nil];

If you print out each element in this array, the objects will appear in the order that you put them into the array. If you want to sort this array by each person’s age, last name, and first name, you can use NSSortDescriptor objects. You need one sort descriptor for each Person property that you’re using to sort.

//Create three sort descriptors and add to an array:
NSSortDescriptor *sd1 = [NSSortDescriptor sortDescriptorWithKey:@"age"
                                                      ascending:YES];

NSSortDescriptor *sd2 = [NSSortDescriptor sortDescriptorWithKey:@"lastName"
                                                      ascending:YES];

NSSortDescriptor *sd3 = [NSSortDescriptor sortDescriptorWithKey:@"firstName"
                                                      ascending:YES];

NSArray *sdArray1 = [NSArray arrayWithObjects:sd1, sd2, sd3, nil];

You must pass in the name of the property as a string and specify whether you want that property sorted ascending or descending. Finally, all the sort descriptors need to be in an array.

The position of each sort descriptor in the array determines the order in which the objects will be sorted. So, if you want the array sorted by age and then last name, make sure you add the sort descriptor corresponding to age before the sort descriptor corresponding to last name.

To get your sorted array, send the sortedArrayUsingDescriptors message to the array that you want to sort and pass the array of sort descriptors as a parameter.

NSArray *sortedArray1 = [listOfObjects sortedArrayUsingDescriptors:sdArray1];

To see the results, use the makeObjectsPerformSelector method to have each object in the sorted array report its state to the log.

[sortedArray1 makeObjectsPerformSelector:@selector(reportState)];

This will print out the details of each Person object to the log in the order that was specified by the sort descriptors (age, last name, first name). See Listings 3-5 through 3-7 for the code.

The Code

Listing 3-5. Person.h

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property(strong) NSString *firstName;
@property(strong) NSString *lastName;
@property(assign) int age;

-(id)initWithFirstName:(NSString *)fName lastName:(NSString *)lName andAge:(int)a;

-(void)reportState;

@end



Listing 3-6. Person.m

#import "Person.h"

@implementation Person

@synthesize firstName, lastName, age;

-(id)initWithFirstName:(NSString *)fName lastName:(NSString *)lName andAge:(int)a{
    self = [super init];
    if (self) {
        self.firstName = fName;
        self.lastName = lName;
        self.age = a;
    }
    return self;
}

-(void)reportState{
    NSLog(@"This person's name is %@ %@ who is %i years old", firstName, lastName, age);
}

@end



Listing 3-7. main.m

#import <Foundation/Foundation.h>
#import "Person.h"

int main (int argc, const char * argv[]){
	@autoreleasepool {

	    //Instantiate Person objects and add them all to an array:
	    Person *p1 = [[Person alloc] initWithFirstName:@"Rebecca"
	                                          lastName:@"Smith"
	                                            andAge:33];

	    Person *p2 = [[Person alloc] initWithFirstName:@"Albert"
	                                          lastName:@"Case"
	                                            andAge:24];

	    Person *p3 = [[Person alloc] initWithFirstName:@"Anton"
	                                          lastName:@"Belfey"
	                                            andAge:45];

	    Person *p4 = [[Person alloc] initWithFirstName:@"Tom"
	                                          lastName:@"Gun"
	                                            andAge:17];

	    Person *p5 = [[Person alloc] initWithFirstName:@"Cindy"
	                                          lastName:@"Lou"
	                                            andAge:6];

	    Person *p6 = [[Person alloc] initWithFirstName:@"Yanno"
	                                          lastName:@"Dirst"
	                                            andAge:76];

	    NSArray *listOfObjects = [NSArray arrayWithObjects:p1, p2, p3, p4, p5, p6, nil];

	    NSLog(@"PRINT OUT ARRAY UNSORTED");

	    [listOfObjects makeObjectsPerformSelector:@selector(reportState)];

	    //Create three sort descriptors and add to an array:
	    NSSortDescriptor *sd1 = [NSSortDescriptor sortDescriptorWithKey:@"age"
	                                                          ascending:YES];

	    NSSortDescriptor *sd2 = [NSSortDescriptor sortDescriptorWithKey:@"lastName"
	                                                          ascending:YES];


	    NSSortDescriptor *sd3 = [NSSortDescriptor sortDescriptorWithKey:@"firstName"
	                                                          ascending:YES];

	    NSArray *sdArray1 = [NSArray arrayWithObjects:sd1, sd2, sd3, nil];

	    NSLog(@"PRINT OUT SORTED ARRAY (AGE,LASTNAME,FIRSTNAME)");

	    NSArray *sortedArray1 = [listOfObjects sortedArrayUsingDescriptors:sdArray1];

	    [sortedArray1 makeObjectsPerformSelector:@selector(reportState)];

	    NSArray *sdArray2 = [NSArray arrayWithObjects:sd2, sd1, sd3, nil];

	    NSArray *sortedArray2 = [listOfObjects sortedArrayUsingDescriptors:sdArray2];

	    NSLog(@"PRINT OUT SORTED ARRAY (LASTNAME,FIRSTNAME,AGE)");

	    [sortedArray2 makeObjectsPerformSelector:@selector(reportState)];

	}
    return 0;
}

Usage

To use this code, you need to create a file for the Person class. This is an Objective-C class, and you may use the Xcode file templates to start it. The Person class must be imported into code located in main.m (for Mac command line apps). Build and run the project and then inspect the console log to see the results of the sorted arrays.

PRINT OUT ARRAY UNSORTED
This person's name is Rebecca Smith who is 33 years old
This person's name is Albert Case who is 24 years old
This person's name is Anton Belfey who is 45 years old
This person's name is Tom Gun who is 17 years old
This person's name is Cindy Lou who is 6 years old
This person's name is Yanno Dirst who is 76 years old
PRINT OUT SORTED ARRAY (AGE,LASTNAME,FIRSTNAME)
This person's name is Cindy Lou who is 6 years old
This person's name is Tom Gun who is 17 years old
This person's name is Albert Case who is 24 years old
This person's name is Rebecca Smith who is 33 years old
This person's name is Anton Belfey who is 45 years old
This person's name is Yanno Dirst who is 76 years old
PRINT OUT SORTED ARRAY (LASTNAME,FIRSTNAME,AGE)
This person's name is Anton Belfey who is 45 years old
This person's name is Albert Case who is 24 years old
This person's name is Yanno Dirst who is 76 years old
This person's name is Tom Gun who is 17 years old
This person's name is Cindy Lou who is 6 years old
This person's name is Rebecca Smith who is 33 years old

PS: you can get Objective-C Recipes starting today in two flavors: the traditional paper book and an eBook.

So, click right on this link to get your copy immediately!