¿Cómo puedo invertir un NSArray en Objective-C?


Necesito invertir mi NSArray.

Como ejemplo:

[1,2,3,4,5] debe convertirse en: [5,4,3,2,1]

¿Cuál es la mejor manera de lograr esto?

Author: James Harnett, 2009-02-25

18 answers

Para obtener una copia invertida de un array, mira la solución de danielpunkass usando reverseObjectEnumerator.

Para invertir una matriz mutable, puede agregar la siguiente categoría a su código:

@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
 300
Author: Georg Schölly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-24 09:07:17

Hay una solución mucho más fácil, si se aprovecha del método incorporado reverseObjectEnumerator en NSArray, y el método allObjects de NSEnumerator:

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

allObjects se documenta como devolver un array con los objetos que aún no han sido atravesados con nextObject, en orden:

Esta matriz contiene todos los objetos restantes del enumerador en orden enumerado.

 1238
Author: danielpunkass,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-27 03:31:37

Algunos puntos de referencia

1. reverseObjectEnumerator allObjects

Este es el método más rápido:

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

Resultado: executionTime = 0.000026

2. Iterando sobre un inverso fijador

Esto es entre 1.5 x y 2.5 x más lento:

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

Resultado: executionTime = 0.000071

3. Comparador de navegación de clasificación

Esto es entre 30x y 40x más lento (sin sorpresas aquí):

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

Resultado: executionTime = 0.001100

Así que [[anArray reverseObjectEnumerator] allObjects] es el claro ganador cuando se trata de velocidad y facilidad.

 48
Author: Johannes Fahrenkrug,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-01 13:03:57

DasBoot tiene el enfoque correcto, pero hay algunos errores en su código. Aquí hay un fragmento de código completamente genérico que invertirá cualquier NSMutableArray en su lugar:

/* 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];
}

Puede envolver eso en una función C, o para puntos de bonificación, use categorías para agregarlo a NSMutableArray. (En ese caso, 'array' se convertiría en 'self'.) También puede optimizarlo asignando [array count] a una variable antes del bucle y usando esa variable, si lo desea.

Si solo tiene un NSArray regular, no hay manera de revertirlo en su lugar, porque NSArrays no puede ser modificado. Pero puedes hacer una copia invertida:

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

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

O usa este pequeño truco para hacerlo en una línea:

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

Si solo desea hacer un bucle sobre un array hacia atrás, puede usar un for/in loop con [array reverseObjectEnumerator], pero es probable que sea un poco más eficiente usar -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.)
}];

(Nota: Sustancialmente actualizado en 2014 con cinco años más de experiencia de la Fundación, una nueva característica de Objective-C o dos, y un un par de consejos de los comentarios.)

 19
Author: Brent Royal-Gordon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-10 23:36:16

Después de revisar las respuestas del otro arriba y encontrar La discusión de Matt Gallagher aquí

Propongo esto:

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

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

Como Matt observa:

En el caso anterior, puede preguntarse si -[NSArray reverseObjectEnumerator] se ejecutaría en cada iteración del bucle, lo que podría ralentizar el código. <...>

Poco después, él responde así:

<...> La expresión" colección " solo se evalúa una vez, cuando el bucle for comenzar. Este es el mejor caso, ya que puede colocar de forma segura una función costosa en la expresión "colección" sin afectar el rendimiento por iteración del bucle.

 8
Author: Aeronin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-23 03:11:45

Las categorías de Georg Schölly son muy bonitas. Sin embargo, para NSMutableArray, usar NSUIntegers para los índices resulta en un bloqueo cuando la matriz está vacía. El código correcto es:

@implementation NSMutableArray (Reverse)

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

        i++;
        j--;
    }
}

@end
 8
Author: Werner Jainek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-12 12:05:53
NSMutableArray *objMyObject = [NSMutableArray arrayWithArray:[self reverseArray:objArrayToBeReversed]];

// Function reverseArray 
-(NSArray *) reverseArray : (NSArray *) myArray {   
    return [[myArray reverseObjectEnumerator] allObjects];
}
 7
Author: Jayprakash Dubey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-21 14:36:37

La forma más eficiente de enumerar un array al revés:

Use enumerateObjectsWithOptions:NSEnumerationReverse usingBlock. Usando el punto de referencia de @JohannesFahrenkrug anterior, esto se completó 8 veces más rápido que [[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);
 7
Author: brandonscript,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-10 02:59:13

Matriz inversa y bucle a través de ella:

[[[startArray reverseObjectEnumerator] allObjects] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    ...
}];
 3
Author: Aqib Mumtaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-25 20:15:52

Para actualizar esto, en Swift se puede hacer fácilmente con:

array.reverse()
 2
Author: Julio Vasquez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-17 19:45:40

En cuanto a mí, ¿has considerado cómo se pobló la matriz en primer lugar? Estaba en el proceso de agregar MUCHOS objetos a una matriz, y decidí insertar cada uno al principio, empujando cualquier objeto existente hacia arriba por uno. Requiere una matriz mutable, en este caso.

NSMutableArray *myMutableArray = [[NSMutableArray alloc] initWithCapacity:1];
[myMutableArray insertObject:aNewObject atIndex:0];
 1
Author: James Perih,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-14 01:45:59

O la vía Scala:

-(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
Author: Hugo Stieglitz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-23 10:24:12

No conozco ningún método incorporado. Pero, codificar a mano no es demasiado difícil. Suponiendo que los elementos de la matriz con la que está tratando son objetos NSNumber de tipo entero, y 'arr' es el NSMutableArray que desea revertir.

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

Dado que se empieza con un NSArray, primero se tiene que crear el array mutable con el contenido del NSArray original ('origArray').

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

Editar: Corregido n - > n / 2 en el conteo de bucles y cambiado NSNumber al id más genérico due a las sugerencias de la respuesta de Brent.

 0
Author: Himadri Choudhury,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-25 15:41:19

Si todo lo que quieres hacer es iterar al revés, prueba esto:

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

Puede hacer el [myArrayCount] una vez y guardarlo en una variable local (creo que es caro), pero también estoy adivinando que el compilador hará más o menos lo mismo con el código que se escribió anteriormente.

 0
Author: DougPA,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-16 23:06:35

Swift 3 sintaxis:

let reversedArray = array.reversed()
 0
Author: fethica,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-30 18:04:26

Prueba esto:

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];
}
 0
Author: Shashank shree,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-09 08:51:58

Hay una manera fácil de hacerlo.

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

Espero que esto ayude.

 0
Author: vroldan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-09 20:17:39

Aquí hay una buena macro que funcionará para NSMutableArray O 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]];\
    }\
}

Para usar simplemente llame: reverseArray(myArray);

 0
Author: Albert Renshaw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-14 19:00:36