Domanda

Ho bisogno di invertire la mia NSArray.

Come esempio:

[1,2,3,4,5] deve diventare: [5,4,3,2,1]

Qual è il modo migliore per raggiungere questo obiettivo?

È stato utile?

Soluzione

Per ottenere una copia invertita di un array, guarda danielpunkass' soluzione utilizzando reverseObjectEnumerator.

Per invertire una matrice mutevole, è possibile aggiungere la seguente categoria al codice:

@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

Altri suggerimenti

C'è una soluzione molto più semplice, se si prende vantaggio del metodo reverseObjectEnumerator built-in su NSArray, e il metodo di allObjects NSEnumerator:

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

allObjects è documentato come restituendo un array con gli oggetti che non sono ancora stati attraversati con nextObject, in ordine:

  

Questa matrice contiene tutti i restanti oggetti del enumeratore al fine elencate .

Alcuni parametri di riferimento

1. reverseObjectEnumerator allObjects

Questo è il metodo più veloce:

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

Risultato: executionTime = 0.000026

2. L'iterazione di un reverseObjectEnumerator

Questa è compresa tra 1,5x e 2,5x più 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);

Risultato: executionTime = 0.000071

3. sortedArrayUsingComparator

Questa è compresa tra 30x e 40x più lento (nessuna sorpresa qui):

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

Risultato: executionTime = 0.001100

Quindi [[anArray reverseObjectEnumerator] allObjects] è il chiaro vincitore quando si tratta di velocità e facilità.

DasBoot ha il giusto approccio, ma ci sono un paio di errori nel suo codice.Ecco una completamente generico frammento di codice che inverte qualsiasi NSMutableArray a posto:

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

Si può avvolgere in una funzione C, o per i punti bonus, utilizzare le categorie per aggiungere NSMutableArray.(In questo caso, 'matrice' sarebbe diventato il 'sé'.) È inoltre possibile ottimizzare assegnando [array count] per una variabile prima del ciclo e l'utilizzo di tale variabile, se lo desideri.

Se si dispone solo di un normale NSArray, non c'è modo di invertire la tendenza in atto, perché NSArrays non può essere modificato.Ma si può fare una copia invertita:

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

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

O utilizzare questo trucco per farlo in una sola riga:

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

Se si desidera solo per loop su un array all'indietro, è possibile utilizzare un for/in anello con [array reverseObjectEnumerator], ma è probabile che un po ' più efficiente l'uso -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:Sostanzialmente aggiornato nel 2014, con cinque anni di esperienza della Fondazione, un nuovo Obiettivo-C o due, e un paio di suggerimenti dai commenti.)

Dopo aver esaminato le risposte degli altri sopra e trovare la discussione di Matt Gallagher qui

Vi propongo questo:

NSMutableArray * reverseArray = [NSMutableArray arrayWithCapacity:[myArray count]]; 

for (id element in [myArray reverseObjectEnumerator]) {
    [reverseArray addObject:element];
}

Come osserva Matt:

  

Nel caso di cui sopra, ci si potrebbe chiedere se - [NSArray reverseObjectEnumerator] sarebbe stato eseguito su ogni iterazione del ciclo - potenzialmente rallentare il codice. <...>

Poco dopo, lui risponde così:

  

<...> L'espressione "collezione" viene valutata solo una volta, quando inizia il ciclo for. Questo è il caso migliore, dal momento che si può mettere in sicurezza una funzione costosa nell'espressione "collezione" senza alcun impatto sulle prestazioni per-iterazione del ciclo.

categorie di Georg Schölly sono molto belle. Tuttavia, per NSMutableArray, utilizzando NSUIntegers per gli indici traduce in un incidente quando la matrice è vuota. Il codice corretto è:

@implementation NSMutableArray (Reverse)

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

        i++;
        j--;
    }
}

@end

Il modo più efficace per enumerare una matrice in senso inverso:

Usa enumerateObjectsWithOptions:NSEnumerationReverse usingBlock. Utilizzando @ benchmark JohannesFahrenkrug sopra, questo ha completato 8x più veloce di [[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];
}

matrice inversa e loop attraverso di essa:

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

Per aggiornare questo, a Swift si può fare facilmente con:

array.reverse()

Per quanto riguarda me, avete considerato come la matrice è stata popolata, in primo luogo? Ero nel processo di aggiunta di molti oggetti a un array, e decisi di inserire ciascuno all'inizio, spingendo tutti gli oggetti esistenti da uno. Richiede una matrice mutevole, in questo caso.

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

O lo Scala-way:

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

Non so di alcun costruita nel metodo. Ma, codifica a mano non è troppo difficile. Supponendo che l'elementi della matrice si sta trattando sono oggetti NSNumber di tipo intero, e 'arr' è il NSMutableArray che si vuole invertire.

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

Dal momento che si inizia con un NSArray allora è necessario creare l'array mutabile prima con i contenuti del NSArray originale ( 'origArray').

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

Modifica:. Fisso n -> n / 2 nel numero di loop e ha cambiato NSNumber alla id più generico a causa dei suggerimenti in risposta di Brent

Se tutto quello che vogliamo fare è iterare in senso inverso, provate questo:

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

Si può fare lo [myArrayCount] una volta e salvarlo in una variabile locale (credo che il suo costoso), ma sono anche indovinando che il compilatore praticamente fare la stessa cosa con il codice come scritto sopra.

Swift 3 sintassi:

let reversedArray = array.reversed()

Prova questo:

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

C'è un modo semplice per farlo.

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

Spero che questo aiuta.

Ecco una bella macro che funziona per entrambi i 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]];\
    }\
}

Per usare basta chiamare: reverseArray(myArray);

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top