web-dev-qa-db-fra.com

Le meilleur moyen 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 manière la plus simple et la meilleure de le faire?

uniquearray = [[NSSet setWithArray:yourarray] allObjects];
144
Teo Choong Ping

Votre approche NSSet est la meilleure solution si vous n'êtes pas inquiet pour l'ordre des objets, mais encore une fois, si vous n'êtes pas inquiet pour l'ordre, 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. Un algorithme s'appelle désormais deux lignes de code:

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

Si la commande vous inquiète et que vous utilisez iOS 4 ou une version antérieure, passez en boucle sur une copie du 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];
237
Jim Puls

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'objet du codage de valeur de clé , nous pouvons le faire:

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

Comme AnthoPak a également noté, il est possible de supprimer les doublons basés sur une propriété. Un exemple serait: @distinctUnionOfObjects.name

77
Tiago Almeida

Oui, utiliser NSSet est une approche judicieuse.

Pour ajouter à la réponse de Jim Puls, voici une approche alternative pour éliminer 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 des éléments uniques dans un nouveau tableau modifiable plutôt que de supprimer les doublons de l'original. Cela le rend légèrement plus efficace en termes de mémoire dans le cas d'un tableau de grande taille avec beaucoup de doublons (inutile de copier le tableau en entier), 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's) ne convient pas aux grands tableaux. Ces contrôles sont exécutés dans le temps O(N)), ce qui signifie que si vous doublez la taille du tableau d'origine, alors , chaque contrôle prendra deux fois plus longtemps. Puisque vous effectuez la vérification de chaque objet du tableau, vous exécuterez également davantage de vérifications plus coûteuses. L’algorithme général (le mien et celui de Jim) s’exécute en O (N).2), ce qui coûte cher rapidement à mesure que le tableau d'origine se développe.

Pour réduire cela à O(N) fois, vous pouvez utiliser un NSMutableSet pour stocker un enregistrement des éléments déjà ajoutés au nouveau tableau, car 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 ensemble NSSet prend le même temps, quel que soit le nombre d'éléments contenus dans l'ensemble.

Le code utilisant cette approche ressemblerait à ceci:

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 cependant; nous générons toujours un nouveau tableau lorsque la question précise que le tableau d'origine est modifiable, nous devrions donc pouvoir le dédoubler sur place et économiser de la mémoire. Quelque chose comme ça:

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

[~ # ~] mise à jour [~ # ~] : Yuri Niyazov a souligné que ma dernière réponse est en fait en O ( N2) car removeObjectAtIndex: _ fonctionne probablement dans le temps O(N).

(Il dit "probablement" car nous ne savons pas avec certitude comment il est implémenté; toutefois, une implémentation possible est qu'après avoir supprimé l'objet situé à l'index X, la méthode parcourt ensuite tous les éléments de l'index X + 1 vers le dernier objet du tableau, en les déplaçant vers l’index précédent. Si tel est le cas, c’est bien O(N) performance.)

Alors que faire? Ça dépend de la situation. Si vous avez un grand tableau et que vous attendez seulement un petit nombre de doublons, la déduplication sur place fonctionnera parfaitement et vous évitera de devoir créer un tableau en double. Si vous avez un tableau dans lequel vous attendez beaucoup de doublons, la meilleure approche consiste probablement à créer un tableau séparé et dupliqué. Le point à retenir ici est que la notation big-O ne décrit que les caractéristiques d'un algorithme, elle ne vous dira pas de manière définitive quelle est la meilleure solution dans une situation donnée.

47
Simon Whitaker

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

Il suffit de faire

NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];

Vous pouvez maintenant le reconvertir en un NSArray unique.

NSArray *uniqueArray = orderedSet.array;

Ou utilisez simplement CommandedSet car il utilise les mêmes méthodes que celles d'un NSArray comme objectAtIndex:, firstObject et ainsi de suite.

Une vérification d'adhésion avec contains est encore plus rapide sur le NSOrderedSet que sur un NSArray

Pour plus d’achat, NSOrderedSet Reference

19
lukaswelte

Disponible sous OS X 10.7 et ultérieur.

Si vous êtes inquiet à propos de l'ordre, bonne façon de le faire

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

Voici le code de suppression des valeurs en double de NSArray dans Order.

19
Sultania

besoin d'ordre

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

ou n'a pas besoin d'ordre

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

Ici, j'ai supprimé les valeurs de nom en double de mainArray et stocker 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
Bibin Joseph

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

1
Peter

Une méthode plus simple que vous pouvez essayer qui n’ajoutera pas de valeur en double avant d’ajouter un objet dans un tableau: -

// Suppose que mutableArray est alloué et initialise et contient une valeur

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

Notez que si vous avez un tableau trié, vous n'avez pas besoin de vérifier tous les autres éléments du tableau, mais uniquement le dernier élément. Cela devrait être beaucoup plus rapide que de vérifier 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 le plus rapide. Je ne suis pas sûr de savoir comment cela se compare à la vitesse des solutions NSOrderedSet. Notez également que mon code vérifie avec isEqualToString:, ainsi la même série de lettres n'apparaîtra pas plus d'une fois dans newArray. Je ne sais pas si la solution NSOrderedSet supprimera les doublons en fonction de la valeur ou de l'emplacement de la mémoire.

Mon exemple suppose que sortedSourceArray ne contient que NSStrings, que NSMutableStrings ou un mélange des deux. Si sortedSourceArray contient plutôt que NSNumbers ou que NSDates, 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. Si sortedSourceArray contient un mélange de NSStrings, NSNumbers et/ou NSDates, il va probablement planter.

1
GeneralMike

Utiliser Orderedset fera l'affaire. Cela gardera les doublons supprimés du tableau et maintiendra l’ordre qui ne fonctionne normalement pas

0
abhi

Supprimer les valeurs en double 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
Arvind Patel

Voici le code de suppression des valeurs en double de NSMutableArray.. Cela fonctionnera pour vous. myArray est votre tableau Mutable que vous souhaitez supprimer des valeurs en double.

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
IHSAN KHAN