361 votes

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

Je dois inverser mon NSArray .

Par exemple:

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

Quel est le meilleur moyen d'y parvenir?

1306voto

danielpunkass Points 10076

Il est beaucoup plus facile de solution, si vous prenez avantage de la bâtie-en reverseObjectEnumerator méthode NSArray, et l' allObjects méthode de NSEnumerator:

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

Parce qu' allObjects est documenté comme renvoyant un tableau avec les objets qui n'ont pas encore été traversée par des nextObject, il implique fortement que ces objets seront remis dans l'ordre de l'agent recenseur. Il précise même qu'après l'appel de allObjects, l'objet suivant sur le recenseur sera nil.

305voto

Georg Schölly Points 63123
@implementation NSArray (Reverse)

- (NSArray *)reversedArray {
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count]];
    NSEnumerator *enumerator = [self reverseObjectEnumerator];
    for (id element in enumerator) {
        [array addObject:element];
    }
    return array;
}

@end

@implementation NSMutableArray (Reverse)

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

        i++;
        j--;
    }
}

@end

50voto

Johannes Fahrenkrug Points 12795

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. Une itération sur un reverseObjectEnumerator

C'est entre 1,5 x et 2.5 x 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. sortedArrayUsingComparator

C'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 gagnant clair quand il s'agit de la vitesse et de la facilité.

21voto

Brent Royal-Gordon Points 8044

DasBoot a la bonne approche, mais il y a quelques erreurs dans son code. Voici un complètement générique extrait de code qui va inverser 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 ce dans une fonction C, ou pour les points de bonus, les catégories d'utilisation à l'ajouter à NSMutableArray. (Dans ce cas, 'array' serait 'auto'.) Vous pouvez également optimiser en attribuant [array count] d'une variable avant la boucle et l'utilisation de cette variable, si vous le désirez.

Si vous n'avez régulière NSArray, il n'y a aucune possibilité de revenir en place, parce que NSArrays ne peut pas être modifié. Mais vous pouvez faire une copie inverse:

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

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

Ou utiliser cette petite astuce pour le faire en une seule ligne:

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

Si vous voulez juste faire une boucle sur un tableau à l'envers, vous pouvez utiliser un for/in boucle avec [array reverseObjectEnumerator], mais c'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.)
}];

(Note: Sensiblement mis à jour en 2014 avec plus de cinq ans de la Fondation de l'expérience, un nouvel Objectif-C ou les deux, et quelques conseils de commentaires.)

18voto

Dans la plupart des cas, l'exigence est d'inverser la même instance de tableau.

 array = [[array reverseObjectEnumerator] allObjects];
 

Travaille pour moi.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X