Objective-C NSString
から重複する値()を削除する最良の方法はNSMutableArray
?
これは最も簡単で正しい方法ですか?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
Objective-C NSString
から重複する値()を削除する最良の方法はNSMutableArray
?
これは最も簡単で正しい方法ですか?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
回答:
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];
[NSOrderedSet orderedSetWithArray:array];
ます。最初に、代わりにをarray = [orderedSet allObjects];
使用して配列を取得するか、NSOrderedSet
sを使用しますNSArray
。
[orderedSet allObjects]
と[orderedSet array]
!
NSArray
tempを作成し、作成する必要がありますNSMutableArray
。あなたの例ではあなたはその逆で働いています
NSSet
)または@Simon Whitaker リンクが重複値を追加する前に防ぐので、効率的な方法ですか?
これは古い質問であることは承知NSArray
していますが、注文を気にしない場合は、で重複を削除するよりエレガントな方法があります。
キー値コーディングのオブジェクト演算子を使用すると、次のようになります。
uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
以下のようAnthoPakはまた指摘し、プロパティに基づいて重複を削除することが可能です。例は次のとおりです。@distinctUnionOfObjects.name
@distinctUnionOfObjects.property
カスタムオブジェクトの配列のプロパティによって重複を削除する場合などにも使用できます。例@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:
ジムで使用されている)かどうかは、大規模な配列では適切にスケーリングされないことに注意してください。これらのチェックはO(N)時間で実行されます。つまり、元の配列のサイズを2倍にすると、各チェックの実行に2倍の時間がかかります。配列内の各オブジェクトのチェックを実行しているため、これらのより高価なチェックの多くを実行することになります。全体的なアルゴリズム(鉱山とジムの両方)はO(N 2)時間で実行され、元の配列が大きくなるとすぐにコストが高くなります。
NSMutableSet
NSSetルックアップはO(N)ではなくO(1)であるため、これをO(N)時間に下げるには、を使用して、新しい配列にすでに追加されている項目のレコードを格納します。つまり、要素が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]];
}
}
並べ替えられた配列がある場合は、配列内の他のすべての項目をチェックする必要はなく、最後の項目のみをチェックする必要があることに注意してください。これは、すべてのアイテムをチェックするよりもはるかに高速です。
// 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のみ、sのみ、または2つの組み合わせがsortedSourceArray
含まれていると想定しています。場合だけではなく、含まれているSまたはちょうどよ、あなたは置き換えることができますNSString
NSMutableString
sortedSourceArray
NSNumber
NSDate
if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])
と
if ([[sortedSourceArray objectAtIndex:i] compare:[sortedSourceArray objectAtIndex:(i-1)]] != NSOrderedSame)
そしてそれは完全に動作するはずです。s、s、sのsortedSourceArray
組み合わせが含まれている場合、おそらくクラッシュします。NSString
NSNumber
NSDate
よりエレガントなソリューションを提供するKVCオブジェクトオペレーターがあります。uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
これがNSArrayカテゴリです。
配列にオブジェクトを追加する前に重複する値を追加しないことを試すことができるもう1つの簡単な方法:-
// mutableArrayが割り当てられて初期化され、いくつかの値が含まれていると仮定します
if (![yourMutableArray containsObject:someValue])
{
[yourMutableArray addObject:someValue];
}
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は重複した値を許可せず、すべてのオブジェクトが配列を返すため
NSOrderedSet
insteed ofを使用しますNSSet
。