Objective-C NSMutableArray yinelenen değerleri kaldırmak için en iyi yolu?


147

Objective-C yinelenen değerleri ( NSString) kaldırmak için en iyi yolu NSMutableArray?

Bunu yapmanın en kolay ve doğru yolu mu?

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

5
Aynı nesneye yapılan referansları mı yoksa farklı nesneler olan ancak her alan için aynı değerlere sahip olanları mı ortadan kaldırmak istediğinizi açıklığa kavuşturmak isteyebilirsiniz.
Amagrammer

Dizinin herhangi bir kopyasını oluşturmadan bunu yapmanın bir yolu yok mu?
hfossli

Bu şekilde yeterince kolay ve belki de en iyisi. Ama örneğin benim durumum için işe yaramaz - dizi öğeleri tam kopya değildir ve bir özellik ile karşılaştırılmalıdır.
Vyachaslav Gerchicov

Yanıtlar:


242

Kişisel NSSetsonra tekrar nesnelerin sırasına hakkında endişeli, ama değilseniz sırayla endişe değilseniz yaklaşım o zaman neden onları saklamak değil, en iyisi NSSetile başlamalı?

Aşağıdaki cevabı 2009'da yazdım; 2011'de Apple NSOrderedSet, iOS 5 ve Mac OS X 10.7'ye eklendi. Bir algoritma olan şey şimdi iki kod satırıdır:

NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
NSArray *arrayWithoutDuplicates = [orderedSet array];

Siparişle ilgili endişeleriniz varsa ve iOS 4 veya daha eski bir sürümde çalışıyorsanız, dizinin bir kopyasının üzerine gelin:

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
Benzersiz ve siparişe ihtiyacınız varsa, sadece kullanın [NSOrderedSet orderedSetWithArray:array];Bir diziyi geri alabilir array = [orderedSet allObjects];veya ilk etapta NSOrderedSets yerine kullanabilirsiniz NSArray.
Regexident

10
@ Regexident'in çözümü idealdir. Sadece değiştirmeniz gerekiyor [orderedSet allObjects]ile [orderedSet array]!
inket

Nice One;) Geliştiriciyi çok fazla değişiklik yapmadan kopyalayıp yapıştırma yapan yanıtı beğendim, bu her iOS geliştiricisinin beğeneceği cevaptır;) @ abo3atef
Abo3atef

Teşekkürler ama size örnek düzeltmelisiniz. Sebep - genellikle NSArraytemp oluşturduk ve yaratmalıyız NSMutableArray.
Örneğinizde,

Herkes yinelenen kaldırmak için en iyi görünüm olduğunu bilmek bu yöntem (kullanarak NSSet) veya @Simon Whitaker bağlantı verimli yolu olan yinelenen değer eklemeden önce önlemek mi?
Mathi Arasan

78

Bunun eski bir soru olduğunu biliyorum, ancak NSArray siparişi umursamıyorsanız kopyaları kaldırmanın daha zarif bir yolu var .

Anahtar Değer Kodlamasından Nesne İşleçleri kullanırsak bunu yapabiliriz:

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

As AnthoPak da belirtildiği bir özelliğine dayanarak çiftleri kaldırmak mümkündür. Örnek olarak şunlar verilebilir:@distinctUnionOfObjects.name


3
Evet, ben de bunu kullanıyorum! Bu, birçok iOS geliştiricisinin bilmediği çok güçlü bir yaklaşımdır!
Lefteris

1
Bunun mümkün olabileceğini öğrendiğimde şaşırdım. Birçok iOS geliştiricisinin bunu bilemeyeceğini düşündüm, bu yüzden bu yanıtı eklemeye karar verdim :)
Tiago Almeida

12
Bu, nesnelerin sırasını korumaz.
Rudolf Adamkovič

2
Evet, düzeni bozuyor.
Rostyslav Druzhchenko

@distinctUnionOfObjects.propertyBir dizi özel nesnenin özelliğine göre yinelenenleri kaldırmak gibi de kullanılabileceğini unutmayın . Örneğin@distinctUnionOfObjects.name
AnthoPak 28:18

47

Evet, NSSet kullanmak mantıklı bir yaklaşımdır.

Jim Puls'un cevabına eklemek için, sırayı korurken kopyaları çıkarmaya alternatif bir yaklaşım var:

// Initialise a new, empty mutable array 
NSMutableArray *unique = [NSMutableArray array];

for (id obj in originalArray) {
    if (![unique containsObject:obj]) {
        [unique addObject:obj];
    }
}

Aslında Jim'inkiyle aynı yaklaşımdır, ancak orijinali kopyaları silmek yerine benzersiz öğeleri yeni bir değişken diziye kopyalar. Bu, çoğaltılmış çok sayıda (tüm dizinin bir kopyasını oluşturmaya gerek yok) büyük bir dizi durumunda biraz daha bellek verimli hale getirir ve bence biraz daha okunabilir.

Her iki durumda da, bir öğenin hedef diziye zaten dahil edilip edilmediğini kontrol etmenin ( örneğimde containsObject:veya indexOfObject:inRange:Jim'inde) büyük diziler için iyi ölçeklenmediğini unutmayın. Bu kontroller O (N) zamanında çalışır, yani orijinal dizinin boyutunu iki katına çıkarırsanız, her kontrolün çalışması iki kat daha uzun sürer. Dizideki her bir nesne için denetim yaptığınızdan, bu daha pahalı denetimlerin çoğunu da çalıştırırsınız. Genel algoritma (hem benim hem de Jim'in) O (N 2 ) zamanında çalışır ve orijinal dizi büyüdükçe hızlı bir şekilde pahalı olur.

Bunu O (N) zamanına düşürmek için NSMutableSet, yeni diziye zaten eklenmiş olan öğelerin kaydını saklamak için a kullanabilirsiniz , çünkü NSSet aramaları O (N) yerine O (1) şeklindedir. Başka bir deyişle, bir öğenin bir NSSet'in üyesi olup olmadığını kontrol etmek, kümede kaç öğe bulunduğundan bağımsız olarak aynı zamanı alır.

Bu yaklaşımı kullanan kod şöyle görünecektir:

NSMutableArray *unique = [NSMutableArray array];
NSMutableSet *seen = [NSMutableSet set];

for (id obj in originalArray) {
    if (![seen containsObject:obj]) {
        [unique addObject:obj];
        [seen addObject:obj];
    }
}

Bu yine de biraz savurgan görünüyor; orijinal dizinin değiştirilebilir olduğunu açıkça belirttikten sonra hala yeni bir dizi oluşturuyoruz, bu yüzden yerinde de-dupe ve biraz bellek tasarrufu yapabilmeliyiz. Bunun gibi bir şey:

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++;
    }
}

GÜNCELLEME : Yuri Niyazov , son cevabımın aslında O (N 2 ) 'de, çünkü muhtemelen O (N) zamanda çalıştığına dikkat çekti .removeObjectAtIndex:

("Muhtemelen" diyor çünkü nasıl uygulandığından emin değiliz; ancak olası bir uygulama, X dizinindeki nesneyi sildikten sonra yöntemin X + 1 dizininden dizideki son nesneye kadar her öğeye geçiş yapmasıdır. bunları bir önceki dizine taşıyın. Durum buysa, aslında O (N) performansıdır.)

Peki ne yapmalı? Bu duruma bağlıdır. Büyük bir diziniz varsa ve yalnızca az sayıda kopya bekliyorsanız, yerinde çoğaltma işlemi iyi çalışır ve yinelenen bir dizi oluşturmak zorunda kalmanızı sağlar. Eğer çok sayıda kopya beklediğiniz bir dizi varsa, o zaman ayrı, de-duped dizi oluşturmak muhtemelen en iyi yaklaşımdır. Buradaki devralma, big-O gösteriminin sadece bir algoritmanın özelliklerini tanımlamasıdır, size herhangi bir koşul için en iyisi olduğunu kesin olarak söylemez.


20

İOS 5+ 'ı (tüm iOS dünyasını kapsayan şey) hedefliyorsanız, en iyi şekilde kullanın NSOrderedSet. Yinelemeleri kaldırır ve sırasını korur NSArray.

Sadece yap

NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];

Şimdi tekrar benzersiz bir NSArray'e dönüştürebilirsiniz

NSArray *uniqueArray = orderedSet.array;

Bunun gibi bir NSArray gibi aynı yöntemleri vardır çünkü Ya da sadece orderedSet kullanmak objectAtIndex:, firstObjectvb vb.

A üyelik onay containsdaha hızlı olduğunu NSOrderedSetbir olurdu dahaNSArray

Daha fazla ödeme için NSOrderedSet Başvurusu


Bu benim oyumu aldı, hepsini okudum ve en iyi cevap bu. Üst cevabın manuel bir döngü olduğuna inanamıyorum. Ah şimdi bu cevabı kopyaladılar.
malhal

19

OS Xv10.7 ve sonraki sürümlerinde kullanılabilir.

Sipariş hakkında endişeleniyorsanız, doğru yol

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

Sıralı olarak NSArray'den yinelenen değerleri kaldırmanın kodu.


1
allObjects dizi olmalıdır
malhal

7

sipariş gerekli

NSArray *yourarray = @[@"a",@"b",@"c"];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourarray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
NSLog(@"%@",arrayWithoutDuplicates);

veya siparişe gerek yok

NSSet *set = [NSSet setWithArray:yourarray];
NSArray *arrayWithoutOrder = [set allObjects];
NSLog(@"%@",arrayWithoutOrder);

3

Burada mainArray ve mağaza sonucu NSMutableArray (listOfUsers) yinelenen ad değerlerini kaldırdım

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

Sıralanmış bir diziniz varsa, dizideki diğer tüm öğeleri, yalnızca son öğeyi kontrol etmeniz gerekmediğini unutmayın. Bu, tüm öğelere karşı kontrol etmekten çok daha hızlı olmalıdır.

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

NSOrderedSetAyrıca önerilen cevaplar çok daha az kod gerektirir gibi görünüyor , ama bir NSOrderedSetnedenle kullanamazsanız ve sıralı bir dizi varsa, benim çözüm en hızlı olacağını düşünüyorum. NSOrderedSetÇözümlerin hızı ile nasıl karşılaştırıldığından emin değilim . Kodumun kontrol ettiğini de unutmayın isEqualToString:, bu nedenle aynı harf dizisi birden fazla kez görünmeyecektir newArray. NSOrderedSetÇözümlerin, değere veya bellek konumuna göre yinelenenleri kaldıracağından emin değilim .

Örneğim sortedSourceArraysadece NSStrings, sadece NSMutableStrings veya ikisinin bir karışımını içerdiğini varsayar . Eğer sortedSourceArrayyerine içeriyor sadece NSNumbers ya da sadece NSDate, biliyor yerine

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

ile

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

ve mükemmel çalışmalıdır. Eğer sortedSourceArraybir karışımını içerir NSStringler, NSNumberlar, ve / veya NSDates, muhtemelen çökmesine.


1

Daha zarif bir çözüm sunan bir KVC Nesne Operatörü var uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];İşte bir NSArray kategorisi .


1

Diziye nesne eklemeden önce yinelenen Değer eklemeyeceğinizi deneyebileceğiniz daha basit bir yol: -

// mutableArray öğesinin ayrıldığını ve başlattığını ve bir değer içerdiğini varsayın

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

1

Objective-C NSMutableArray yinelenen değerleri kaldırma

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

0

NSMutable Array öğesinden yinelenen değerleri kaldırmanın kodu. sizin için çalışacaktır. myArray, yinelenen değerleri kaldırmak istediğiniz Değişken Dizinizdir.

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

Kullanmak Orderedsethile yapacak. Bu, yinelenenleri diziden koruyacak ve normalde ayarlanmayan kümeleri koruyacaktır


-3

sadece bu basit kodu kullanın:

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

nsset yinelenen değerlere izin vermediğinden ve tüm nesneler bir dizi döndürdüğü için


Benim için çalıştı. Tek yapmanız gereken NSSS'nizi sıralanmamış bir NSArray döndürdüğü için NSArray'ınızı tekrar sıralamaktır.
Lindinax

Ya da sadece NSOrderedSetinsteed kullanın NSSet.
Lindinax
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.