2010-11-17 13 views

Respuesta

49

Mi idea inicial fue que podrías hacer:

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

Pero eso no mantiene el orden. Por lo tanto, hay que hacerlo de forma manual:

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

utilizo un NSMutableSet para determinar si ya he encontrado con esta entrada antes (en contraposición a [unique containsObject:string], ya que un conjunto tendrá O (1) tiempo de búsqueda, y una matriz tiene O (n) tiempo de búsqueda. Si solo está tratando con una pequeña cantidad de objetos, entonces esto no importará. Sin embargo, si la matriz de origen es muy grande, entonces usar el conjunto para determinar la singularidad puede agregar un poco de un impulso de velocidad. (Sin embargo, se debe utilizar instrumentos para perfilar el código y ver si es necesario)

+0

Realmente no es lo más intuitivo que @distinctUnionOfObjects arruina el orden (las implementaciones simplemente parecen hacer el antiguo setWithArray:/allObjects), ya que toma y devuelve una matriz. –

+0

Gracias por esta publicación. Me pareció muy útil, sin embargo, también descubrí que si solo estás tratando de cargar una segunda matriz de la primera matriz sin duplicados, entonces no necesitas NSMutableSet. simplemente puede usar el bucle for y la instrucción if para verificar cada objeto y agregarlo si es necesario. Muy útil. Gracias – Miek

0

Hmm .. usted podría utilizar un bucle?

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

+1 inteligente usando la última cadena conocida para comparación –

+0

gracias ... depende si la matriz original está realmente ordenada de esa manera ... si algo como ("uno", "uno", "dos", "uno") , "tres") es posible ya que la matriz original no funcionará. – Bastian

+0

Um, pero eso solo funciona si los elementos idénticos están siempre adyacentes en la matriz, ¿verdad? En este caso, esto parece ser cierto, pero quién sabe ... –

46

Se podría hacer como esto:

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

De esta manera, también preservar el orden!

+3

correcto, pero debería ser NSArray * uniqueArray = [[NSOrderedSet orderedSetWithArray: duplicatesArray] allObjects] – thewormsterror

6

Creo que se puede hacer esto con que

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

espero que esto le ayudaría a

+1

+1: Esto es lo que estaba buscando. Sin embargo, esto no cumple con los requisitos del asker ya que esto no mantendrá el orden de la matriz original ... (1) '[Yourarray valueForKeyPath: @" @ distinctUnionOfObjects.self "]' no respeta el orden y (2) 'sortedArrayUsingSelector: @selector (caseInsensitiveCompare:)' obviamente ordena la matriz ... Si está buscando una manera de "filtrar duplicados y ordenar alfabéticamente la matriz final", este es el indicado para usted. :RE –

0

He aquí una buena categoría que define un custom operator como @distinctUnionOfObjects, excepto que sólo funciona con cadenas y se mantendrá su orden original. Nota: No ordena las cadenas por ti. Deja intacta solo la primera instancia de cualquier cadena que se repita.

Uso:

#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"]; 

Salida:

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

archivo .h: archivo

#import <Foundation/Foundation.h> 

@interface NSArray (orderedDistinctUnionOfStrings) 

@end 

.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 

Y aquí hay una excelente lectura sobre cómo generar sus propios operadores, y desmitifica (por un momento) cómo funciona esto: http://bou.io/KVCCustomOperators.html

Cuestiones relacionadas