Objective-C의 NSMutableArray에서 중복 값을 제거하는 가장 좋은 방법은 무엇입니까?


147

Objective-C NSString에서 중복 값 ( ) 을 제거하는 가장 좋은 방법은 NSMutableArray무엇입니까?

이것이 가장 쉽고 올바른 방법입니까?

uniquearray = [[NSSet setWithArray:yourarray] allObjects];

5
정확히 동일한 객체 또는 별개의 객체이지만 모든 필드에 대해 동일한 값을 갖는 객체에 대한 참조를 제거 할 것인지를 명확히 할 수 있습니다.
Amagrammer 2016 년

배열의 복사본을 만들지 않고이 작업을 수행 할 수있는 방법이 없습니까?
hfossli

이 방법은 충분히 쉽고 아마도 가장 좋습니다. 그러나 예를 들어 내 경우에는 작동하지 않습니다. 배열의 항목이 완전히 중복되지 않으므로 하나의 속성으로 비교해야합니다.
Vyachaslav Gerchicov 2016 년

한번 시도해보십시오. stackoverflow.com/a/38007095/3908884
Doshi

답변:


242

당신 NSSet이 다시 객체의 순서에 대한 걱정,하지만하지 않는 경우 당신은 순서에 대해 걱정하지 않는 경우 접근 방법은 왜 당신은에 저장되지 않고, 최고 NSSet와 함께 시작?

2009 년에 아래 답변을 썼습니다. 2011 년에 Apple NSOrderedSet은 iOS 5 및 Mac OS X 10.7에 추가되었습니다 . 알고리즘은 이제 두 줄의 코드입니다.

NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
NSArray *arrayWithoutDuplicates = [orderedSet array];

주문이 걱정되고 iOS 4 또는 이전 버전에서 실행중인 경우 배열 사본을 반복하십시오.

NSArray *copy = [mutableArray copy];
NSInteger index = [copy count] - 1;
for (id object in [copy reverseObjectEnumerator]) {
    if ([mutableArray indexOfObject:object inRange:NSMakeRange(0, index)] != NSNotFound) {
        [mutableArray removeObjectAtIndex:index];
    }
    index--;
}
[copy release];

53
독창성과 순서가 필요한 경우 간단히 사용하십시오. [NSOrderedSet orderedSetWithArray:array];그런 다음 처음부터 대신을 array = [orderedSet allObjects];사용하거나 NSOrderedSets를 사용 하여 배열을 가져올 수 있습니다 NSArray.
Regexident

10
@Regexident의 솔루션이 이상적입니다. 그냥 교체해야 [orderedSet allObjects]와 함께 [orderedSet array]!
inket

니스 하나, 개발자 사본 및이 많은 수정없이 붙여하게 대답 등) I,이 모든 아이폰 OS 개발자가 같은 것이라는 대답은) @ abo3atef
Abo3atef

고맙지 만 예를 수정해야합니다. 이유-우리는 일반적으로 NSArraytemp를 가지고 있어야합니다 NSMutableArray. 귀하의 예제에서는 반대의 반대를 작동
Vyachaslav Gerchicov

중복을 제거하는 것이 가장 좋은 방법을 알고있는 사람이라면이 방법 ( NSSet) 또는 @Simon Whitaker 링크 가 효율적인 중복 값을 추가하기 전에 방지합니까?
Mathi Arasan 2016 년

78

나는 이것이 오래된 질문이라는 것을 알고 있지만 NSArray 주문에 신경 쓰지 않는다면 중복을 제거하는보다 우아한 방법이 있습니다.

Key Value Coding의 Object Operators를 사용 하면 다음과 같이 할 수 있습니다.

uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];

마찬가지로 AnthoPak이 또한 주목 그 속성에 기초하여 중복을 제거하는 것이 가능하다. 예를 들면 다음과 같습니다.@distinctUnionOfObjects.name


3
예, 이것이 제가 사용하는 것입니다! 이것은 많은 iOS 개발자들이 모르는 매우 강력한 접근법입니다!
Lefteris

1
이것이 가능하다는 사실을 알고 놀랐습니다. 나는 많은 iOS 개발자들이 이것을 알 수 없다고 생각했기 때문에이 답변을 추가하기로 결정했습니다. :)
Tiago Almeida

12
이것은 객체의 순서를 유지하지 않습니다.
Rudolf Adamkovič가 14:14

2
그래, 명령을 어 겼어
Rostyslav Druzhchenko

@distinctUnionOfObjects.property사용자 정의 객체 배열의 속성으로 중복을 제거 하는 것과 같이 사용할 수도 있습니다 . 예를 들어@distinctUnionOfObjects.name
AnthoPak

47

예, NSSet을 사용하는 것이 현명한 방법입니다.

Jim Puls의 답변에 추가하려면 순서를 유지하면서 중복을 제거하는 다른 방법이 있습니다.

// Initialise a new, empty mutable array 
NSMutableArray *unique = [NSMutableArray array];

for (id obj in originalArray) {
    if (![unique containsObject:obj]) {
        [unique addObject:obj];
    }
}

본질적으로 Jim과 동일한 접근 방법이지만 원본에서 복제본을 삭제하는 대신 고유 한 항목을 새로운 가변 배열에 복사합니다. 이것은 많은 중복을 가진 큰 배열 (전체 배열의 사본을 만들 필요가 없음)의 경우 약간 더 메모리 효율적이며, 제 생각에는 좀 더 읽기 쉽습니다.

두 경우 모두 대상 배열에 항목이 이미 포함되어 있는지 ( containsObject:내 예제 또는 indexOfObject:inRange:Jim의 경우에) 확인하면 큰 배열에는 잘 맞지 않습니다. 이러한 검사는 O (N) 시간에 실행됩니다. 즉, 원래 배열의 크기를 두 배로 늘리면 각 검사 를 실행하는 데 두 배의 시간이 걸립니다. 배열의 각 객체에 대해 검사를 수행하므로 더 비싼 검사를 더 많이 실행하게됩니다. 전체 알고리즘 (광산과 Jim 모두)은 O (N 2 ) 시간에 실행되며 원래 배열이 커짐에 따라 빠르게 비싸집니다.

NSMutableSetNSSet 조회가 O (N)가 아닌 O (1)이기 때문에 이를 O (N) 시간으로 낮추려면 a 를 사용하여 이미 새 배열에 추가 된 항목의 레코드를 저장할 수 있습니다 . 즉, 요소가 NSSet의 멤버인지 여부를 확인하면 세트에있는 요소 수에 관계없이 동일한 시간이 걸립니다.

이 접근법을 사용하는 코드는 다음과 같습니다.

NSMutableArray *unique = [NSMutableArray array];
NSMutableSet *seen = [NSMutableSet set];

for (id obj in originalArray) {
    if (![seen containsObject:obj]) {
        [unique addObject:obj];
        [seen addObject:obj];
    }
}

그래도 여전히 조금 낭비적인 것 같습니다. 우리는 원래 배열이 변경 가능하다는 질문이 명백해지면 여전히 새로운 배열을 생성하고 있으므로 중복 제거하고 메모리를 절약 할 수 있어야합니다. 이 같은:

NSMutableSet *seen = [NSMutableSet set];
NSUInteger i = 0;

while (i < [originalArray count]) {
    id obj = [originalArray objectAtIndex:i];

    if ([seen containsObject:obj]) {
        [originalArray removeObjectAtIndex:i];
        // NB: we *don't* increment i here; since
        // we've removed the object previously at
        // index i, [originalArray objectAtIndex:i]
        // now points to the next object in the array.
    } else {
        [seen addObject:obj];
        i++;
    }
}

업데이트 : 유리 니야 조프 (Yuri Niyazov) 아마도 O (N) 시간에 실행 되기 때문에 마지막 대답은 실제로 O (N 2 ) 에서 실행된다고 지적했습니다removeObjectAtIndex: .

(우리는 그것이 어떻게 구현되는지 알지 못하기 때문에 "아마도"라고 말하지만 가능한 한 구현은 인덱스 X에서 객체를 삭제 한 후 메소드가 인덱스 X + 1에서 배열의 마지막 객체까지 모든 요소를 ​​반복한다는 것입니다 , 이전 인덱스로 이동합니다.이 경우 실제로 O (N) 성능입니다.)

그래서 뭘 할건데? 상황에 따라 다릅니다. 대규모 배열이 있고 적은 수의 복제본 만 기대하는 경우 전체 중복 제거 기능이 제대로 작동하여 중복 배열을 구축하지 않아도됩니다. 중복이 많이 예상되는 어레이가있는 경우 별도의 중복 제거 된 어레이를 구축하는 것이 가장 좋은 방법 일 것입니다. 여기서의 탈취는 big-O 표기법은 알고리즘의 특성만을 설명하며 주어진 상황에 가장 적합한 것을 명확하게 알려주지는 않습니다.


20

iOS 5 이상 (전체 iOS 환경을 대상으로하는)을 대상으로하는 경우 가장 좋습니다 NSOrderedSet. 중복을 제거하고의 순서를 유지합니다 NSArray.

그냥 해

NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];

이제 고유 한 NSArray로 다시 변환 할 수 있습니다

NSArray *uniqueArray = orderedSet.array;

이 같은있는 NSArray 같은 동일한 방법을 가지고 있기 때문에 또는 단지 orderedSet를 사용 objectAtIndex:, firstObject등등합니다.

회원 가입 확인 contains은 회원 가입 보다 더 빠릅니다 NSOrderedSet.NSArray

자세한 체크 아웃을 위해 NSOrderedSet 참조


이것은 내 표를 얻었고, 나는 그들 모두를 읽었으며 가장 좋은 대답입니다. 최고의 대답은 수동 루프라고 믿을 수 없습니다. 오 그들은 이제이 답변을 복사했습니다.
malhal

19

OS X v10.7 이상에서 사용 가능합니다.

주문이 걱정되는 경우 올바른 방법

NSArray *no = [[NSOrderedSet orderedSetWithArray:originalArray]allObjects];

다음은 NSArray에서 중복 값을 순서대로 제거하는 코드입니다.


1
allObjects는 배열이어야합니다
malhal

7

주문이 필요하다

NSArray *yourarray = @[@"a",@"b",@"c"];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourarray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
NSLog(@"%@",arrayWithoutDuplicates);

또는 주문이 필요하지 않습니다

NSSet *set = [NSSet setWithArray:yourarray];
NSArray *arrayWithoutOrder = [set allObjects];
NSLog(@"%@",arrayWithoutOrder);

3

여기에서 mainArray에서 중복 이름 값을 제거하고 결과를 NSMutableArray (listOfUsers)에 저장합니다.

for (int i=0; i<mainArray.count; i++) {
    if (listOfUsers.count==0) {
        [listOfUsers addObject:[mainArray objectAtIndex:i]];

    }
   else if ([[listOfUsers valueForKey:@"name" ] containsObject:[[mainArray objectAtIndex:i] valueForKey:@"name"]])
    {  
       NSLog(@"Same object");
    }
    else
    {
        [listOfUsers addObject:[mainArray objectAtIndex:i]];
    }
}

1

정렬 된 배열이있는 경우 배열의 다른 모든 항목을 확인하지 않아도 마지막 항목 만 확인할 수 있습니다. 모든 항목을 확인하는 것보다 훨씬 빠릅니다.

// sortedSourceArray is the source array, already sorted
NSMutableArray *newArray = [[NSMutableArray alloc] initWithObjects:[sortedSourceArray objectAtIndex:0]];
for (int i = 1; i < [sortedSourceArray count]; i++)
{
    if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])
    {
        [newArray addObject:[tempArray objectAtIndex:i]];
    }
}

등 같습니다 NSOrderedSet또한 제안 답변이 훨씬 덜 코드가 필요하지만, 당신은을 사용할 수없는 경우 NSOrderedSet어떤 이유로, 당신은 정렬 된 배열을 가지고, 내 솔루션은 빠른 것입니다 생각합니다. NSOrderedSet솔루션 의 속도와 어떻게 비교되는지 잘 모르겠습니다 . 또한 내 코드가 확인 isEqualToString:중이므로 동일한 일련의 문자가 두 번 이상 나타나지 않습니다 newArray. NSOrderedSet솔루션이 값이나 메모리 위치를 기반으로 중복을 제거 하는지 확실하지 않습니다 .

내 예는 s, just s 또는 둘의 혼합을 sortedSourceArray포함 한다고 가정합니다 . 대신 s 또는 s 만 포함하는 경우 교체 할 수 있습니다.NSStringNSMutableStringsortedSourceArrayNSNumberNSDate

if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])

if ([[sortedSourceArray objectAtIndex:i] compare:[sortedSourceArray objectAtIndex:(i-1)]] != NSOrderedSame)

완벽하게 작동합니다. s, s 및 / 또는 s sortedSourceArray가 혼합되어 있으면 충돌이 발생했을 수 있습니다.NSStringNSNumberNSDate


1

보다 세련된 솔루션을 제공하는 KVC 객체 연산자가 있습니다. NSArray 범주uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"]; 는 다음과 같습니다 .


1

배열에 객체를 추가하기 전에 중복 값을 추가하지 않는 한 가지 간단한 방법을 시도해보십시오.

// mutableArray가 할당되어 초기화되고 값이 있다고 가정

if (![yourMutableArray containsObject:someValue])
{
   [yourMutableArray addObject:someValue];
}

1

Objective-C의 NSMutableArray에서 중복 값 제거

NSMutableArray *datelistArray = [[NSMutableArray alloc]init];
for (Student * data in fetchStudentDateArray)
{
    if([datelistArray indexOfObject:data.date] == NSNotFound)
    [datelistArray addObject:data.date];
}

0

다음은 NSMutable Array에서 중복 값을 제거하는 코드입니다. 그것은 당신을 위해 일할 것입니다. myArray는 중복 값을 제거하려는 Mutable Array입니다.

for(int j = 0; j < [myMutableArray count]; j++){
    for( k = j+1;k < [myMutableArray count];k++){
    NSString *str1 = [myMutableArray objectAtIndex:j];
    NSString *str2 = [myMutableArray objectAtIndex:k];
    if([str1 isEqualToString:str2])
        [myMutableArray removeObjectAtIndex:k];
    }
 } // Now print your array and will see there is no repeated value

0

사용 Orderedset하면 트릭을 수행합니다. 이렇게하면 배열에서 중복 제거가 유지되고 정상적으로 설정되지 않는 순서가 유지됩니다.


-3

이 간단한 코드를 사용하십시오.

NSArray *hasDuplicates = /* (...) */;
NSArray *noDuplicates = [[NSSet setWithArray: hasDuplicates] allObjects];

nsset은 중복 값을 허용하지 않으며 모든 객체가 배열을 반환하기 때문에


나를 위해 일했다. NSSet이 정렬되지 않은 NSArray를 반환 할 때 NSArray를 다시 정렬하기 만하면됩니다.
lindinax

또는 단순히 NSOrderedSetinsteed of을 사용 하십시오 NSSet.
lindinax
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.