Comment puis-je inverser un NSArray dans Objective-C?

Je dois inverser mon NSArray .

Par exemple:

[1,2,3,4,5] doit devenir: [5,4,3,2,1]

Quelle est la meilleure façon d’y parvenir?

Pour obtenir une copie inversée d’un tableau, consultez la solution de reverseObjectEnumerator aide de reverseObjectEnumerator .

Pour inverser un tableau mutable, vous pouvez append la catégorie suivante à votre 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 

Il existe une solution beaucoup plus simple, si vous tirez parti de la méthode reverseObjectEnumerator sur NSArray et de la méthode NSEnumerator de NSEnumerator :

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

allObjects est documenté comme renvoyant un tableau avec les objects qui n’ont pas encore été parcourus avec nextObject , dans l’ordre:

Ce tableau contient tous les objects restants de l’énumérateur dans l’ordre énuméré .

Quelques repères

1. reverseObjectEnumerator allObjects

C’est la méthode la plus rapide:

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

Résultat: executionTime = 0.000026

2. Itération sur un reverseObjectEnumerator

C’est entre 1.5x et 2.5x plus lentement:

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

Résultat: executionTime = 0.000071

3. classésArrayUsingComparator

C’est entre 30x et 40x plus lent (pas de surprise ici):

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

Résultat: executionTime = 0.001100

Donc, [[anArray reverseObjectEnumerator] allObjects] est le gagnant évident en [[anArray reverseObjectEnumerator] allObjects] de rapidité et de facilité.

DasBoot a la bonne approche, mais il y a quelques erreurs dans son code. Voici un extrait de code complètement générique qui annulera tout NSMutableArray en 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]; } 

Vous pouvez emballer cela dans une fonction C ou, pour les points bonus, utiliser des catégories pour l'append à NSMutableArray. (Dans ce cas, "array" deviendrait "self".) Vous pouvez également l'optimiser en affectant [array count] à une variable avant la boucle et en utilisant cette variable, si vous le souhaitez.

Si vous ne disposez que d'un NSArray standard, il est impossible de l'inverser car NSArrays ne peut pas être modifié. Mais vous pouvez faire une copie inversée:

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

Ou utilisez ce petit truc pour le faire en une seule ligne:

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

Si vous voulez simplement passer en boucle sur un tableau, vous pouvez utiliser une boucle for / in avec [array reverseObjectEnumerator] , mais il est probablement plus efficace d'utiliser -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 sortingcky and probably // requires a '__block' variable. // (This is the worst feature of this method, IMHO.) }]; 

( Remarque : mise à jour substantielle en 2014 avec cinq années supplémentaires d'expérience de la Fondation, une nouvelle fonctionnalité Objective-C ou deux, et quelques conseils tirés des commentaires.)

Après avoir examiné les réponses ci-dessus et avoir trouvé la discussion de Matt Gallagher ici

Je propose ceci:

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

Comme Matt l’observe:

Dans le cas ci-dessus, vous pouvez vous demander si – [NSArray reverseObjectEnumerator] serait exécuté à chaque itération de la boucle – ce qui pourrait ralentir le code. <...>

Peu de temps après, il répond ainsi:

<...> L’expression “collection” n’est évaluée qu’une seule fois, lorsque la boucle for commence. C’est le meilleur cas, car vous pouvez sans risque mettre une fonction coûteuse dans l’expression “collection” sans affecter la performance par itération de la boucle.

Les catégories de Georg Schölly sont très agréables. Toutefois, pour NSMutableArray, l’utilisation de NSUIntegers pour les index entraîne un blocage lorsque le tableau est vide. Le code correct est:

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

Le moyen le plus efficace d’énumérer un tableau en sens inverse:

Utilisez enumerateObjectsWithOptions:NSEnumerationReverse usingBlock . En utilisant le [[array reverseObjectEnumerator] allObjects]; @ JohannesFahrenkrug ci-dessus, cela a été 8 fois plus rapide 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); 

Inverser le tableau et le parcourir en boucle:

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

Pour mettre à jour cela, dans Swift, cela peut être fait facilement avec:

 array.reverse() 

En ce qui me concerne, avez-vous envisagé la façon dont le tableau était rempli en premier lieu? J’étais en train d’append des objects MANY à un tableau et j’ai décidé de les insérer au début, en poussant tous les objects existants de un. Nécessite un tableau mutable, dans ce cas.

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

Ou le chemin de la 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 @[]; } 

Je ne connais aucune méthode intégrée. Mais, coder à la main n’est pas trop difficile. En supposant que les éléments du tableau que vous traitez sont des objects NSNumber de type entier, et «arr» est le NSMutableArray que vous souhaitez inverser.

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

Puisque vous commencez avec un NSArray, vous devez d'abord créer le tableau mutable avec le contenu du NSArray original («origArray»).

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

Edit: Correction de n -> n / 2 dans le compte de la boucle et modification de NSNumber par un identifiant plus générique en raison des suggestions de la réponse de Brent.

Si tout ce que vous voulez faire est d’itérer en sens inverse, essayez ceci:

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

Vous pouvez faire le [myArrayCount] une fois et l’enregistrer dans une variable locale (je pense que c’est cher), mais je suppose également que le compilateur fera pratiquement la même chose avec le code écrit ci-dessus.

Swift 3 syntaxe:

 let reversedArray = array.reversed() 

Essaye ça:

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

Il y a un moyen facile de le faire.

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

J'espère que ça aide.

Voici une belle macro qui fonctionnera pour 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]];\ }\ } 

Pour utiliser, appelez simplement: reverseArray(myArray);