Objective-CNSString
から重複値 ( )を削除する最良の方法は?NSMutableArray
これが最も簡単で正しい方法ですか?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
Objective-CNSString
から重複値 ( )を削除する最良の方法は?NSMutableArray
これが最も簡単で正しい方法ですか?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
NSSet
オブジェクトの順序を気にしない場合は、あなたのアプローチが最適ですが、順序を気にしない場合は、そもそもオブジェクトを に格納しないのはなぜNSSet
ですか?
私は2009年に以下の答えを書きました。2011 年、AppleNSOrderedSet
は 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];
これは古い質問であることは知っていNSArray
ますが、順序を気にしない場合は、重複を削除するよりエレガントな方法があります。
Key Value Coding のオブジェクト演算子を使用すると、次のことができます。
uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
AnthoPakも指摘しているように、プロパティに基づいて重複を削除することが可能です。例は次のとおりです。@distinctUnionOfObjects.name
はい、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];
}
}
これは本質的にジムと同じアプローチですが、オリジナルから重複を削除するのではなく、一意のアイテムを新しい可変配列にコピーします。これにより、多くの重複がある大きな配列の場合にメモリ効率がわずかに向上し (配列全体のコピーを作成する必要はありません)、私の意見では、もう少し読みやすくなります。
いずれの場合も、項目がターゲット配列に既に含まれているかどうかを確認する (containsObject:
私の例またはindexOfObject:inRange:
Jim の例を使用) ことは、大きな配列に対してうまくスケーリングされないことに注意してください。これらのチェックは O(N) 時間で実行されます。つまり、元の配列のサイズを 2 倍にすると、各チェックの実行に 2 倍の時間がかかります。配列内の各オブジェクトのチェックを行っているため、これらのより高価なチェックをさらに実行することになります。全体的なアルゴリズム (私と Jim の両方) は O(N 2 ) 時間で実行され、元の配列が大きくなるにつれてすぐにコストがかかります。
NSMutableSet
NSSet ルックアップは 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:
(実装方法がよくわからないため、彼は「おそらく」と言っています。しかし、考えられる実装の 1 つは、インデックス X のオブジェクトを削除した後、メソッドがインデックス X+1 から配列の最後のオブジェクトまでのすべての要素をループすることです。 、それらを前のインデックスに移動します。その場合、それは実際に O(N) パフォーマンスです。)
じゃあ何をすればいいの?状況によります。大規模なアレイがあり、少数の重複のみが予想される場合は、インプレース重複除外がうまく機能し、重複アレイを構築する必要がなくなります。多数の重複が予想される配列がある場合は、重複除去された別個の配列を構築することがおそらく最善の方法です。ここで重要なのは、Big-O 表記法はアルゴリズムの特性のみを説明するものであり、特定の状況に最適な方法を明確に示すものではないということです。
iOS 5 以降 (iOS の世界全体をカバーするもの) をターゲットにしている場合は、 を使用することをお勧めしますNSOrderedSet
。重複を削除し、の順序を保持しますNSArray
。
やるだけ
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
一意の NSArray に戻すことができるようになりました
NSArray *uniqueArray = orderedSet.array;
または、単に NSArray のような同じメソッドを持っているので、orderedSet を使用objectAtIndex:
しfirstObject
ます。
のメンバーシップ チェックcontains
はNSOrderedSet
、NSArray
詳細については、NSOrderedSet リファレンスを参照してください。
注文が必要
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);
ここで、重複する名前の値を 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]];
}
}
より洗練されたソリューションを提供する KVC Object Operator があります。uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
これがNSArray カテゴリです。
配列にオブジェクトを追加する前に重複する値を追加しない、もう 1 つの簡単な方法を試すことができます。
// mutableArray が割り当てられて初期化され、何らかの値が含まれていると仮定します
if (![yourMutableArray containsObject:someValue])
{
[yourMutableArray addObject:someValue];
}
を使用Orderedset
するとうまくいきます。これにより、配列からの重複の削除が維持され、セットが通常行わない順序が維持されます
NSMutable 配列から重複値を削除するコードを次に示します。.それはあなたのために働くでしょう。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
この単純なコードを使用するだけです:
NSArray *hasDuplicates = /* (...) */;
NSArray *noDuplicates = [[NSSet setWithArray: hasDuplicates] allObjects];
nsset は値の重複を許可せず、すべてのオブジェクトが配列を返すため