views:

6305

answers:

7

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?

A: 

just read the array from back to front. wouldn't that work?

Mladen Prajdic
no i really need to reverse my array
Andy Jacobs
A: 

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.

DasBoot
Isn't this missing a release on c?
Clay Bridges
+7  A: 

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 intact if count is odd 
 */
for(int i = 0; i < [array count] / 2; i++) {
    int j = [array count] - i - 1;

    id temp = [[array objectAtIndex:i] retain];

    [array replaceObjectAtIndex:i withObject:[array objectAtIndex:j]];
    [array replaceObjectAtIndex:j withObject:temp];

    [temp release];
}

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'.)

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]];
}
Brent Royal-Gordon
Does that work? I don't think NSMutableArray has a setObject:atIndex: method. Thanks for the suggested fix for the loop though, and using generic id instead of NSNumber.
DasBoot
You're right, I caught that when I read some of the other examples. Fixed now.
Brent Royal-Gordon
[array count] is called every time you loop. This is very costly. There's even a function which changes the positions of two objects.
Georg
+21  A: 
@implementation NSArray (Reverse)

- (NSArray *)reversedArray {
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count]];
    NSEnumerator *enumerator = [self reverseObjectEnumerator];
    for (id element in enumerator) {
        [array addObject:element];
    }
    return array;
}

@end

@implementation NSMutableArray (Reverse)

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

        i++;
        j--;
    }
}

@end
Georg
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.
Brent Royal-Gordon
They are better than C++ iterators, but I definitely won't miss enumerators.
Georg
Wht are they better then C++ iterators?
Mykola Golubyev
Because C++-iterators have an even worse syntax, they are ugly.
Georg
+36  A: 

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];

Because allObjects is documented as returning an array with the objects that have not yet been traversed with nextObject, it strongly implies that those objects will be delivered in order of the enumerator. It even points out that after calling allObjects, the next object on the enumerator will be nil.

danielpunkass
Now that's clever!
Georg
But, as you said yourself, Apple doesn't guarantee that the array is in the correct order. (Though I think it is.)
Georg
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
What do mean by 'wrong set'? A array that is not in reverse order?
Simo Salminen
A: 

It's also worth looking at this: 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).

scott Lewis
+1  A: 

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!

Matt Williamson
Can you describe the circumstances? iPhone, Mac, which Version?
Georg
It was iphone 3.0.
Matt Williamson
I'm wanting reproduceable code on this.
Clay Bridges
You want code to reproduce the bug or a working reverse snippit?
Matt Williamson