363

I need to reverse my NSArray.

As an example:

[1,2,3,4,5] must become: [5,4,3,2,1]

What is the best way to achieve this?

Andy Jacobs
  • 15,187
  • 13
  • 60
  • 91
  • 1
    It's also worth looking at this: [http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Collections/Articles/sortingFilteringArrays.html](http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Collections/Articles/sortingFilteringArrays.html) which tells you how to sort an array in reverse order (which is commonly what you are doing, for instance in using an array derived from NSDictionary#allKeys, and you want reverse date/alpha order to serve as grouping for UITable on iPhone, etc). –  Nov 03 '09 at 08:01

18 Answers18

1312

There is a much easier solution, if you take advantage of the built-in reverseObjectEnumerator method on NSArray, and the allObjects method of NSEnumerator:

NSArray* reversedArray = [[startArray reverseObjectEnumerator] allObjects];

allObjects is documented as returning an array with the objects that have not yet been traversed with nextObject, in order:

This array contains all the remaining objects of the enumerator in enumerated order.

Pang
  • 9,564
  • 146
  • 81
  • 122
danielpunkass
  • 17,527
  • 4
  • 24
  • 38
  • 6
    There's an answer further down here by Matt Williamson that ought to be a comment: *Don't use danielpunkass's solution. I used it thinking it was a great shortcut, but now I've just spent 3 hours trying to figure out why my A\* algorithm was broken. It's because it returns the wrong set!* – Georg Schölly Mar 12 '10 at 06:23
  • 1
    What do mean by 'wrong set'? A array that is not in reverse order? – Simo Salminen Apr 19 '10 at 05:38
  • 33
    I'm no longer able to reproduce that bug. It could have been my error. This is a very elegant solution. – Matt Williamson Feb 17 '11 at 22:05
  • 3
    The order is now guaranteed in the documentation. – jscs Mar 03 '16 at 06:10
  • i am sure this is the good answer but it will fail for the Mutable Objects. Because NSEnumerator provide readonly object type @property (readonly, copy) NSArray *allObjects; – Anurag Soni Dec 29 '16 at 09:22
  • Hi @AnuragSoni - I'm not sure why that would cause it to fail. None of the methods involved in my prescription involve mutating an object. Do you mean NSEnumerator can't be used at all, for some reason, on a mutable array's allObjects ? – danielpunkass Dec 31 '16 at 03:35
  • Hi @danielpunkass if you are doing modification with the objects in Array received in reversedArray. – Anurag Soni Feb 03 '17 at 08:53
308

For obtaining a reversed copy of an array, look at danielpunkass' solution using reverseObjectEnumerator.

For reversing a mutable array, you can add the following category to your code:

@implementation NSMutableArray (Reverse)

- (void)reverse {
    if ([self count] <= 1)
        return;
    NSUInteger i = 0;
    NSUInteger j = [self count] - 1;
    while (i < j) {
        [self exchangeObjectAtIndex:i
                  withObjectAtIndex:j];

        i++;
        j--;
    }
}

@end
Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
  • 16
    One of the bad things about Fast Enumeration is that new guys like me don't learn about cool things like reverseObjectEnumerator. Pretty neat way to do it. – Becca Royal-Gordon Feb 25 '09 at 15:34
  • They are better than C++ iterators, but I definitely won't miss enumerators. – Georg Schölly Feb 25 '09 at 15:39
  • Wht are they better then C++ iterators? – Mykola Golubyev Apr 21 '09 at 21:39
  • 4
    Because C++-iterators have an even worse syntax, they are ugly. – Georg Schölly Apr 22 '09 at 10:39
  • 4
    Shouldn't you copy the array before returning it? – CIFilter Feb 08 '12 at 03:31
  • 1
    this first method is really returning an NSMutableArray though, no? ;) – Christine Feb 24 '12 at 15:26
  • 1
    @Christine: Yes, but this doesn't matter because it looks like an `NSArray`. If the immutability has to be enforced you can do `return [array copy]`. – Georg Schölly Feb 24 '12 at 16:34
  • 1
    There is a bug in the mutable array if [self count] is 0 since you are using unsigned integers. –  Apr 09 '12 at 16:47
  • @Mark: Thanks! Do you see a better fix than adding an `if` statement? – Georg Schölly Apr 10 '12 at 07:44
  • 12
    @Georg: I disagree with you on this one. If I see a method that returns an immutable object, I expect it to actually be returning an immutable object. Having it appear to return an immutable object but actual return a mutable object is a dangerous practice to get into. – Christine Jul 02 '12 at 22:46
  • Calling a method declared in a category on NSArray on an instance of NSMutableArray, like in this case, crashes your app. – Hans Sjunnesson Jul 03 '12 at 12:53
  • 1
    @HansSjunnesson Are you sure there's a problem with my code? I've just tested it and it worked fine. – Georg Schölly Jul 04 '12 at 21:33
  • The answer from @danielpunkass is much better for immutable arrays, but this has a reasonable implementation for mutable arrays. – Quinn Taylor Nov 01 '12 at 23:18
  • Does `- (void)reverse` actually work? Surely by the time you've reversed the first half of the array, the second half will revert back to the original order? i.e.: for an array with 5 items, 1->5, 2->4, 3->3, 4->2, 5->1 and everything is back where it started? – Zack Brown Sep 09 '13 at 12:11
  • @CaptainRedmuff: You overlooked `j--` – Georg Schölly Sep 09 '13 at 12:58
  • Good call. I had missed the `j--`. Good stuff :] – Zack Brown Sep 09 '13 at 14:38
  • As pointed out below `[[array reverseObjectEnumerator] allObjects]` is a much more concise way to use the reverse enumerator. – Sam Soffes Oct 16 '13 at 21:25
  • 1
    @Christine: You can always just add `return [array copy]`. Personally, I do not think this is necessary in most cases. – Georg Schölly Dec 06 '13 at 16:48
  • NSArray* reversed = [[myArray reverseObjectEnumerator] allObjects]; use this :) – Gökhan Çokkeçeci Feb 09 '15 at 20:49
  • 3
    Suggesting reverseObjectEnumerator allObjects is not helpful since it does not reverse *the* mutable array, Even adding mutableCopy would not help since still the original array is not mutated. Apple documents that immutability should *not* be tested for at run time, but be assumed based on the returned type, so returning an NSMutableArray in this case is perfectly correct code. – Peter N Lewis Feb 26 '15 at 00:06
  • There is a straightforward solution to this refer to @danielpunkass's answer – Shubham Naik May 24 '17 at 04:47
  • Well done now mark that answer as accepted to help ppl looking for answer in future, cuz no one is going to read all the comments till here right :D – Shubham Naik May 24 '17 at 09:11
  • Small test shows that this method is 10 to 30 times faster than `mutableArray = [[[mutableArray reverseObjectEnumerator] allObjects] mutableCopy];` – Terry Jun 13 '17 at 12:40
  • @Terry: The semantics are different. In one case you modify the actual array (everywhere it's used) and in the other case you make a copy. – Georg Schölly Jun 13 '17 at 15:20
51

Some benchmarks

1. reverseObjectEnumerator allObjects

This is the fastest method:

NSArray *anArray = @[@"aa", @"ab", @"ac", @"ad", @"ae", @"af", @"ag",
        @"ah", @"ai", @"aj", @"ak", @"al", @"am", @"an", @"ao", @"ap", @"aq", @"ar", @"as", @"at",
        @"au", @"av", @"aw", @"ax", @"ay", @"az", @"ba", @"bb", @"bc", @"bd", @"bf", @"bg", @"bh",
        @"bi", @"bj", @"bk", @"bl", @"bm", @"bn", @"bo", @"bp", @"bq", @"br", @"bs", @"bt", @"bu",
        @"bv", @"bw", @"bx", @"by", @"bz", @"ca", @"cb", @"cc", @"cd", @"ce", @"cf", @"cg", @"ch",
        @"ci", @"cj", @"ck", @"cl", @"cm", @"cn", @"co", @"cp", @"cq", @"cr", @"cs", @"ct", @"cu",
        @"cv", @"cw", @"cx", @"cy", @"cz"];

NSDate *methodStart = [NSDate date];

NSArray *reversed = [[anArray reverseObjectEnumerator] allObjects];

NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);

Result: executionTime = 0.000026

2. Iterating over an reverseObjectEnumerator

This is between 1.5x and 2.5x slower:

NSDate *methodStart = [NSDate date];
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[anArray count]];
NSEnumerator *enumerator = [anArray reverseObjectEnumerator];
for (id element in enumerator) {
    [array addObject:element];
}
NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);

Result: executionTime = 0.000071

3. sortedArrayUsingComparator

This is between 30x and 40x slower (no surprises here):

NSDate *methodStart = [NSDate date];
NSArray *reversed = [anArray sortedArrayUsingComparator: ^(id obj1, id obj2) {
    return [anArray indexOfObject:obj1] < [anArray indexOfObject:obj2] ? NSOrderedDescending : NSOrderedAscending;
}];

NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);

Result: executionTime = 0.001100

So [[anArray reverseObjectEnumerator] allObjects] is the clear winner when it comes to speed and ease.

Johannes Fahrenkrug
  • 42,912
  • 19
  • 126
  • 165
  • And I can imagine that it will be a lot more than 30-40x slower for larger numbers of objects. I don't know what the complexity of the sort algorithm is (best case O(n*logn)?, but it's also calling indexOfObject, which is probably O(n). With the sort, that could be O(n^2*logn) or something. Not good! – Joseph Humfrey Apr 08 '14 at 09:32
  • 1
    What about a benchmark using `enumerateObjectsWithOptions:NSEnumerationReverse` ? – brandonscript May 10 '15 at 02:03
  • 2
    I just did a benchmark using `enumerateObjectsWithOptions:NSEnumerationReverse` -- the top one completed in `0.000072` seconds, the block method in `0.000009` seconds. – brandonscript May 10 '15 at 02:08
  • Nice observation, it makes sense to me, but I think that executions times are too short to conclude that X algorithm is Y times faster than other. In order to mesure algorithm execution performance we have to take care several things, like, Is there some process that is running at same time?. For instance, perhaps when you run the first algorithm you have more cache memory available, and so on. Moreover, there is only one data set, I think we should run with several data sets (with different sizes are compositions) to conclude. – pcambre Dec 15 '16 at 01:12
23

DasBoot has the right approach, but there are a few mistakes in his code. Here's a completely generic code snippet that will reverse any NSMutableArray in place:

/* Algorithm: swap the object N elements from the top with the object N 
 * elements from the bottom. Integer division will wrap down, leaving 
 * the middle element untouched if count is odd.
 */
for(int i = 0; i < [array count] / 2; i++) {
    int j = [array count] - i - 1;

    [array exchangeObjectAtIndex:i withObjectAtIndex:j];
}

You can wrap that in a C function, or for bonus points, use categories to add it to NSMutableArray. (In that case, 'array' would become 'self'.) You can also optimize it by assigning [array count] to a variable before the loop and using that variable, if you desire.

If you only have a regular NSArray, there's no way to reverse it in place, because NSArrays cannot be modified. But you can make a reversed copy:

NSMutableArray * copy = [NSMutableArray arrayWithCapacity:[array count]];

for(int i = 0; i < [array count]; i++) {
    [copy addObject:[array objectAtIndex:[array count] - i - 1]];
}

Or use this little trick to do it in one line:

NSArray * copy = [[array reverseObjectEnumerator] allObjects];

If you just want to loop over an array backwards, you can use a for/in loop with [array reverseObjectEnumerator], but it's likely a bit more efficient to use -enumerateObjectsWithOptions:usingBlock::

[array enumerateObjectsWithOptions:NSEnumerationReverse
                        usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    // This is your loop body. Use the object in obj here. 
    // If you need the index, it's in idx.
    // (This is the best feature of this method, IMHO.)
    // Instead of using 'continue', use 'return'.
    // Instead of using 'break', set '*stop = YES' and then 'return'.
    // Making the surrounding method/block return is tricky and probably
    // requires a '__block' variable.
    // (This is the worst feature of this method, IMHO.)
}];

(Note: Substantially updated in 2014 with five more years of Foundation experience, a new Objective-C feature or two, and a couple tips from the comments.)

Becca Royal-Gordon
  • 17,541
  • 7
  • 56
  • 91
8

The most efficient way to enumerate an array in reverse:

Use enumerateObjectsWithOptions:NSEnumerationReverse usingBlock. Using @JohannesFahrenkrug's benchmark above, this completed 8x quicker than [[array reverseObjectEnumerator] allObjects];:

NSDate *methodStart = [NSDate date];

[anArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    //
}];

NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);
Community
  • 1
  • 1
brandonscript
  • 68,675
  • 32
  • 163
  • 220
8

After reviewing the other's answers above and finding Matt Gallagher's discussion here

I propose this:

NSMutableArray * reverseArray = [NSMutableArray arrayWithCapacity:[myArray count]]; 

for (id element in [myArray reverseObjectEnumerator]) {
    [reverseArray addObject:element];
}

As Matt observes:

In the above case, you may wonder if -[NSArray reverseObjectEnumerator] would be run on every iteration of the loop — potentially slowing down the code. <...>

Shortly thereafter, he answers thus:

<...> The "collection" expression is only evaluated once, when the for loop begins. This is the best case, since you can safely put an expensive function in the "collection" expression without impacting upon the per-iteration performance of the loop.

Aeronin
  • 89
  • 1
  • 1
8

Georg Schölly's categories are very nice. However, for NSMutableArray, using NSUIntegers for the indices results in a crash when the array is empty. The correct code is:

@implementation NSMutableArray (Reverse)

- (void)reverse {
    NSInteger i = 0;
    NSInteger j = [self count] - 1;
    while (i < j) {
        [self exchangeObjectAtIndex:i
                  withObjectAtIndex:j];

        i++;
        j--;
    }
}

@end
Werner Jainek
  • 91
  • 1
  • 4
7
NSMutableArray *objMyObject = [NSMutableArray arrayWithArray:[self reverseArray:objArrayToBeReversed]];

// Function reverseArray 
-(NSArray *) reverseArray : (NSArray *) myArray {   
    return [[myArray reverseObjectEnumerator] allObjects];
}
Jayprakash Dubey
  • 35,723
  • 18
  • 170
  • 177
3

Reverse array and looping through it:

[[[startArray reverseObjectEnumerator] allObjects] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    ...
}];
Aqib Mumtaz
  • 4,936
  • 1
  • 36
  • 33
2

To update this, in Swift it can be done easily with:

array.reverse()
Julio
  • 483
  • 1
  • 4
  • 17
1

As for me, have you considered how the array was populated in the first place? I was in the process of adding MANY objects to an array, and decided to insert each one at the beginning, pushing any existing objects up by one. Requires a mutable array, in this case.

NSMutableArray *myMutableArray = [[NSMutableArray alloc] initWithCapacity:1];
[myMutableArray insertObject:aNewObject atIndex:0];
James Perih
  • 1,360
  • 1
  • 17
  • 19
1

Or the Scala-way:

-(NSArray *)reverse
{
    if ( self.count < 2 )
        return self;
    else
        return [[self.tail reverse] concat:[NSArray arrayWithObject:self.head]];
}

-(id)head
{
    return self.firstObject;
}

-(NSArray *)tail
{
    if ( self.count > 1 )
        return [self subarrayWithRange:NSMakeRange(1, self.count - 1)];
    else
        return @[];
}
1

There is a easy way to do it.

    NSArray *myArray = @[@"5",@"4",@"3",@"2",@"1"];
    NSMutableArray *myNewArray = [[NSMutableArray alloc] init]; //this object is going to be your new array with inverse order.
    for(int i=0; i<[myNewArray count]; i++){
        [myNewArray insertObject:[myNewArray objectAtIndex:i] atIndex:0];
    }
    //other way to do it
    for(NSString *eachValue in myArray){
        [myNewArray insertObject:eachValue atIndex:0];
    }

    //in both cases your new array will look like this
    NSLog(@"myNewArray: %@", myNewArray);
    //[@"1",@"2",@"3",@"4",@"5"]

I hope this helps.

vroldan
  • 1,077
  • 9
  • 12
0

Try this:

for (int i = 0; i < [arr count]; i++)
{
    NSString *str1 = [arr objectAtIndex:[arr count]-1];
    [arr insertObject:str1 atIndex:i];
    [arr removeObjectAtIndex:[arr count]-1];
}
Pang
  • 9,564
  • 146
  • 81
  • 122
Shashank shree
  • 164
  • 2
  • 11
0

If all you want to do is iterate in reverse, try this:

// iterate backwards
nextIndex = (currentIndex == 0) ? [myArray count] - 1 : (currentIndex - 1) % [myArray count];

You can do the [myArrayCount] once and save it to a local variable (I think its expensive), but I’m also guessing that the compiler will pretty much do the same thing with the code as written above.

DougPA
  • 85
  • 8
0

Swift 3 syntax :

let reversedArray = array.reversed()
fethica
  • 759
  • 6
  • 10
0

Here is a nice macro that will work for either NSMutableArray OR NSArray:

#define reverseArray(__theArray) {\
    if ([__theArray isKindOfClass:[NSMutableArray class]]) {\
        if ([(NSMutableArray *)__theArray count] > 1) {\
            NSUInteger i = 0;\
            NSUInteger j = [(NSMutableArray *)__theArray count]-1;\
            while (i < j) {\
                [(NSMutableArray *)__theArray exchangeObjectAtIndex:i\
                                                withObjectAtIndex:j];\
                i++;\
                j--;\
            }\
        }\
    } else if ([__theArray isKindOfClass:[NSArray class]]) {\
        __theArray = [[NSArray alloc] initWithArray:[[(NSArray *)__theArray reverseObjectEnumerator] allObjects]];\
    }\
}

To use just call: reverseArray(myArray);

Albert Renshaw
  • 17,282
  • 18
  • 107
  • 195
0

I don't know of any built in method. But, coding by hand is not too difficult. Assuming the elements of the array you are dealing with are NSNumber objects of integer type, and 'arr' is the NSMutableArray that you want to reverse.

int n = [arr count];
for (int i=0; i<n/2; ++i) {
  id c  = [[arr objectAtIndex:i] retain];
  [arr replaceObjectAtIndex:i withObject:[arr objectAtIndex:n-i-1]];
  [arr replaceObjectAtIndex:n-i-1 withObject:c];
}

Since you start with a NSArray then you have to create the mutable array first with the contents of the original NSArray ('origArray').

NSMutableArray * arr = [[NSMutableArray alloc] init];
[arr setArray:origArray];

Edit: Fixed n -> n/2 in the loop count and changed NSNumber to the more generic id due to the suggestions in Brent's answer.

Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
Himadri Choudhury
  • 10,217
  • 6
  • 39
  • 47