web-dev-qa-db-fra.com

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

J'ai besoin d'inverser ma NSArray.

Par exemple:

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

Quel est le meilleur moyen d'y parvenir?

336
Andy Jacobs

Pour obtenir une copie inversée d'un tableau, consultez solution de danielpunkass avec reverseObjectEnumerator.

Pour inverser un tableau mutable, vous pouvez ajouter 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
300
Georg Schölly

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

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

allObjects est documenté comme retournant un tableau avec les objets qui n'ont pas encore été parcourus avec nextObject, dans l'ordre:

Ce tableau contient tous les objets restants de l'énumérateur dans l'ordre énuméré.

1246
danielpunkass

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

Ceci est entre 1,5x et 2,5x plus lent:

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. sortArrayUsingComparator

Ceci est entre 30x et 40x plus lent (pas de surprises 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 grand gagnant en matière de rapidité et de facilité.

48

DasBoot a la bonne approche, mais il y a quelques erreurs dans son code. Voici un extrait de code entièrement générique qui inversera 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 envelopper cela dans une fonction C, ou pour les points bonus, utiliser des catégories pour l'ajouter à 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 tableau NSArray standard, il est impossible d'inverser le processus, car les tableaux NSArrays ne peuvent pas être modifiés. 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 souhaitez simplement parcourir en boucle un tableau, vous pouvez utiliser une boucle for/in avec [array reverseObjectEnumerator], mais il est probablement un peu 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 tricky and probably
    // requires a '__block' variable.
    // (This is the worst feature of this method, IMHO.)
}];

( Remarque : substantiellement mis à jour 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.)

19
Brent Royal-Gordon

Les catégories de Georg Schölly sont très gentilles. 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
8
Werner Jainek

Après avoir examiné les réponses de l’autre ci-dessus et 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 le remarque Matt:

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

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

<...> L'expression "collection" n'est évaluée qu'une fois, au début de la boucle for. C'est le meilleur des cas, car vous pouvez mettre en toute sécurité une fonction coûteuse dans l'expression "collection" sans affecter les performances de la boucle en itération.

8
Aeronin
NSMutableArray *objMyObject = [NSMutableArray arrayWithArray:[self reverseArray:objArrayToBeReversed]];

// Function reverseArray 
-(NSArray *) reverseArray : (NSArray *) myArray {   
    return [[myArray reverseObjectEnumerator] allObjects];
}
7
Jayprakash Dubey

Le moyen le plus efficace d'énumérer un tableau à l'envers:

Utilisez enumerateObjectsWithOptions:NSEnumerationReverse usingBlock. En utilisant le repère ci-dessus de @ JohannesFahrenkrug, cela a été réalisé 8 fois plus vite 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
brandonscript

Inverser le tableau et le parcourir en boucle:

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

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

array.reverse()
2
Julio Vasquez

Quant à moi, avez-vous envisagé la composition du tableau? J'étais en train d'ajouter BEAUCOUP objets à un tableau et j'ai décidé d'insérer chacun d'eux au début, en augmentant d'un seul les objets existants. Nécessite un tableau mutable, dans ce cas.

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

Ou la voie 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
Hugo Stieglitz

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.

0
vroldan

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];
}
0
Shashank shree

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 objets NSNumber de type entier et que «arr» est le NSMutableArray que vous souhaitez inverser.

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

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

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

Edit: Correction de n -> n/2 dans le nombre de boucles et changement de NSNumber en id plus générique en raison des suggestions de la réponse de Brent.

0
Himadri Choudhury

Voici une macro Nice qui fonctionnera pour NSMutableArrayOUNSArray:

#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, il suffit d'appeler: reverseArray(myArray);

0
Albert Renshaw

Syntaxe de Swift 3:

let reversedArray = array.reversed()
0
fethica

Si tout ce que vous voulez faire, c'est itérer à l'envers, 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 aussi que le compilateur fera à peu près la même chose avec le code écrit ci-dessus.

0
DougPA