Objective-C에서 NSArray를 어떻게 되돌릴 수 있습니까?


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에서 파생 된 배열을 사용하고 있으며 날짜 / 알파 순서를 반대로하여 iPhone의 UITable에 대한 그룹화를 수행하려는 경우 등).

답변:


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
Fast Enumeration의 나쁜 점 중 하나는 저와 같은 새로운 사람들이 reverseObjectEnumerator와 같은 멋진 것들에 대해 배우지 못한다는 것입니다. 아주 깔끔한 방법입니다.
브렌트 로얄-고든

4
C ++ 반복자는 더 나쁜 구문을 가지고 있기 때문에 추악합니다.
Georg Schölly

4
반환하기 전에 배열을 복사해서는 안됩니까?
CIFilter

12
@ 조지 : 나는 이것에 대해 당신과 동의하지 않습니다. 불변 개체를 반환하는 메서드가 표시되면 실제로 불변 개체를 반환 할 것으로 예상됩니다. 변경 불가능한 객체를 반환하는 것처럼 보이지만 실제로는 변경 가능한 객체를 반환하는 것은 위험한 습관입니다.
Christine

3
이 역하지 않기 때문에 reverseObjectEnumerator allObjects을 제안하는 것은 도움이되지 않습니다 에도 mutableCopy 여전히 원래의 배열이 돌연변이되지 않기 때문에 도움이되지 것입니다 추가 변경 가능한 배열을. 불변성은 런타임에 테스트 되지 않아야하지만 반환 유형을 기반으로 가정 되는 Apple 문서 이므로이 경우 NSMutableArray를 반환하는 것은 완벽하게 올바른 코드입니다.
피터 N 루이스

1287

당신을 활용하면 훨씬 쉽게 솔루션이 내장 reverseObjectEnumerator에 대한 방법 NSArrayallObjects방법 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

이것이 좋은 대답이라고 확신하지만 Mutable Objects에서는 실패합니다. 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.5 배에서 2.5 배 사이입니다.

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. sortedArrayUsingComparator

이것은 30 배에서 40 배 사이의 속도입니다 (여기서 놀라운 것은 아님).

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-40 배 이상 느리다고 상상할 수 있습니다. 정렬 알고리즘의 복잡성이 무엇인지 모르겠습니다 (최상의 경우 O (n * logn)?하지만 아마도 O (n) 인 indexOfObject를 호출합니다. 정렬하면 O (n ^ 2 일 수 있음 * 로그온) 또는 무언가 좋지 않음
Joseph Humfrey

1
를 사용하는 벤치 마크는 enumerateObjectsWithOptions:NSEnumerationReverse어떻습니까?
brandonscript

2
방금 벤치 마크를 수행 enumerateObjectsWithOptions:NSEnumerationReverse했습니다. 최고 1 0.000072초 만에 블록 방법을 0.000009초 단위로 완료했습니다 .
brandonscript

좋은 관찰, 나에게 의미가 있지만 X 알고리즘이 다른 알고리즘보다 Y 배 빠르다는 결론을 내리기에는 실행 시간이 너무 짧다고 생각합니다. 알고리즘 실행 성능을 측정하려면 다음과 같은 몇 가지 사항을주의해야합니다. 동시에 실행중인 프로세스가 있습니까? 예를 들어, 첫 번째 알고리즘을 실행할 때 더 많은 캐시 메모리를 사용할 수 있습니다. 또한 데이터 세트가 하나뿐이므로 여러 데이터 세트 (크기가 다른 컴포지션)로 실행해야 결론을 내릴 수 있습니다.
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 만있는 경우 NSArray를 수정할 수 없기 때문에이를 뒤집을 방법이 없습니다. 그러나 반대의 사본을 만들 수 있습니다.

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

배열을 거꾸로 반복 하려면 for/ in루프를 사용하여 사용할 수 있지만 사용하는 [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 년에 5 년 이상의 재단 경험, 새로운 Objective-C 기능 또는 2 개의 의견과 의견으로 실질적으로 업데이트되었습니다.)


작동합니까? NSMutableArray에 setObject : atIndex : 메소드가 있다고 생각하지 않습니다. 그래도 루프에 대한 제안 된 수정에 감사하고 NSNumber 대신 일반 ID를 사용하십시오.
Himadri Choudhury

네가 맞아, 나는 다른 예제를 읽을 때 그것을 붙 잡았다. 지금 수정했습니다.
브렌트 로얄-고든

2
루프 할 때마다 [배열 카운트]가 호출됩니다. 이것은 매우 비싸다. 두 객체의 위치를 ​​변경하는 기능도 있습니다.
Georg Schölly

8

위의 상대방의 답변을 검토하고 Matt Gallagher의 토론을 여기서 찾은 후

나는 이것을 제안한다 :

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

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

매트가 관찰 한대로 :

위의 경우 루프가 반복 될 때마다-[NSArray reverseObjectEnumerator]가 실행되어 코드 속도가 느려질 수 있습니다. <...>

그 후 얼마 안있어 그는 이렇게 대답합니다.

<...> "루프"표현식은 for 루프가 시작될 때 한 번만 평가됩니다. 루프의 반복 별 성능에 영향을주지 않으면 서 "컬렉션"표현식에 값 비싼 함수를 안전하게 넣을 수 있기 때문에 이것이 가장 좋은 경우입니다.


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의 벤치 마크를 사용하면 [[array reverseObjectEnumerator] allObjects];다음 보다 8 배 빠르게 완료됩니다 .

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) {
    ...
}];

2

이것을 업데이트하려면 Swift에서 다음을 사용하여 쉽게 수행 할 수 있습니다.

array.reverse()

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를보다 일반적인 ID로 변경했습니다.


1
이것이 c의 릴리스가 누락되지 않았습니까?
Clay Bridges

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.