Objective-C bir NSArray nasıl tersine çevirebilirim?


356

Geri çevirmem gerek NSArray .

Örnek olarak:

[1,2,3,4,5] olmalı: [5,4,3,2,1]

Bunu başarmanın en iyi yolu nedir?


1
Ayrıca buna da değinmeye değer: bir diziyi ters sırada nasıl sıralayacağınızı söyleyen http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Collections/Articles/sortingFilteringArrays.html. örneğin NSDictionary # allKeys türetilmiş bir dizi kullanarak yapıyorsunuz ve ters tarih / alfa siparişinin iPhone'da UITable için gruplama işlevi görmesini istiyorsunuz).

Yanıtlar:


305

Bir dizinin ters kopyasını elde etmek için, kullanarak danielpunkass'ın çözümüne bakın reverseObjectEnumerator.

Değiştirilebilir bir diziyi tersine çevirmek için kodunuza aşağıdaki kategoriyi ekleyebilirsiniz:

@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

15
Hızlı Numaralandırma ile ilgili kötü şeylerden biri, benim gibi yeni adamların reverseObjectEnumerator gibi havalı şeyleri öğrenmemesidir. Bunu yapmanın oldukça düzgün bir yolu.
Brent Royal-Gordon

4
C ++ - yineleyiciler daha da kötü bir sözdizimine sahip oldukları için çirkinler.
Georg Schölly

4
Diziyi geri göndermeden önce kopyalamamalısınız?
CIFilter

12
@Georg: Bu konuda sana katılmıyorum. Değişmez bir nesne döndüren bir yöntem görürsem, aslında değişmez bir nesne döndürmesini beklerim. Değişmez bir nesneyi geri döndürüyormuş gibi görünmek ama değişebilir bir nesneyi ele geçirmek tehlikeli bir uygulamadır.
Christine

3
Tüm değişkenler tersine çevrilmediği için reverseObjectEnumerator allObjects önermek yararlı değildir. Değişmezliğin çalışma zamanında test edilmemesi gereken, ancak döndürülen türe göre kabul edilmesi gereken Apple belgeleri, bu durumda bir NSMutableArray döndürmek mükemmel bir şekilde doğru koddur.
Peter N Lewis

1287

Eğer yararlanmak eğer çok daha kolay bir çözüm yoktur yerleşik reverseObjectEnumeratorüzerine yönteme NSArrayve allObjectsyöntemin NSEnumerator:

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

allObjectshenüz üzerinde hareket edilmemiş nesnelerle birlikte bir dizi döndürüleceği belgeleniyornextObject :

Bu dizi, numaralandırıcının kalan tüm nesnelerini numaralandırılmış sırada içerir .


6
Burada Matt Williamson'ın bir yorum olması gereken bir yanıtı var
Georg Schölly

1
'Yanlış set' ile ne demek? Ters sırada olmayan bir dizi?
Simo Salminen

31
Artık bu hatayı yeniden üretemiyorum. Benim hatam olabilirdi. Bu çok zarif bir çözüm.
Matt Williamson

3
Sipariş artık belgelerde garanti edilmektedir.
jscs

eminim bu iyi bir cevap ama Değişken Nesneler için başarısız olacaktır. NSEnumerator salt okunur nesne türü @property sağladığı için (salt okunur, kopya) NSArray <ObjectType> * allObjects;
Anurag Soni

49

Bazı kriterler

1. reverseObjectEnumerator allObjects

Bu en hızlı yöntemdir:

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

Sonuç: executionTime = 0.000026

2. Bir reverseObjectEnumerator üzerinden yineleme

Bu 1,5x ile 2,5x arasında daha yavaştır:

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

Sonuç: executionTime = 0.000071

3. sortArrayUsingComparator

Bu 30x ile 40x arasında daha yavaştır (burada sürpriz yok):

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

Sonuç: executionTime = 0.001100

Yani [[anArray reverseObjectEnumerator] allObjects]bu hız ve kolaylığı geldiğinde eşi yoktur.


Ve daha fazla sayıda nesne için 30-40x'ten daha yavaş olacağını hayal edebiliyorum. Sıralama algoritması karmaşıklığını bilmiyorum (en iyi durumda O (n * logn)?, Ama aynı zamanda muhtemelen O (n) olan indexOfObject çağırıyor. Sıralama ile O (n ^ 2 olabilir) * logn) falan ... İyi değil!
Joseph Humfrey

1
Kullanarak bir kıyaslama ne olacak enumerateObjectsWithOptions:NSEnumerationReverse?
brandonscript

2
Ben sadece kullanarak bir kriter yaptım enumerateObjectsWithOptions:NSEnumerationReverse- ilk bir 0.000072saniye içinde tamamlandı , blok yöntemi 0.000009saniye içinde.
brandonscript

Güzel bir gözlem, bana mantıklı geliyor, ancak sanırım X algoritmasının diğerlerinden Y kat daha hızlı olduğu sonucuna varmak için çok kısa. Algoritma yürütme performansını ölçmek için, aynı anda çalışan bir süreç var mı? Gibi birkaç şeye dikkat etmeliyiz. Örneğin, belki de ilk algoritmayı çalıştırdığınızda daha fazla önbellek kullanılabilir ve bu şekilde devam eder. Dahası, sadece bir veri kümesi var, sanırım sonuçlandırmak için birkaç veri seti (farklı boyutlarda kompozisyonlar) ile çalışmalıyız.
pcambre

21

DasBoot'un doğru yaklaşımı var, ancak kodunda birkaç hata var. İşte herhangi bir NSMutableArray'i tersine çevirecek tamamen genel bir kod snippet'i:

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

Bunu bir C işlevinde sarabilir veya bonus puanlar için NSMutableArray'e eklemek için kategorileri kullanabilirsiniz. (Bu durumda, 'dizi' 'benlik' olur.) Ayrıca atayarak da onu optimize edebilirsiniz.[array count] önce bir değişkene ve isterseniz bu değişkeni kullanarak .

Yalnızca normal bir NSArray'ınız varsa, onu tersine çevirmenin bir yolu yoktur, çünkü NSArray'ler değiştirilemez. Ancak ters çevrilmiş bir kopya oluşturabilirsiniz:

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

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

Veya tek bir satırda yapmak için bu küçük numarayı kullanın:

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

Bir diziyi geriye doğru döngüye almak istiyorsanız, for/ indöngüsünü ile kullanabilirsiniz [array reverseObjectEnumerator], ancak kullanımı biraz daha etkilidir -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.)
}];

( Not : 2014 yılında beş yıllık Vakıf deneyimi, yeni bir Objective-C özelliği ve iki yorumdan birkaç ipucu ile önemli ölçüde güncellendi.)


İşe yarıyor mu? NSMutableArray bir setObject: atIndex: yöntemi olduğunu sanmıyorum. Döngü için önerilen düzeltme olsa da ve NSNumber yerine genel kimlik kullanarak teşekkürler.
Himadri Choudhury

Haklısın, diğer örneklerden bazılarını okuduğumda yakaladım. Şimdi düzeltildi.
Brent Royal-Gordon

2
[dizi sayısı] her döngü yaptığınızda çağrılır. Bu çok maliyetli. İki nesnenin konumunu değiştiren bir işlev bile var.
Georg Schölly

8

Diğerlerinin yukarıdaki cevaplarını inceledikten ve Matt Gallagher'ın burada tartışmasını bulduktan sonra

Bunu öneriyorum:

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

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

Matt'in gözlemlediği gibi:

Yukarıdaki durumda, - [NSArray reverseObjectEnumerator] öğesinin döngünün her yinelemesinde çalıştırılıp çalıştırılmayacağını merak edebilirsiniz - muhtemelen kodu yavaşlatır. <...>

Kısa bir süre sonra şöyle cevap verir:

<...> "toplama" ifadesi, for döngüsü başladığında yalnızca bir kez değerlendirilir. Bu, en iyi durumdur, çünkü "yineleme" ifadesine, döngünün yineleme başına performansını etkilemeden güvenli bir şekilde pahalı bir işlev koyabilirsiniz.


8

Georg Schölly'nin kategorileri çok güzel. Ancak, NSMutableArray için, dizin boş olduğunda dizinler için NSUIntegers kullanmak çökme ile sonuçlanır. Doğru kod:

@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

Bir diziyi tersine sıralamanın en etkili yolu:

Kullanın enumerateObjectsWithOptions:NSEnumerationReverse usingBlock. @ JohannesFahrenkrug'un yukarıdaki ölçütünü kullanarak, bu, aşağıdakilerden 8 kat daha hızlı tamamladı [[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
NSMutableArray *objMyObject = [NSMutableArray arrayWithArray:[self reverseArray:objArrayToBeReversed]];

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

3

Ters dizi ve içinden döngü:

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

2

Bunu güncellemek için Swift'te aşağıdakilerle kolayca yapılabilir:

array.reverse()

1

Bana gelince, dizinin ilk başta nasıl doldurulduğunu düşündünüz mü? Bir diziye MANY nesneleri ekleme sürecindeydim ve her birini başlangıçta eklemeye karar verdim, mevcut nesneleri birer birer yukarı iterek. Bu durumda değiştirilebilir bir dizi gerekir.

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

1

Veya Scala-yolu:

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

2
Özyineleme, bellek bakımından büyük veri yapıları üzerinde korkunç bir fikirdir.
Eran Goldin

Kuyruk özyineleme ile derlenirse sorun olmamalı
simple_code

1

Bunu yapmanın kolay bir yolu var.

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

Umarım bu yardımcı olur.


0

Yerleşik bir yöntem bilmiyorum. Ancak, elle kodlamak çok zor değildir. Karşılaştığınız dizinin öğelerinin, tamsayı türünde NSNumber nesneleri ve 'arr', tersine çevirmek istediğiniz NSMutableArray olduğunu varsayarsak.

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

Bir NSArray ile başladığınız için, önce orijinal NSArray ('origArray') içeriğiyle mutable dizisini oluşturmanız gerekir.

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

Düzenleme: Döngü sayısında n -> n / 2 düzeltildi ve Brent'in cevabındaki öneriler nedeniyle NSNumber'ı daha genel kimliğe değiştirdi.


1
Bu, c'de serbest bırakılmamış mı?
Clay Bridges

0

Tek yapmak istediğiniz şey tersine yinelemekse, şunu deneyin:

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

Bir kere [myArrayCount] yapabilir ve yerel bir değişkene kaydedebilirim (pahalı olduğunu düşünüyorum), ama aynı zamanda derleyicinin yukarıda yazılı kodla hemen hemen aynı şeyi yapacağını tahmin ediyorum.


0

Swift 3 sözdizimi:

let reversedArray = array.reversed()

0

Bunu dene:

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

İşte NSMutableArray VEYA NSArray için çalışacak güzel bir makro :

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

Kullanmak için arayın: reverseArray(myArray);

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.