Necesito revertir 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?
Necesito revertir 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?
Respuestas:
Para obtener una copia invertida de una matriz, 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
Hay una solución mucho más fácil, si aprovecha el reverseObjectEnumerator
método incorporado NSArray
y el allObjects
método de NSEnumerator
:
NSArray* reversedArray = [[startArray reverseObjectEnumerator] allObjects];
allObjects
está documentado como devolver una matriz con los objetos que aún no se han recorrido nextObject
, en orden:
Esta matriz contiene todos los objetos restantes del enumerador en orden enumerado .
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 reverseObjectEnumerator
Esto es entre 1.5x y 2.5x 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. sortedArrayUsingComparator
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í [[anArray reverseObjectEnumerator] allObjects]
es el claro ganador cuando se trata de velocidad y facilidad.
enumerateObjectsWithOptions:NSEnumerationReverse
?
enumerateObjectsWithOptions:NSEnumerationReverse
: el primero completado en 0.000072
segundos, el método de bloqueo en 0.000009
segundos.
DasBoot tiene el enfoque correcto, pero hay algunos errores en su código. Aquí hay un fragmento de código completamente genérico que revertirá 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 ajustar 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 asignándolo [array count]
a una variable antes del ciclo y utilizando esa variable, si lo desea.
Si solo tiene un NSArray regular, no hay forma de revertirlo en su lugar, porque los NSArray no pueden modificarse. 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 use este pequeño truco para hacerlo en una línea:
NSArray * copy = [[array reverseObjectEnumerator] allObjects];
Si solo desea recorrer una matriz hacia atrás, puede usar un for
/ in
loop con [array reverseObjectEnumerator]
, pero es probable que sea un poco más eficiente de 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 par de consejos de los comentarios).
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, responde así:
<...> La expresión "colección" solo se evalúa una vez, cuando comienza el ciclo for. Este es el mejor caso, ya que puede poner de manera segura una función costosa en la expresión "colección" sin afectar el rendimiento por iteración del bucle.
Las categorías de Georg Schölly son muy agradables. Sin embargo, para NSMutableArray, el uso de NSUIntegers para los índices da como resultado 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
Uso 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);
NSMutableArray *objMyObject = [NSMutableArray arrayWithArray:[self reverseArray:objArrayToBeReversed]];
// Function reverseArray
-(NSArray *) reverseArray : (NSArray *) myArray {
return [[myArray reverseObjectEnumerator] allObjects];
}
Matriz inversa y recorrerla:
[[[startArray reverseObjectEnumerator] allObjects] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
...
}];
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 los objetos existentes hacia arriba uno por uno. Requiere una matriz mutable, en este caso.
NSMutableArray *myMutableArray = [[NSMutableArray alloc] initWithCapacity:1];
[myMutableArray insertObject:aNewObject atIndex:0];
O el camino de escala:
-(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 @[];
}
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.
No conozco ningún método incorporado. Pero, la codificación 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];
}
Como comienza con un NSArray, primero debe crear la matriz mutable con el contenido del NSArray original ('origArray').
NSMutableArray * arr = [[NSMutableArray alloc] init];
[arr setArray:origArray];
Editar: Se corrigió n -> n / 2 en el recuento del bucle y se cambió NSNumber a la identificación más genérica debido a las sugerencias en la respuesta de Brent.
Si todo lo que quiere hacer es iterar en reversa, intente esto:
// iterate backwards
nextIndex = (currentIndex == 0) ? [myArray count] - 1 : (currentIndex - 1) % [myArray count];
Puede hacer [myArrayCount] una vez y guardarlo en una variable local (creo que es costoso), pero también supongo que el compilador hará lo mismo con el código que se escribió anteriormente.
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];
}
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 solo llame: reverseArray(myArray);