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];
これは古い質問ですが、順序を気にしない場合は、NSArray
の重複を削除するよりエレガントな方法があります。
Key Value CodingのObject Operators を使用すると、次のようになります。
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];
}
}
基本的に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(N2)removeObjectAtIndex:
はおそらくO(N)時間で実行されるためです。
(彼は「おそらく」と言っていますが、それがどのように実装されているのかわからないためです。しかし、可能な実装の1つは、インデックスXでオブジェクトを削除した後、メソッドがインデックスX + 1から最後まですべての要素をループすることです配列内のオブジェクトを前のインデックスに移動します。その場合は、実際にO(N)パフォーマンスです。)
じゃあ何をすればいいの?それは状況次第です。大きな配列があり、少数の重複しか期待していない場合、インプレース重複排除はうまく機能し、重複配列を構築する必要がなくなります。多数の重複が予想されるアレイがある場合は、おそらく重複排除された別のアレイを構築するのが最善の方法です。ここで重要なことは、big-O表記はアルゴリズムの特性のみを説明するものであり、特定の状況に最適なものを明確に示すものではないということです。
IOS 5以降(iOSの世界全体をカバーするもの)をターゲットにしている場合は、NSOrderedSet
を使用するのが最適です。重複を削除し、NSArray
の順序を保持します。
ただやる
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
一意のNSArrayに戻すことができるようになりました
NSArray *uniqueArray = orderedSet.array;
または、objectAtIndex:
、firstObject
などのNSArrayのような同じメソッドを持っているため、orderedSetを使用します。
contains
を使用したメンバーシップチェックは、NSOrderedSet
を使用する場合よりも、NSArray
を使用した場合の方が高速です。
詳細なチェックアウトについては、 NSOrderedSet Reference
OS X v10.7以降で使用できます。
注文が気になる場合、正しい方法
NSArray *no = [[NSOrderedSet orderedSetWithArray:originalArray]allObjects];
NSArrayから重複した値を順番に削除するコードを次に示します。
注文が必要
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オブジェクトオペレーターがありますuniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
ここに NSArrayカテゴリ があります。
配列にオブジェクトを追加する前に重複する値を追加しない、試すことができるもう1つの簡単な方法:
// mutableArrayが割り当てられて初期化され、何らかの値が含まれていると仮定する
if (![yourMutableArray containsObject:someValue])
{
[yourMutableArray addObject:someValue];
}
ソートされた配列がある場合、配列内の他のすべての項目をチェックする必要はなく、最後の項目だけをチェックする必要があることに注意してください。これは、すべてのアイテムをチェックするよりもはるかに高速です。
// 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
にはNSString
sのみ、_NSMutableString
sのみ、または2つの組み合わせが含まれていると想定しています。代わりにsortedSourceArray
にNSNumber
sまたはNSDate
sのみが含まれる場合は、置き換えることができます
if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])
と
if ([[sortedSourceArray objectAtIndex:i] compare:[sortedSourceArray objectAtIndex:(i-1)]] != NSOrderedSame)
そしてそれは完全に動作するはずです。 sortedSourceArray
にNSString
s、NSNumber
s、および/またはNSDate
sが混在している場合、おそらくクラッシュします。
Orderedset
を使用すると、トリックを実行できます。これは、配列から重複を削除し、通常は設定しない順序を維持します
Objective-CのNSMutableArrayから重複する値を削除します
NSMutableArray *datelistArray = [[NSMutableArray alloc]init];
for (Student * data in fetchStudentDateArray)
{
if([datelistArray indexOfObject:data.date] == NSNotFound)
[datelistArray addObject:data.date];
}
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