La meilleure façon de supprimer les valeurs en double de NSMutableArray dans Objective-C?

La meilleure façon de supprimer les valeurs en double (NSString) de NSMutableArray dans Objective-C?

Est-ce la façon la plus simple et la plus juste de le faire?

uniquearray = [[NSSet setWithArray:yourarray] allObjects];
141
demandé sur Bhavin Ramani 2009-06-22 10:11:04

14 réponses

Votre approche NSSet est la meilleure si vous n'êtes pas inquiet de l'ordre des objets, mais encore une fois, si vous n'êtes pas inquiet de l'ordre, alors pourquoi ne les stockez-vous pas dans un NSSet pour commencer?

J'ai écrit la réponse ci-dessous en 2009; en 2011, Apple a ajouté NSOrderedSet à iOS 5 et Mac OS X 10.7. Ce qui avait été un algorithme est maintenant deux lignes de code:

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

Si vous êtes inquiet au sujet de la commande et que vous exécutez sur iOS 4 ou plus tôt, faites une boucle sur une copie de la tableau:

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
répondu Jim Puls 2015-04-07 15:59:16

Je sais que c'est une vieille question, mais il existe un moyen plus élégant de supprimer les doublons dans un NSArray Si vous ne vous souciez pas de la commande .

Si nous utilisons opérateurs D'objets à partir du codage de valeur clé nous pouvons faire ceci:

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

, Comme AnthoPak a également noté qu'il est possible de supprimer les doublons en fonction d'une propriété. Un exemple serait: @distinctUnionOfObjects.name

77
répondu Tiago Almeida 2018-03-28 10:19:29

Oui, l'utilisation de NSSet est une approche sensée.

Pour ajouter à la réponse de Jim Puls, voici une approche alternative pour supprimer les doublons tout en conservant l'ordre:

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

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

C'est essentiellement la même approche que celle de Jim, mais copie les éléments uniques dans un nouveau tableau mutable plutôt que de supprimer les doublons de l'original. Cela rend légèrement plus efficace la mémoire dans le cas d'un grand tableau avec beaucoup de doublons (pas besoin de faire une copie de l'ensemble du tableau), et est à mon avis Un peu plus lisible.

Notez que dans les deux cas, vérifier si un élément est déjà inclus dans le tableau cible (en utilisant containsObject: dans mon exemple, ou indexOfObject:inRange: dans Jim) ne s'adapte pas bien pour les grands tableaux. Ces vérifications s'exécutent en temps O (N), ce qui signifie que si vous doublez la taille du tableau d'origine, alors chaque vérification prendra deux fois plus de temps à exécuter. Puisque vous effectuez la vérification pour chaque objet du tableau, vous exécuterez également plus de ces vérifications plus coûteuses. Global algorithme (à la fois le mien et Jim) fonctionne dans O (N2) temps, qui devient cher rapidement que le tableau d'origine se développe.

Pour obtenir cela Jusqu'à O (N) temps, vous pouvez utiliser un NSMutableSet pour stocker un enregistrement des éléments déjà ajoutés au nouveau tableau, puisque les recherches NSSet sont O (1) plutôt que O (N). En d'autres termes, vérifier si un élément est membre d'un NSSet prend le même temps quel que soit le nombre d'éléments dans l'ensemble.

Le Code utilisant cette approche ressemblerait à quelque chose comme ce:

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

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

Cela semble encore un peu inutile; nous générons toujours un nouveau tableau lorsque la question indique clairement que le tableau d'origine est mutable, donc nous devrions être en mesure de le dé-Duper en place et d'économiser de la mémoire. Quelque chose comme ceci:

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 a souligné que ma dernière réponse fonctionne réellement dans O (N2) parce que removeObjectAtIndex: fonctionne probablement en temps O (N).

(Il dit "probablement" parce que nous ne savons pas pour bien sûr, comment il est implémenté; mais une implémentation possible est qu'après avoir supprimé l'objet à l'index X, la méthode passe ensuite à travers chaque élément de l'index X + 1 au dernier objet du tableau, en les déplaçant vers l'index précédent. Si c'est le cas, c'est en effet la performance O(N).)

Alors, Que faire? Cela dépend de la situation. Si vous avez un grand tableau et que vous n'attendez qu'un petit nombre de doublons, la déduplication sur place fonctionnera très bien et économisera vous devez construire un tableau en double. Si vous avez un tableau où vous attendez beaucoup de doublons, la création d'un tableau séparé et dupliqué est probablement la meilleure approche. Le take-away ici est que la notation big-O ne décrit que les caractéristiques d'un algorithme, elle ne vous dira pas définitivement ce qui est le mieux pour une circonstance donnée.

46
répondu Simon Whitaker 2012-10-06 08:41:02

Disponible dans OS X v10. 7 et versions ultérieures.

Si vous êtes inquiet au sujet de la commande, bonne façon de faire

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

Voici le code de suppression des valeurs de doublons de NSArray dans l'ordre.

19
répondu Sultania 2013-05-14 08:53:31

Si vous ciblez iOS 5+ (ce qui couvre l'ensemble du Monde iOS), utilisez au mieux NSOrderedSet. Il supprime les doublons et conserve l'ordre de votre NSArray.

Faites juste

NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];

Vous pouvez maintenant le convertir en un NSArray unique

NSArray *uniqueArray = orderedSet.array;

Ou utilisez simplement orderedSet car il a les mêmes méthodes comme un NSArray comme objectAtIndex:, firstObject et ainsi de suite.

Une adhésion à vérifier avec contains est encore plus rapide sur le NSOrderedSet qu'il serait sur un NSArray

Pour plus de checkout le NSOrderedSet Référence

19
répondu lukaswelte 2013-06-19 09:24:38

Besoin d'ordre

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

Ou n'ont pas besoin d'ordre

NSSet *set = [NSSet setWithArray:yourarray];
NSArray *arrayWithoutOrder = [set allObjects];
NSLog(@"%@",arrayWithoutOrder);
6
répondu Mike 2016-07-12 03:57:59

Ici, j'ai supprimé les valeurs de nom en double de mainArray et stocké le résultat dans 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
répondu Bibin Joseph 2016-05-12 12:58:29

Notez que si vous avez un tableau trié, vous n'avez pas besoin de vérifier tous les autres éléments du tableau, juste le dernier élément. Cela devrait être beaucoup plus rapide que de vérifier contre tous les éléments.

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

Il semble que les réponses NSOrderedSet qui sont également suggérées nécessitent beaucoup moins de code, mais si vous ne pouvez pas utiliser un NSOrderedSet pour une raison quelconque, et que vous avez un tableau trié, je crois que ma solution serait la plus rapide. Je ne sais pas comment cela se compare à la vitesse des solutions NSOrderedSet. Notez également que mon le code vérifie avec isEqualToString:, donc la même série de lettres n'apparaîtra pas plus d'une fois dans newArray. Je ne suis pas sûr si les solutions NSOrderedSet supprimeront les doublons en fonction de la valeur ou de l'emplacement de la mémoire.

Mon exemple suppose que sortedSourceArray contient NSStrings, juste NSMutableStrings, ou un mélange des deux. Si sortedSourceArray contient à la place juste NSNumber S ou juste NSDate s, vous pouvez remplacer

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

Avec

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

Et cela devrait fonctionner parfaitement. If sortedSourceArray contient un mélange de NSStrings, NSNumbers, et / ou NSDate s, il va probablement planter.

1
répondu GeneralMike 2013-08-14 14:04:08

Il y a un opérateur D'objet KVC qui offre une solution plus élégante uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"]; Voici une catégorie NSArray .

1
répondu Peter 2015-03-20 06:30:06

Un moyen plus simple que vous pouvez essayer qui n'ajoutera pas de valeur en double avant d'ajouter un objet dans le tableau: -

/ / supposons que mutableArray est alloué et initialise et contient une certaine valeur

if (![yourMutableArray containsObject:someValue])
{
   [yourMutableArray addObject:someValue];
}
1
répondu Hussain Shabbir 2015-05-14 16:27:15

Voici le code de suppression des valeurs de doublons du tableau NSMutable. . cela fonctionnera pour vous. myArray est votre tableau Mutable que vous souhaitez supprimer les valeurs de doublons..

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
répondu IHSAN KHAN 2012-05-15 10:52:38

Utiliser Orderedset fera l'affaire. Cela gardera les doublons de suppression du tableau et maintiendra l'ordre qui définit normalement ne fait pas

0
répondu abhi 2016-06-08 17:42:07

Supprime les valeurs dupliquées de NSMutableArray dans Objective-C

NSMutableArray *datelistArray = [[NSMutableArray alloc]init];
for (Student * data in fetchStudentDateArray)
{
    if([datelistArray indexOfObject:data.date] == NSNotFound)
    [datelistArray addObject:data.date];
}
0
répondu Arvind Patel 2018-05-23 13:52:52

Il suffit d'utiliser ce code simple:

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

Puisque nsset n'autorise pas les valeurs en double et que tous les objets renvoient un tableau

-3
répondu Dinesh619 2012-06-10 22:12:54