web-dev-qa-db-ja.com

Objective-CのNSMutableArrayから重複値を削除する最良の方法は?

Objective-CのNSStringから重複する値(NSMutableArray)を削除する最良の方法は?

これが最も簡単で正しい方法ですか?

uniquearray = [[NSSet setWithArray:yourarray] allObjects];
144
Teo Choong Ping

NSSetアプローチは、オブジェクトの順序が気にならない場合に最適ですが、再度、順序が気にならない場合は、なぜNSSetそもそも?

2009年に以下の回答を書きました。 2011年、AppleはNSOrderedSetをiOS 5およびMac OS X 10.7に追加しました。アルゴリズムであったものは、現在2行のコードです。

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];
237
Jim Puls

これは古い質問ですが、順序を気にしない場合は、NSArrayの重複を削除するよりエレガントな方法があります

Key Value CodingのObject Operators を使用すると、次のようになります。

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

AnthoPak も指摘したように、プロパティに基づいて重複を削除することが可能です。例は次のとおりです。@distinctUnionOfObjects.name

77
Tiago Almeida

はい、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:、またはJimのindexOfObject:inRange:を使用して)、大きな配列にはうまくスケーリングしないことに注意してください。これらのチェックはO(N)時間で実行されます。つまり、元の配列のサイズを2倍にすると各チェックの実行に2倍の時間がかかります。配列内の各オブジェクトのチェックを実行しているため、これらのより高価なチェックをさらに実行することになります。全体的なアルゴリズム(私のものとジムの両方)はO(N2)、元の配列が大きくなるとすぐに高価になります。

NSSetルックアップはO(N)ではなくO(1)時間であるため、NSMutableSetを使用して、新しい配列に既に追加されているアイテムのレコードを保存できます。オン)。つまり、要素が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++;
    }
}

UPDATE:Yuri Niyazov 指摘 私の最後の回答は実際にO(N2removeObjectAtIndex:はおそらくO(N)時間で実行されるためです。

(彼は「おそらく」と言っていますが、それがどのように実装されているのかわからないためです。しかし、可能な実装の1つは、インデックスXでオブジェクトを削除した後、メソッドがインデックスX + 1から最後まですべての要素をループすることです配列内のオブジェクトを前のインデックスに移動します。その場合は、実際にO(N)パフォーマンスです。)

じゃあ何をすればいいの?それは状況次第です。大きな配列があり、少数の重複しか期待していない場合、インプレース重複排除はうまく機能し、重複配列を構築する必要がなくなります。多数の重複が予想されるアレイがある場合は、おそらく重複排除された別のアレイを構築するのが最善の方法です。ここで重要なことは、big-O表記はアルゴリズムの特性のみを説明するものであり、特定の状況に最適なものを明確に示すものではないということです。

47
Simon Whitaker

IOS 5以降(iOSの世界全体をカバーするもの)をターゲットにしている場合は、NSOrderedSetを使用するのが最適です。重複を削除し、NSArrayの順序を保持します。

ただやる

NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];

一意のNSArrayに戻すことができるようになりました

NSArray *uniqueArray = orderedSet.array;

または、objectAtIndex:firstObjectなどのNSArrayのような同じメソッドを持っているため、orderedSetを使用します。

containsを使用したメンバーシップチェックは、NSOrderedSetを使用する場合よりも、NSArrayを使用した場合の方が高速です。

詳細なチェックアウトについては、 NSOrderedSet Reference

19
lukaswelte

OS X v10.7以降で使用できます。

注文が気になる場合、正しい方法

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

NSArrayから重複した値を順番に削除するコードを次に示します。

19
Sultania

注文が必要

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

ここで、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]];
    }
}
3
Bibin Joseph

より洗練されたソリューションを提供するKVCオブジェクトオペレーターがありますuniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];ここに NSArrayカテゴリ があります。

1
Peter

配列にオブジェクトを追加する前に重複する値を追加しない、試すことができるもう1つの簡単な方法:

// mutableArrayが割り当てられて初期化され、何らかの値が含まれていると仮定する

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

ソートされた配列がある場合、配列内の他のすべての項目をチェックする必要はなく、最後の項目だけをチェックする必要があることに注意してください。これは、すべてのアイテムをチェックするよりもはるかに高速です。

// 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ソリューションが値またはメモリの場所に基づいて重複を削除するかどうかはわかりません。

私の例では、sortedSourceArrayにはNSStringsのみ、_NSMutableStringsのみ、または2つの組み合わせが含まれていると想定しています。代わりにsortedSourceArrayNSNumbersまたはNSDatesのみが含まれる場合は、置き換えることができます

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

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

そしてそれは完全に動作するはずです。 sortedSourceArrayNSStrings、NSNumbers、および/またはNSDatesが混在している場合、おそらくクラッシュします。

1
GeneralMike

Orderedsetを使用すると、トリックを実行できます。これは、配列から重複を削除し、通常は設定しない順序を維持します

0
abhi

Objective-CのNSMutableArrayから重複する値を削除します

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

NSMutableArrayから重複値を削除するコードを示します。。itが機能します。 myArrayは、重複する値を削除する可変配列です。

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
IHSAN KHAN