Devo invertire il mio NSArray
.
Come esempio:
[1,2,3,4,5]
deve diventare: [5,4,3,2,1]
Qual è il modo migliore per raggiungere questo obiettivo?
Devo invertire il mio NSArray
.
Come esempio:
[1,2,3,4,5]
deve diventare: [5,4,3,2,1]
Qual è il modo migliore per raggiungere questo obiettivo?
Risposte:
Per ottenere una copia invertita di un array, guarda la soluzione di danielpunkass usando reverseObjectEnumerator
.
Per invertire un array mutabile, è 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
Esiste una soluzione molto più semplice, se si sfrutta il reverseObjectEnumerator
metodo integrato attivo NSArray
e il allObjects
metodo di NSEnumerator
:
NSArray* reversedArray = [[startArray reverseObjectEnumerator] allObjects];
allObjects
è documentato come restituire un array con gli oggetti che non sono stati ancora attraversati nextObject
, nell'ordine:
Questa matrice contiene tutti gli oggetti rimanenti dell'enumeratore in ordine enumerato .
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. Iterazione su un reverseObjectEnumerator
Questo è 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. sortArrayUsingComparator
Questo è 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à.
enumerateObjectsWithOptions:NSEnumerationReverse
?
enumerateObjectsWithOptions:NSEnumerationReverse
: il primo completato in 0.000072
pochi secondi, il metodo del blocco in 0.000009
pochi secondi.
DasBoot ha l'approccio giusto, ma ci sono alcuni errori nel suo codice. Ecco uno snippet di codice completamente generico che annullerà qualsiasi NSMutableArray in atto:
/* 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];
}
Puoi racchiuderlo in una funzione C o, per i punti bonus, utilizzare le categorie per aggiungerlo a NSMutableArray. (In tal caso, 'array' diventerebbe 'self'.) Puoi anche ottimizzarlo assegnando [array count]
una variabile prima del ciclo e usando quella variabile, se lo desideri.
Se si dispone solo di un normale NSArray, non è possibile invertirlo in posizione, poiché NSArrays non può essere modificato. Ma puoi fare una copia rovesciata:
NSMutableArray * copy = [NSMutableArray arrayWithCapacity:[array count]];
for(int i = 0; i < [array count]; i++) {
[copy addObject:[array objectAtIndex:[array count] - i - 1]];
}
O usa questo piccolo trucco per farlo in una riga:
NSArray * copy = [[array reverseObjectEnumerator] allObjects];
Se vuoi semplicemente eseguire il loop su un array all'indietro, puoi usare un for
/ in
loop con [array reverseObjectEnumerator]
, ma è probabilmente un po 'più efficiente da usare -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 altri cinque anni di esperienza della Fondazione, una o due nuove funzionalità di Objective-C e un paio di suggerimenti dai commenti.)
Dopo aver esaminato le risposte dell'altro sopra e trovato qui la discussione di Matt Gallagher
Propongo questo:
NSMutableArray * reverseArray = [NSMutableArray arrayWithCapacity:[myArray count]];
for (id element in [myArray reverseObjectEnumerator]) {
[reverseArray addObject:element];
}
Come osserva Matt:
Nel caso precedente, potresti chiederti se - [NSArray reverseObjectEnumerator] verrebbe eseguito su ogni iterazione del ciclo - potenzialmente rallentando il codice. <...>
Poco dopo, risponde così:
<...> L'espressione "collection" viene valutata una sola volta, quando inizia il ciclo for. Questo è il caso migliore, poiché puoi tranquillamente inserire una funzione costosa nell'espressione "collection" senza influire sulle prestazioni di pererazione del loop.
Le categorie di Georg Schölly sono molto belle. Tuttavia, per NSMutableArray, l'utilizzo di NSUIntegers per gli indici provoca un arresto anomalo quando l'array è vuoto. 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
Usa enumerateObjectsWithOptions:NSEnumerationReverse usingBlock
. Utilizzando il punto di riferimento @ JohannesFahrenkrug sopra, questo ha completato 8 volte più velocemente 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) {
...
}];
Quanto a me, hai considerato come l'array era popolato in primo luogo? Stavo aggiungendo MOLTI oggetti a un array e ho deciso di inserirli all'inizio, spingendo tutti gli oggetti esistenti di uno. Richiede un array mutabile, in questo caso.
NSMutableArray *myMutableArray = [[NSMutableArray alloc] initWithCapacity:1];
[myMutableArray insertObject:aNewObject atIndex:0];
O alla 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 @[];
}
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 possa essere d'aiuto.
Non conosco alcun metodo integrato. Ma codificare a mano non è troppo difficile. Supponendo che gli elementi dell'array con cui si ha a che fare siano oggetti NSNumber di tipo intero e 'arr' è l'array NSMutable che si desidera 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];
}
Poiché inizi con un NSArray, devi prima creare l'array mutabile con i contenuti dell'originale NSArray ('origArray').
NSMutableArray * arr = [[NSMutableArray alloc] init];
[arr setArray:origArray];
Modifica: risolto n -> n / 2 nel conteggio dei loop e modificato NSNumber nell'ID più generico a causa dei suggerimenti nella risposta di Brent.
Se tutto ciò che vuoi fare è iterare al contrario, prova questo:
// iterate backwards
nextIndex = (currentIndex == 0) ? [myArray count] - 1 : (currentIndex - 1) % [myArray count];
Puoi fare una volta [myArrayCount] e salvarlo in una variabile locale (penso che sia costoso), ma sto anche immaginando che il compilatore farà praticamente la stessa cosa con il codice come scritto sopra.
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];
}
Ecco una macro bello 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);