Najlepszym sposobem na usunięcie zduplikowanych wartości z NSMutableArray w Objective-C?

Najlepszy sposób na usunięcie zduplikowanych wartości (NSString) z NSMutableArray w Objective-C?

Czy to najłatwiejszy i właściwy sposób?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
Author: Bhavin Ramani, 2009-06-22

14 answers

Twoje NSSet podejście jest najlepsze, jeśli nie martwisz się o kolejność obiektów, ale z drugiej strony, jeśli nie martwisz się o kolejność, to dlaczego nie przechowujesz ich w NSSet na początek?

Napisałem odpowiedź poniżej w 2009 roku; w 2011 roku Apple dodał NSOrderedSet do iOS 5 i Mac OS X 10.7. To, co było algorytmem, to teraz dwie linie kodu:

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

Jeśli martwisz się o zamówienie i używasz systemu iOS 4 lub wcześniejszego, Przełącz na kopię tablica:

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];
 227
Author: Jim Puls,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-07 15:59:16

Wiem, że to stare pytanie, ale jest bardziej elegancki sposób na usunięcie duplikatów w NSArray Jeśli nie zależy ci na zamówieniu .

Jeśli użyjemy operatorów obiektów z kodowania wartości klucza możemy to zrobić:

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

Jak AnthoPak zauważył również, że możliwe jest usuwanie duplikatów na podstawie właściwości. Przykładem może być: @distinctUnionOfObjects.name

 77
Author: Tiago Almeida,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-28 10:19:29

Tak, używanie NSSet jest rozsądnym podejściem.

Aby dodać do odpowiedzi Jima Pulsa, oto alternatywne podejście do usuwania duplikatów przy zachowaniu porządku:

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

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

To zasadniczo to samo podejście co Jima, ale kopiuje unikalne przedmioty do nowej mutowalnej tablicy, zamiast usuwać duplikaty z oryginału. To sprawia, że nieco bardziej wydajna pamięć w przypadku dużej tablicy z dużą ilością duplikatów( nie trzeba robić kopii całej tablicy), i jest moim zdaniem trochę bardziej czytelne.

Zauważ, że w obu przypadkach sprawdzenie, czy Element jest już zawarty w tablicy docelowej (używając containsObject: w moim przykładzie lub indexOfObject:inRange: W Jim ' s) nie jest dobrze skalowane dla dużych tablic. Te kontrole są uruchamiane w czasie O (N), co oznacza, że jeśli podwoisz rozmiar oryginalnej tablicy, to każde sprawdzenie będzie trwało dwa razy dłużej. Ponieważ wykonujesz kontrolę dla każdego obiektu w tablicy, będziesz również uruchamiał więcej tych droższych kontroli. Ogólnie algorytm (zarówno mój, jak i Jima) działa w O (N2) czas, który szybko staje się kosztowny, gdy oryginalna tablica rośnie.

Aby ograniczyć czas O(N), można użyć NSMutableSet do przechowywania rekordu pozycji już dodanych do nowej tablicy, ponieważ wyszukiwanie NSSet to O(1), a nie O (N). Innymi słowy, sprawdzenie, czy element jest członkiem zestawu NSSet zajmuje ten sam czas, niezależnie od tego, ile elementów jest w zestawie.

Kod wykorzystujący to podejście wyglądałby jak to:

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

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

Nadal wydaje się to trochę marnotrawne; wciąż generujemy nową tablicę, gdy pytanie stało się jasne, że oryginalna tablica jest zmienna, więc powinniśmy być w stanie ją odkręcić i zachować trochę pamięci. Coś takiego:

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 Niyazovwskazał że moja ostatnia odpowiedź faktycznie działa w O(N2) ponieważ removeObjectAtIndex: prawdopodobnie działa w czasie O (N).

(mówi "prawdopodobnie", bo nie wiemy na oczywiście, jak to zaimplementowane; ale jedną z możliwych implementacji jest to, że po usunięciu obiektu o indeksie X metoda przecina każdy element od indeksu X+1 do ostatniego obiektu w tablicy, przenosząc go do poprzedniego indeksu. Jeśli tak jest, to jest to rzeczywiście wydajność O (N).)

Więc, co robić? To zależy od sytuacji. Jeśli masz dużą tablicę i oczekujesz tylko niewielkiej liczby duplikatów, de-duplikacja na miejscu będzie działać dobrze i zapisać musisz zbudować duplikat tablicy. Jeśli masz tablicę, w której spodziewasz się wielu duplikatów, zbudowanie oddzielnej, pozbawionej duplikatów tablicy jest prawdopodobnie najlepszym podejściem. W tym przypadku notacja big-O opisuje tylko cechy algorytmu, nie powie Ci definitywnie, który jest najlepszy dla danej sytuacji.
 46
Author: Simon Whitaker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-06 08:41:02

Dostępne w OS X v10. 7 i nowszych.

Jeśli martwisz się o zamówienie, dobrze zrób

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

Oto kod usuwania duplikatów wartości z nsArray w kolejności.

 19
Author: Sultania,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-14 08:53:31

Jeśli używasz iOS 5+ (co obejmuje cały świat iOS), najlepiej użyj NSOrderedSet. Usuwa duplikaty i zachowuje kolejność NSArray.

Po prostu zrób

NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];

Możesz teraz przekonwertować go z powrotem do unikalnego NSArray

NSArray *uniqueArray = orderedSet.array;

Lub po prostu użyj orderedSet, ponieważ ma te same metody, jak NSArray jak objectAtIndex:, firstObject i tak dalej.

Czek członkowski z contains jest jeszcze szybszy na NSOrderedSet niż na NSArray

Aby uzyskać więcej informacji Nsorderedset Reference

 19
Author: lukaswelte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-19 09:24:38

Need order

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

Or don ' t need order

NSSet *set = [NSSet setWithArray:yourarray];
NSArray *arrayWithoutOrder = [set allObjects];
NSLog(@"%@",arrayWithoutOrder);
 6
Author: Mike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-12 03:57:59

Tutaj usunąłem zduplikowane wartości nazw z mainArray i zapisałem wynik w 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]];
    }
}
 3
Author: Bibin Joseph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-12 12:58:29

Zauważ, że jeśli masz posortowaną tablicę, nie musisz sprawdzać każdego innego elementu w tablicy, tylko ostatniego elementu. Powinno to być znacznie szybsze niż sprawdzanie wszystkich przedmiotów.

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

Wygląda na to, że sugerowane odpowiedzi NSOrderedSet wymagają znacznie mniej kodu, ale jeśli z jakiegoś powodu nie możesz użyć NSOrderedSet, a masz uporządkowaną tablicę, uważam, że moje rozwiązanie byłoby najszybsze. Nie jestem pewien, jak to się porównuje z szybkością rozwiązań NSOrderedSet. Zauważ również, że mój kod sprawdza się za pomocą isEqualToString:, więc ta sama seria liter nie pojawi się więcej niż raz w newArray. Nie jestem pewien, czy rozwiązania NSOrderedSet usuną duplikaty na podstawie wartości czy na podstawie lokalizacji pamięci.

Mój przykład zakłada, że sortedSourceArray zawiera tylko NSStrings, tylko NSMutableString s, lub mieszankę tych dwóch. Jeśli sortedSourceArray zamiast tego zawiera tylko NSNumber s lub tylko NSDate s, możesz zastąpić

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

Z

if ([[sortedSourceArray objectAtIndex:i] compare:[sortedSourceArray objectAtIndex:(i-1)]] != NSOrderedSame)
I powinno działać idealnie. Jeśli sortedSourceArray zawiera mieszankę NSStrings, NSNumber s, i / lub NSDateS, prawdopodobnie się rozwali.
 1
Author: GeneralMike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-14 14:04:08

Istnieje Operator obiektu KVC, który oferuje bardziej eleganckie rozwiązanie uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"]; oto Kategoria NSArray .

 1
Author: Peter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-20 06:30:06

Jeszcze jeden prosty sposób, który nie będzie dodawał zduplikowanej wartości przed dodaniem obiektu do tablicy: -

//Załóżmy, że mutableArray jest alokowany i inicjalizowany i zawiera pewną wartość

if (![yourMutableArray containsObject:someValue])
{
   [yourMutableArray addObject:someValue];
}
 1
Author: Hussain Shabbir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-14 16:27:15

Oto kod usuwania duplikatów wartości z tablicy NSMutable. . będzie to działać dla Ciebie. myArray jest zmienną tablicą, którą chcesz usunąć duplikaty wartości..

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
Author: IHSAN KHAN,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-15 10:52:38

Użycie Orderedset załatwi sprawę. Spowoduje to zachowanie usuwa duplikaty z tablicy i utrzyma porządek, który normalnie nie działa

 0
Author: abhi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-08 17:42:07

Usuń zduplikowane wartości z NSMutableArray w Objective-C

NSMutableArray *datelistArray = [[NSMutableArray alloc]init];
for (Student * data in fetchStudentDateArray)
{
    if([datelistArray indexOfObject:data.date] == NSNotFound)
    [datelistArray addObject:data.date];
}
 0
Author: Arvind Patel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-23 13:52:52

Po prostu użyj tego prostego kodu:

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

Ponieważ nsset nie pozwala na duplikowanie wartości i wszystkie obiekty zwracają tablicę

 -3
Author: Dinesh619,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-10 22:12:54