2010-11-17 16 views
26

Si vous avez un NSArray de chaînesComment transformer un tableau de chaînes NSArray en un tableau de chaînes uniques, dans le même ordre?

{ @"ONE", @"ONE", @"ONE", "TWO", @"THREE", @"THREE" } 

Comment puis-je transformer en

{ @"ONE", @"TWO", @"THREE" } 

..where le tableau suit le même ordre que l'original. Je pense que vous pouvez transformer un tableau en un NSSet pour obtenir des éléments uniques, mais si vous le retournez dans un tableau, vous n'êtes pas sûr d'obtenir le même ordre ..

Répondre

49

Ma première pensée était que vous pourriez faire:

NSArray * a = [NSArray arrayWithObjects:@"ONE", @"ONE", @"ONE", @"TWO", @"THREE", @"THREE", nil]; 
NSLog(@"%@", [a valueForKeyPath:@"@distinctUnionOfObjects.self"]); 

Mais cela ne maintient pas l'ordre. Par conséquent, vous devez le faire manuellement:

NSArray * a = [NSArray arrayWithObjects:@"ONE", @"ONE", @"ONE", @"TWO", @"THREE", @"THREE", nil]; 
NSMutableArray * unique = [NSMutableArray array]; 
NSMutableSet * processed = [NSMutableSet set]; 
for (NSString * string in a) { 
    if ([processed containsObject:string] == NO) { 
    [unique addObject:string]; 
    [processed addObject:string]; 
    } 
} 

J'utilise un NSMutableSet pour déterminer si je suis déjà venu à travers cette entrée avant (par opposition à [unique containsObject:string], car un ensemble aura O (1) rechercher le temps, et un tableau a O (n) temps de recherche.Si vous ne traitez qu'un petit nombre d'objets, cela n'aura aucune importance.Cependant, si le tableau source est très grand, alors utiliser l'ensemble pour déterminer l'unicité peut ajouter un peu d'un coup de pouce de vitesse. (toutefois, vous devez utiliser des instruments pour profiler votre code et voir si elle est nécessaire)

+0

Vraiment pas la chose la plus intuitive que @distinctUnionOfObjects brouille l'ordre (les implémentations semblent juste faire l'ancien setWithArray:/allObjects), car il prend et retourne un tableau. –

+0

Merci pour ce post. Je l'ai trouvé très utile, cependant, j'ai également découvert que si vous essayez simplement de charger un deuxième tableau du premier tableau sans doublons, vous n'avez pas besoin de NSMutableSet. vous pouvez simplement utiliser la boucle for et l'instruction if pour vérifier chaque objet et l'ajouter si nécessaire. Très utile. Merci – Miek

0

Hmm .. vous pouvez simplement utiliser une boucle?

NSMutableArray *newarray = [[NSMutableArray alloc] init]; 
NSString *laststring = nil; 
for (NSString *currentstring in oldarray) 
{ 
    if (![currentstring isEqualtoString:laststring]) [newarray addObject:currentstring]; 
    laststring = currentstring 
} 
+0

+ 1 intelligent en utilisant la dernière chaîne connue pour la comparaison –

+0

merci .. cela dépend si le tableau d'origine est vraiment ordonné de cette façon ... si quelque chose comme ("un", "un", "deux", "un" , "trois") est possible car le tableau original ne fonctionnera pas. – Bastian

+0

Um, mais cela ne fonctionne que si les éléments identiques sont toujours adjacents dans le tableau, non? Dans ce cas, cela semble être vrai, mais qui sait ... –

46

Vous pouvez faire comme ceci:

NSArray * uniqueArray = [[NSOrderedSet orderedSetWithArray:duplicatesArray] array]; 

De cette façon, vous préservez aussi l'ordre!

+3

c'est vrai mais il devrait être NSArray * uniqueArray = [[NSOrderedSet orderedSetWithArray: duplicatesArray] allObjects] – thewormsterror

6

Je pense que vous pouvez le faire avec cette

NSArray * uniqueArray = [[Yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; 

J'espère que cela vous aiderait

+1

+1: C'est ce que je cherchais. Cependant, ceci ne remplit pas les conditions du demandeur car cela ne gardera pas l'ordre du tableau original ... (1) '[Yourarray valueForKeyPath: @" @ distinctUnionOfObjects.self "]' ne respecte pas l'ordre et (2) 'sortedArrayUsingSelector: @selector (caseInsensitiveCompare:)' trie évidemment le tableau ... Si vous cherchez un moyen de "filtrer les doublons et trier alphabétiquement le tableau final", c'est celui qui vous convient. :RÉ –

0

Voici une catégorie agréable qui définit un custom operator comme @distinctUnionOfObjects, sauf qu'il ne fonctionne que sur les chaînes et il maintiendra leur original ordre. Remarque: Il ne triera pas les chaînes pour vous. Il ne laisse intacte que la première instance des chaînes répétées.

Utilisation:

#import "NSArray+orderedDistinctUnionOfStrings.h" 
... 
// if you feed it an array that has already been ordered, it will work as expected 
NSArray *myArray = @[@"ONE", @"ONE", @"ONE", @"TWO", @"THREE", @"THREE"]; 
NSArray *myUniqueArray = [myArray valueForKeyPath:@"@orderedDistinctUnionOfStrings.self"]; 

Sortie:

myUniqueArray = ("ONE", "TWO", "THREE") 

.h:

#import <Foundation/Foundation.h> 

@interface NSArray (orderedDistinctUnionOfStrings) 

@end 

fichier .m:

#import "NSArray+orderedDistinctUnionOfObjects.h" 

@implementation NSArray (orderedDistinctUnionOfObjects) 

- (id) _orderedDistinctUnionOfStringsForKeyPath:(NSString*)keyPath { 
    NSMutableIndexSet *removeIndexes = [NSMutableIndexSet indexSet]; 

    for (NSUInteger i = 0, n = self.count; i < n; ++i) { 
     if ([removeIndexes containsIndex:i]) { 
      continue; 
     } 
     NSString *str1 = [[self objectAtIndex:i] valueForKeyPath:keyPath]; 

     for (NSUInteger j = i+1; j < n; ++j) { 
      if ([removeIndexes containsIndex:j]) { 
       continue; 
      } 
      id obj = [self objectAtIndex:j]; 
      NSString *str2 = [obj valueForKeyPath:keyPath]; 
      if ([str1 isEqualToString:str2]) { 
       [removeIndexes addIndex:j]; 
      } 
     } 
    } 

    NSMutableArray *myMutableCopy = [self mutableCopy]; 
    [myMutableCopy removeObjectsAtIndexes:removeIndexes]; 

    return [[NSArray arrayWithArray:myMutableCopy] valueForKeyPath:[NSString stringWithFormat:@"@unionOfObjects.%@", keyPath]]; 
} 

@end 

Et voici une excellente lecture sur la façon de générer vos propres opérateurs, et démystifie (par un peu) comment cela fonctionne: http://bou.io/KVCCustomOperators.html