Occasionally you will come across the need to re-order the contents of an NSArray. One reason could be that you want to put the contents of an NSArray in to a table view, but in a different order to what is currently in the NSArray. This tutorial shows one way (of many) of how this can be done.
The method we will look at in this tutorial is the sortedArrayUsingComparator: which is an instance method from the NSArray class. On first looks, this can look a little complicated, but if you take time to read through and see what is happening, it begins to make perfect sense. It also makes for quite clean code too.
arrayOfObjects = [arrayOfObjects sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
}];
Looking above we have line 1 which has an NSArray (arrayOfObjects) that we will be putting the results in. On the right side of the = we also use that same arrayOfObjects and then pass it the sortedArrayUsingComparator: method. We then have a block passed in as an argument. This is boiler plate code and is provided by code completion when you start to type out sortedArr…..
Although the internal workings might be obscured from us, what we do know is what happens when we use this block.
What we have is two objects of type id. obj1 is the first and is followed by obj2.
When this block is run, it passes each object from the NSArray to obj1 and obj2 which in turn, lets us take the contents of those objects and run them through our own custom comparison at which point, we return a value. The process then repeats until all objects have been used.
Adding to the code above, we can then do the following:
Lets say that we have an NSArray of CLBeacon objects which has an NSNumber for minor values (if you are interested in iBeacons, take a look at our tutorial here). If we want to order the array of beacons by minor value, we can use each object and run the minor values through the comparison.
- (void)sortArrayItemsByMinorValue:(NSArray *)arrayOfObjects {
arrayOfObjects = [arrayOfObjects sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
CLBeacon *beacon1 = obj1;
CLBeacon *beacon2 = obj2;
if ( beacon1.minor < beacon2.minor ) {
return (NSComparisonResult)NSOrderedAscending;
} else if ( beacon1.minor > beacon2.minor ) {
return (NSComparisonResult)NSOrderedDescending;
}
return (NSComparisonResult)NSOrderedSame;
}];
}
Here is the code we end up with.
What we are doing here is running a custom method called sortArrayItemsByMinorValue:. We pass this an NSArray.
We already discussed line 2 above, so I won’t repeat that here.
Lines 3 and 4 we have a few options here, but for the sake of showing that obj1 and obj2 are of type CLBeacon, I manually created these and called them beacon1 and beacon2 and stored obj1 and obj2 in them. If you were allowing others to subclass you, you would want to use more robust code here so that an NSString doesn’t get passed in to a CLBeacon object.
Now that we have beacon1 and beacon2, we can now run some comparisons on these. To do this we move to 5 where we compare beacon1.minor to beacon2.minor. If the value of beacon1 is smaller than the value of beacon2 we return an (NSComparisonResult)NTOrderedAscending. If its the other way around, we pass NSOrderedDescending. Finally, if it is neither we pass NSOrderedSame.
This process is cycled through however many times vs objects in the array that you are comparing and when done, the new array has been stored in the NSArray called arrayOfObjects on line 2. You might choose to make this method return an NSString. If so, just add return arrayOfObjects; before the method closes out.
If you want to switch the order so that they go in reverse, simply change the comparators around in the if statement to a greater than or less than so that the correct beacon is ordered as needed.
Conclusion
Although you will find a manual way of doing this, probably by comparing values 1 at a time and then adding them to a mutable array in the desired order, I find it far simpler to get familiar with the sorting methods that the NSArray class provides. Some take a little bit of work to understand, but once you get them you’ll find that you can accomplish the task in an efficient manner with relatively few lines of code.
Luke says
Nice explanation, thanks. Typo though – in your code example there, you define the method as -(void), it should return -(NSComparisonResult*) though, right? Thanks though, good explanation! Luke
Jake says
Luke,
The return is returning for the comparison block.
Bhavya says
Thanks. It helped.