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


147

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

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

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

5
まったく同じオブジェクトへの参照を排除するか、または異なるオブジェクトであるがすべてのフィールドに同じ値を持つオブジェクトへの参照を排除するかを明確にしたい場合があります。
Amagrammer 2009年

配列のコピーを作成せずにこれを行う方法はありませんか?
hfossli 2014

この方法は十分に簡単で、おそらく最良の方法です。しかし、たとえば、私の場合は機能しません-配列の項目は完全な重複ではなく、1つのプロパティで比較する必要があります。
Vyachaslav Gerchicov 2016年

これを一度お試しください。.stackoverflow.com/a/38007095/3908884
Doshi

回答:


242

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];

53
一意性と順序が必要な場合は、次のように使用し[NSOrderedSet orderedSetWithArray:array];ます。最初に、代わりにをarray = [orderedSet allObjects];使用して配列を取得するか、NSOrderedSetsを使用しますNSArray
Regexident 2013年

10
@Regexidentのソリューションが理想的です。ただ、交換する必要がある[orderedSet allObjects][orderedSet array]
2013

ナイスワン;)私は多くの変更をせずに開発者にコピーと貼り付けをさせる答えが好きです、これはすべてのiOS開発者が好む答えです;)@ abo3atef
Abo3atef

ありがとうございますが、例を修正する必要があります。理由-通常、NSArraytempを作成し、作成する必要がありますNSMutableArray。あなたの例ではあなたはその逆で働いています
Vyachaslav Gerchicov

重複を削除するのに最適なビューがどれかを知っている人は、この方法(を使用NSSet)または@Simon Whitaker リンクが重複値を追加する前に防ぐので、効率的な方法ですか?
Mathi Arasan

78

これは古い質問であることは承知NSArray していますが、注文を気にしない場合は、で重複を削除するよりエレガントな方法があります

キー値コーディングのオブジェクト演算子を使用すると、次のようになります。

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

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


3
はい、これも私が使うものです!これは非常に強力なアプローチであり、多くのiOS開発者にはわかりません!
Lefteris 2013年

1
これが可能だと知って驚いた。私は多くのiOS開発者がこれを知ることができないと思ったので、この回答を追加することにしました:)
Tiago Almeida

12
これはオブジェクトの順序を維持しません。
ルドルフアダムコビッチ

2
ええ、それは秩序を壊します。
Rostyslav Druzhchenko 2014

@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];
    }
}

これは基本的にジムのアプローチと同じですが、元のアイテムから重複を削除するのではなく、一意のアイテムを新しい可変配列にコピーします。これにより、多くの重複がある大規模な配列の場合にメモリ効率がわずかに向上し(配列全体のコピーを作成する必要がない)、私の意見ではもう少し読みやすくなります。

どちらの場合も、項目が既にターゲット配列に含まれているかどうかを確認する(containsObject:私の例またはindexOfObject:inRange:ジムで使用されている)かどうかは、大規模な配列では適切にスケーリングされないことに注意してください。これらのチェックはO(N)時間で実行されます。つまり、元の配列のサイズを2倍にすると、各チェックの実行に2倍の時間がかかります。配列内の各オブジェクトのチェックを実行しているため、これらのより高価なチェックの多くを実行することになります。全体的なアルゴリズム(鉱山とジムの両方)はO(N 2)時間で実行され、元の配列が大きくなるとすぐにコストが高くなります。

NSMutableSetNSSetルックアップは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表記はアルゴリズムの特性のみを説明するものであり、特定の状況に最適な方法を明確に示すものではありません。


20

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

するだけ

NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];

これを変換して一意のNSArrayに戻すことができます

NSArray *uniqueArray = orderedSet.array;

それはのようなNSArrayのように同じメソッドを持っているためか、単にorderedSetを使用しobjectAtIndex:firstObjectそしてそうで。

でのメンバーシップチェックcontainsNSOrderedSetNSArray

詳細については、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:ため、同じ一連の文字がで複数回表示されることはありませんnewArrayNSOrderedSetソリューションが値に基づいて、またはメモリの場所に基づいて重複を削除するかどうかはわかりません。

私の例では、sのみ、sのみ、または2つの組み合わせがsortedSourceArray含まれていると想定しています。場合だけではなく、含まれている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オブジェクトオペレーターがありますuniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];これがNSArrayカテゴリです。


1

配列にオブジェクトを追加する前に重複する値を追加しないことを試すことができるもう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配列から重複する値を削除するコードを次に示します。。それはあなたのために働くでしょう。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

を使用Orderedsetしてトリックを行います。これにより、配列から重複が削除され、通常は行われない順序が維持されます


-3

この単純なコードを使用してください:

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

nssetは重複した値を許可せず、すべてのオブジェクトが配列を返すため


私のために働いた。NSSetはソートされていないNSArrayを返すため、NSArrayを再度ソートするだけで済みます。
lindinax 2014年

または単にNSOrderedSetinsteed ofを使用しますNSSet
lindinax 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.