Como posso reverter um NSArray no Objective-C?

Eu preciso reverter meu NSArray .

Como um exemplo:

[1,2,3,4,5] deve se tornar: [5,4,3,2,1]

Qual a melhor maneira de alcançar isto?

Para obter uma cópia invertida de um array, veja a solução danielpunkass ‘ usando reverseObjectEnumerator .

Para reverter um array mutável, você pode adicionar a seguinte categoria ao seu 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 

Existe uma solução muito mais fácil, se você aproveitar o método allObjects no NSArray e o método allObjects do NSEnumerator :

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

allObjects é documentado como retornando uma matriz com os objects que ainda não foram percorridos com nextObject , em ordem:

Essa matriz contém todos os objects restantes do enumerador em ordem enumerada .

Alguns benchmarks

1. reverseObjectEnumerator allObjects

Este é o método mais 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 um reverseObjectEnumerator

Isso é entre 1,5x e 2,5x mais 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

Isso é entre 30x e 40x mais lento (sem surpresas aqui):

 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

Então [[anArray reverseObjectEnumerator] allObjects] é o vencedor claro quando se trata de velocidade e facilidade.

O DasBoot tem a abordagem correta, mas existem alguns erros em seu código. Aqui está um trecho de código completamente genérico que irá reverter qualquer NSMutableArray no 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]; } 

Você pode envolver isso em uma function C, ou para pontos de bônus, usar categorias para adicioná-lo ao NSMutableArray. (Nesse caso, 'array' se tornaria 'self'.) Você também pode otimizá-lo atribuindo [array count] a uma variável antes do loop e usando essa variável, se desejar.

Se você tem apenas um NSArray regular, não há como invertê-lo, porque o NSArrays não pode ser modificado. Mas você pode fazer uma cópia invertida:

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

Ou use esse pequeno truque para fazer isso em uma linha:

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

Se você quiser apenas fazer o loop de uma matriz para trás, você pode usar um loop for / in com [array reverseObjectEnumerator] , mas é provável que seja um pouco mais 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 : Substancialmente atualizado em 2014 com mais cinco anos de experiência em Foundation, um novo recurso do Objective-C ou dois, e algumas dicas dos comentários.)

Depois de rever as respostas do outro acima e encontrar a discussão de Matt Gallagher aqui

Eu proponho isso:

 NSMutableArray * reverseArray = [NSMutableArray arrayWithCapacity:[myArray count]]; for (id element in [myArray reverseObjectEnumerator]) { [reverseArray addObject:element]; } 

Como Matt observa:

No caso acima, você pode se perguntar se – [NSArray reverseObjectEnumerator] seria executado em cada iteração do loop – potencialmente abrandando o código. < ...>

Pouco depois, ele responde assim:

< ...> A expressão “coleção” é avaliada apenas uma vez, quando o loop for começa. Esse é o melhor caso, pois você pode colocar com segurança uma function cara na expressão “coleção” sem afetar o desempenho por iteração do loop.

As categorias de Georg Schölly são muito agradáveis. No entanto, para NSMutableArray, usando NSUIntegers para os resultados resulta em uma falha quando a matriz está vazia. O código correto é:

 @implementation NSMutableArray (Reverse) - (void)reverse { NSInteger i = 0; NSInteger j = [self count] - 1; while (i < j) { [self exchangeObjectAtIndex:i withObjectAtIndex:j]; i++; j--; } } @end 
 NSMutableArray *objMyObject = [NSMutableArray arrayWithArray:[self reverseArray:objArrayToBeReversed]]; // Function reverseArray -(NSArray *) reverseArray : (NSArray *) myArray { return [[myArray reverseObjectEnumerator] allObjects]; } 

A maneira mais eficiente de enumerar um array ao contrário:

Use enumerateObjectsWithOptions:NSEnumerationReverse usingBlock . Usando o benchmark do @ JohannesFahrenkrug acima, isso completou 8x mais rápido do 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); 

Reverse array e looping por ele:

 [[[startArray reverseObjectEnumerator] allObjects] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { ... }]; 

Para atualizar isso, no Swift, isso pode ser feito facilmente com:

 array.reverse() 

Quanto a mim, você já considerou como o array foi preenchido em primeiro lugar? Eu estava no processo de adicionar MUITOS objects a uma matriz e decidi inserir cada um no começo, empurrando todos os objects existentes para cima em um. Requer um array mutável, neste caso.

 NSMutableArray *myMutableArray = [[NSMutableArray alloc] initWithCapacity:1]; [myMutableArray insertObject:aNewObject atIndex:0]; 

Ou o caminho do 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 @[]; } 

Eu não sei de nenhum método embutido. Mas, codificar à mão não é muito difícil. Supondo que os elementos da matriz com os quais você está lidando sejam objects NSNumber do tipo inteiro, e ‘arr’ é o NSMutableArray que você deseja inverter.

 int n = [arr count]; for (int i=0; i 

Desde que você começa com um NSArray, então você tem que criar o array mutável primeiro com o conteúdo do NSArray original ('origArray').

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

Edit: Corrigido n -> n / 2 na contagem de loop e alterado NSNumber para o id mais genérico, devido às sugestões na resposta de Brent.

Se tudo o que você deseja fazer é repetir ao contrário, tente o seguinte:

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

Você pode fazer o [myArrayCount] uma vez e salvá-lo em uma variável local (acho que é caro), mas também estou supondo que o compilador fará praticamente a mesma coisa com o código escrito acima.

Sintaxe do Swift 3:

 let reversedArray = array.reversed() 

Tente isto:

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

Existe uma maneira fácil de fazer isso.

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

Eu espero que isso ajude.

Aqui está uma boa macro que funcionará para NSMutableArray OU 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 basta chamar: reverseArray(myArray);