ฉันจะสลับ NSArray ใน Objective-C ได้อย่างไร


356

NSArrayฉันต้องย้อนกลับของฉัน

ตัวอย่างเช่น:

[1,2,3,4,5] จะต้องกลายเป็น: [5,4,3,2,1]

อะไรคือวิธีที่ดีที่สุดในการบรรลุเป้าหมายนี้?


1
นอกจากนี้ยังควรดูที่: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Collections/Articles/sortingFilteringArrays.htmlซึ่งจะบอกวิธีการเรียงลำดับอาร์เรย์ในลำดับกลับกัน คุณกำลังทำตัวอย่างเช่นในการใช้อาร์เรย์ที่มาจาก NSDictionary # allKeys และคุณต้องการ reverse date / alpha order เพื่อใช้เป็นกลุ่มสำหรับ UITable บน iPhone และอื่น ๆ

คำตอบ:


305

สำหรับการได้รับสำเนาแบบผันกลับของอาร์เรย์ดูที่วิธีการแก้ปัญหา danielpunkass'reverseObjectEnumeratorใช้

สำหรับการย้อนกลับอาร์เรย์ที่ไม่แน่นอนคุณสามารถเพิ่มหมวดหมู่ต่อไปนี้ในรหัสของคุณ:

@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
หนึ่งในสิ่งที่ไม่ดีเกี่ยวกับการแจงนับอย่างรวดเร็วคือผู้ชายใหม่อย่างฉันไม่ได้เรียนรู้เกี่ยวกับสิ่งดีๆเช่น reverseObjectEnumerator วิธีทำสวยเนี๊ยบ
Brent Royal-Gordon

4
เนื่องจาก C ++ - ตัววนซ้ำมีไวยากรณ์แย่ยิ่งกว่าพวกมันน่าเกลียด
Georg Schölly

4
คุณไม่ควรคัดลอกอาร์เรย์ก่อนส่งคืน
CIFilter

12
@Gorg: ฉันไม่เห็นด้วยกับคุณในนี้ ถ้าฉันเห็นวิธีที่ส่งคืนวัตถุที่ไม่เปลี่ยนรูปฉันคาดหวังให้จริง ๆ แล้วจะคืนค่าวัตถุที่ไม่เปลี่ยนรูปแบบ หากว่ามันดูเหมือนว่าจะส่งคืนวัตถุที่ไม่เปลี่ยนรูป แต่จริง ๆ แล้วการส่งคืนวัตถุที่เปลี่ยนแปลงไม่ได้นั้นเป็นวิธีปฏิบัติที่อันตรายที่จะเข้าไป
Christine

3
เสนอแนะ reverseObjectEnumerator allObjects ไม่เป็นประโยชน์เพราะมันไม่ได้ย้อนกลับอาร์เรย์ไม่แน่นอนแม้การเพิ่ม mutableCopy จะไม่ช่วยเหลือตั้งแต่ยังอาร์เรย์ต้นฉบับไม่ได้กลายพันธุ์ เอกสาร Apple ที่ไม่สามารถเปลี่ยนแปลงได้ไม่ควรทดสอบ ณ รันไทม์ แต่ถูกสันนิษฐานตามชนิดที่ส่งคืนดังนั้นการส่งคืน NSMutableArray ในกรณีนี้คือรหัสที่ถูกต้องสมบูรณ์
Peter N Lewis

1287

มีวิธีแก้ปัญหาที่ง่ายกว่ามากถ้าคุณใช้ประโยชน์จากreverseObjectEnumeratorวิธีการในตัวNSArrayและallObjectsวิธีการNSEnumerator:

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

allObjectsมีการบันทึกว่าส่งคืนอาร์เรย์ด้วยวัตถุที่ยังไม่ได้ถูกสำรวจตามnextObjectลำดับ:

อาร์เรย์นี้มีทั้งหมดวัตถุที่เหลือของการแจงนับเพื่อแจกแจง


6
มีคำตอบเพิ่มเติมจากที่นี่โดย Matt Williamson ที่ควรแสดงความคิดเห็น: อย่าใช้โซลูชันของ danielpunkass ฉันใช้มันโดยคิดว่ามันเป็นทางลัดที่ยอดเยี่ยม แต่ตอนนี้ฉันใช้เวลา 3 ชั่วโมงในการหาสาเหตุที่อัลกอริทึม A * ของฉันเสีย เป็นเพราะมันส่งคืนชุดที่ไม่ถูกต้อง!
Georg Schölly

1
'ชุดที่ไม่ถูกต้อง' หมายถึงอะไร? อาร์เรย์ที่ไม่ได้อยู่ในลำดับย้อนกลับ?
Simo Salminen

31
ฉันไม่สามารถทำซ้ำข้อผิดพลาดนั้นได้อีก มันอาจเป็นข้อผิดพลาดของฉัน นี่เป็นทางออกที่หรูหรามาก
Matt Williamson

3
ตอนนี้รับประกันการสั่งซื้อในเอกสารประกอบ
jscs

ฉันแน่ใจว่านี่เป็นคำตอบที่ดี แต่มันจะล้มเหลวสำหรับวัตถุที่ไม่แน่นอน เนื่องจาก NSEnumerator จัดเตรียมออบเจ็กต์ชนิดคุณสมบัติ @ property (อ่านอย่างเดียวคัดลอก) NSArray <ObjectType> * allObjects แบบอ่านอย่างเดียว
Anurag Soni

49

มาตรฐานบางอย่าง

1. reverseObjectEnumerator allObjects ทั้งหมด

นี่เป็นวิธีที่เร็วที่สุด:

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

ผลลัพธ์: executionTime = 0.000026

2. วนซ้ำใน reverseObjectEnumerator

นี่คือระหว่าง 1.5x และ 2.5x ช้าลง:

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

ผลลัพธ์: executionTime = 0.000071

3. SortArrayUsingComparator

นี่คือระหว่าง 30x และ 40x ช้าลง (ไม่แปลกใจที่นี่):

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

ผลลัพธ์: executionTime = 0.001100

ดังนั้นจึง[[anArray reverseObjectEnumerator] allObjects]เป็นผู้ชนะที่ชัดเจนเมื่อพูดถึงเรื่องความเร็วและความสะดวก


และฉันสามารถจินตนาการได้ว่ามันจะช้ากว่า 30-40x มากสำหรับวัตถุจำนวนมาก ฉันไม่รู้ว่าความซับซ้อนของอัลกอริทึมการเรียงลำดับคืออะไร (กรณีที่ดีที่สุด O (n * logn)? แต่มันก็เรียก indexOfObject ซึ่งอาจเป็น O (n) ด้วยการเรียงลำดับที่อาจเป็น O (n ^ 2 * logn) หรือบางอย่างไม่ดี!
โจเซฟฮัมฟรีย์

1
เกณฑ์มาตรฐานใช้enumerateObjectsWithOptions:NSEnumerationReverseอะไร
brandonscript

2
ฉันเพิ่งสร้างเกณฑ์มาตรฐานโดยใช้enumerateObjectsWithOptions:NSEnumerationReverse- อันดับแรกเสร็จในไม่0.000072กี่วินาทีวิธีบล็อกในไม่0.000009กี่วินาที
brandonscript

การสังเกตที่ดีมันสมเหตุสมผลสำหรับฉัน แต่ฉันคิดว่าเวลาการประหารชีวิตสั้นเกินไปที่จะสรุปได้ว่าอัลกอริธึม X คือ Y คูณเร็วกว่าตัวอื่น เพื่อประสิทธิภาพการดำเนินการอัลกอริทึม mesure เราต้องดูแลหลาย ๆ อย่างเช่นมีกระบวนการบางอย่างที่ทำงานในเวลาเดียวกันหรือไม่? ตัวอย่างเช่นบางทีเมื่อคุณเรียกใช้อัลกอริทึมแรกคุณจะมีหน่วยความจำแคชมากขึ้นเรื่อย ๆ ยิ่งไปกว่านั้นมีชุดข้อมูลเพียงชุดเดียวเท่านั้นฉันคิดว่าเราควรใช้ชุดข้อมูลหลายชุด
pcambre

21

DasBoot มีแนวทางที่ถูกต้อง แต่มีข้อผิดพลาดเล็กน้อยในรหัสของเขา ต่อไปนี้เป็นข้อมูลโค้ดทั่วไปที่จะย้อนกลับ NSMutableArray ใด ๆ

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

คุณสามารถสรุปได้ในฟังก์ชัน C หรือสำหรับคะแนนโบนัสให้ใช้หมวดหมู่เพื่อเพิ่มเข้าไปใน NSMutableArray (ในกรณีนั้น 'อาเรย์' จะกลายเป็น 'ตัวเอง') คุณยังสามารถปรับให้เหมาะสมได้โดยกำหนดให้[array count]กับตัวแปรก่อนลูปและใช้ตัวแปรนั้นหากคุณต้องการ

หากคุณมี NSArray ปกติเท่านั้นคุณจะไม่สามารถย้อนกลับได้เนื่องจาก NSArrays ไม่สามารถแก้ไขได้ แต่คุณสามารถทำสำเนาย้อนกลับ:

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

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

หรือใช้เคล็ดลับเล็ก ๆ นี้ในบรรทัดเดียว:

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

หากคุณต้องการวนซ้ำอาร์เรย์ไปข้างหลังคุณสามารถใช้ a for/ inloop ได้[array reverseObjectEnumerator]แต่อาจมีประสิทธิภาพมากกว่าที่จะใช้-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.)
}];

( หมายเหตุ : อัปเดตอย่างมีนัยสำคัญในปี 2014 พร้อมด้วยประสบการณ์รากฐานอีกห้าปีคุณลักษณะ Objective-C ใหม่หรือสองและเคล็ดลับจากความคิดเห็น)


ใช้งานได้หรือไม่ ฉันไม่คิดว่า NSMutableArray มีวิธี setObject: atIndex: ขอบคุณสำหรับการแก้ไขที่แนะนำสำหรับการวนซ้ำแม้ว่าและใช้ id ทั่วไปแทน NSNumber
Himadri Choudhury

คุณพูดถูกฉันจับได้ว่าเมื่อฉันอ่านตัวอย่างอื่น แก้ไขแล้ว
Brent Royal-Gordon

2
[จำนวนอาร์เรย์] จะถูกเรียกทุกครั้งที่คุณวนซ้ำ มันมีราคาแพงมาก มีฟังก์ชั่นที่เปลี่ยนตำแหน่งของวัตถุสองอย่าง
Georg Schölly

8

หลังจากตรวจสอบคำตอบของผู้อื่นด้านบนและค้นหาการสนทนาของ Matt Gallagher ที่นี่

ฉันเสนอสิ่งนี้:

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

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

ในฐานะที่เป็นแมตต์สังเกต:

ในกรณีข้างต้นคุณอาจสงสัยว่า - [NSArray reverseObjectEnumerator] จะทำงานในการวนซ้ำทุกครั้งของลูป - อาจทำให้โค้ดช้าลง < ... >

ไม่นานหลังจากนั้นเขาตอบดังนี้:

<... > นิพจน์ "คอลเลกชัน" ถูกประเมินเพียงครั้งเดียวเมื่อเริ่มการวนซ้ำ นี่เป็นกรณีที่ดีที่สุดเนื่องจากคุณสามารถใส่ฟังก์ชั่นราคาแพงลงในนิพจน์ "คอลเลกชัน" ได้อย่างปลอดภัยโดยไม่กระทบกับประสิทธิภาพการวนซ้ำต่อเนื่องของลูป


8

หมวดหมู่ของ Georg Schöllyนั้นดีมาก อย่างไรก็ตามสำหรับ NSMutableArray การใช้ NSUIntegers สำหรับดัชนีส่งผลให้เกิดความผิดพลาดเมื่ออาร์เรย์ว่างเปล่า รหัสที่ถูกต้องคือ:

@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

วิธีที่มีประสิทธิภาพมากที่สุดในการแจกแจงอาร์เรย์ในสิ่งที่ตรงกันข้าม:

enumerateObjectsWithOptions:NSEnumerationReverse usingBlockใช้ การใช้เกณฑ์มาตรฐานของ @ JohannesFahrenkrug ด้านบนนี้เสร็จสมบูรณ์ 8x เร็วกว่า[[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

ย้อนกลับอาร์เรย์และวนซ้ำผ่านมัน

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


1

สำหรับฉันคุณได้พิจารณาแล้วว่าอาเรย์นั้นมีการเติมข้อมูลอย่างไรตั้งแต่แรก? ฉันอยู่ในขั้นตอนการเพิ่มวัตถุ MANY ลงในอาร์เรย์และตัดสินใจที่จะแทรกวัตถุแต่ละอันที่จุดเริ่มต้นผลักวัตถุใด ๆ ที่มีอยู่ขึ้นหนึ่งอัน ต้องมีอาร์เรย์ที่ไม่แน่นอนในกรณีนี้

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

1

หรือทางสกาลา:

-(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
การเรียกซ้ำเป็นความคิดที่แย่มากเกี่ยวกับโครงสร้างข้อมูลขนาดใหญ่หน่วยความจำที่ชาญฉลาด
Eran Goldin

หากคอมไพล์ด้วยการเรียกซ้ำแบบหางมันไม่ควรเป็นปัญหา
simple_code

1

มีวิธีง่าย ๆ ในการทำ

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

ฉันหวังว่านี่จะช่วยได้.


0

ฉันไม่รู้วิธีการในตัวใด ๆ แต่การเข้ารหัสด้วยมือนั้นไม่ยากเกินไป สมมติว่าองค์ประกอบของอาร์เรย์ที่คุณติดต่อด้วยคือวัตถุ NSNumber ประเภทจำนวนเต็มและ 'arr' เป็น NSMutableArray ที่คุณต้องการย้อนกลับ

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

เนื่องจากคุณเริ่มต้นด้วย NSArray คุณต้องสร้างอาร์เรย์ที่ไม่แน่นอนก่อนด้วยเนื้อหาของ NSArray ดั้งเดิม ('origArray')

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

แก้ไข: แก้ไข n -> n / 2 ในการนับลูปและเปลี่ยน NSNumber เป็นรหัสทั่วไปมากขึ้นเนื่องจากคำแนะนำในคำตอบของเบรนต์


1
นี่ไม่ใช่การปล่อยใน c หรือไม่?
สะพานดินเหนียว

0

หากสิ่งที่คุณต้องการทำคือทำซ้ำแบบย้อนกลับลองสิ่งนี้:

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

คุณสามารถทำ [myArrayCount] หนึ่งครั้งและบันทึกลงในตัวแปรท้องถิ่น (ฉันคิดว่ามันแพง) แต่ฉันก็คาดเดาว่าคอมไพเลอร์จะทำสิ่งเดียวกันกับรหัสตามที่เขียนไว้ด้านบน



0

ลองสิ่งนี้:

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

นี่คือแมโครที่ดีที่จะทำงานได้ทั้ง NSMutableArray หรือ 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]];\
    }\
}

วิธีใช้เพียงโทร: reverseArray(myArray);

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.