¿La mejor manera de eliminar valores duplicados ( NSString
) NSMutableArray
en Objective-C?
¿Es esta la forma más fácil y correcta de hacerlo?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
¿La mejor manera de eliminar valores duplicados ( NSString
) NSMutableArray
en Objective-C?
¿Es esta la forma más fácil y correcta de hacerlo?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
Respuestas:
Su NSSet
enfoque es el mejor si no está preocupado por el orden de los objetos, pero, de nuevo, si no está preocupado por el orden, ¿por qué no los está almacenando NSSet
para empezar?
Escribí la respuesta a continuación en 2009; en 2011, Apple agregó NSOrderedSet
a iOS 5 y Mac OS X 10.7. Lo que había sido un algoritmo ahora son dos líneas de código:
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
Si le preocupa el pedido y está ejecutando iOS 4 o anterior, repita una copia de la matriz:
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];
[NSOrderedSet orderedSetWithArray:array];
Puede recuperar una matriz a través de array = [orderedSet allObjects];
o simplemente usar NSOrderedSet
s en lugar de NSArray
en primer lugar.
[orderedSet allObjects]
con [orderedSet array]
!
NSArray
y debemos crear temp NSMutableArray
. En su ejemplo, usted trabaja al revés
NSSet
) o @Simon Whitaker enlace evitan antes de añadir valor duplicado que es la forma eficiente?
Sé que esta es una vieja pregunta, pero hay una forma más elegante de eliminar duplicados en un NSArray
si no te importa el pedido .
Si usamos operadores de objetos de la codificación de valor clave , podemos hacer esto:
uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
Como también señaló AnthoPak , es posible eliminar duplicados en función de una propiedad. Un ejemplo sería:@distinctUnionOfObjects.name
@distinctUnionOfObjects.property
para eliminar duplicados por propiedad de una matriz de objetos personalizados. Por ejemplo@distinctUnionOfObjects.name
Sí, usar NSSet es un enfoque sensato.
Para agregar a la respuesta de Jim Puls, aquí hay un enfoque alternativo para eliminar los duplicados mientras se mantiene el orden:
// Initialise a new, empty mutable array
NSMutableArray *unique = [NSMutableArray array];
for (id obj in originalArray) {
if (![unique containsObject:obj]) {
[unique addObject:obj];
}
}
Es esencialmente el mismo enfoque que el de Jim, pero copia elementos únicos en una nueva matriz mutable en lugar de eliminar duplicados del original. Esto hace que sea un poco más eficiente en memoria en el caso de una gran matriz con muchos duplicados (no es necesario hacer una copia de toda la matriz), y en mi opinión es un poco más legible.
Tenga en cuenta que, en cualquier caso, verificar si un elemento ya está incluido en la matriz de destino (usando containsObject:
en mi ejemplo, o indexOfObject:inRange:
en el de Jim) no escala bien para grandes matrices. Esas comprobaciones se ejecutan en tiempo O (N), lo que significa que si duplica el tamaño de la matriz original, cada comprobación tardará el doble de tiempo en ejecutarse. Como está haciendo la verificación de cada objeto en la matriz, también ejecutará más de esas verificaciones más costosas. El algoritmo general (tanto el mío como el de Jim) se ejecuta en tiempo O (N 2 ), que se vuelve caro rápidamente a medida que crece la matriz original.
Para reducir el tiempo a O (N), puede usar a NSMutableSet
para almacenar un registro de elementos ya agregados a la nueva matriz, ya que las búsquedas de NSSet son O (1) en lugar de O (N). En otras palabras, verificar si un elemento es miembro de un NSSet toma el mismo tiempo, independientemente de cuántos elementos haya en el conjunto.
El código que usa este enfoque se vería así:
NSMutableArray *unique = [NSMutableArray array];
NSMutableSet *seen = [NSMutableSet set];
for (id obj in originalArray) {
if (![seen containsObject:obj]) {
[unique addObject:obj];
[seen addObject:obj];
}
}
Sin embargo, esto todavía parece un poco derrochador; todavía estamos generando una nueva matriz cuando la pregunta dejó en claro que la matriz original es mutable, por lo que deberíamos poder eliminarla en su lugar y ahorrar algo de memoria. Algo como esto:
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++;
}
}
ACTUALIZACIÓN : Yuri Niyazov señaló que mi última respuesta en realidad se ejecuta en O (N 2 ) porque removeObjectAtIndex:
probablemente se ejecuta en tiempo O (N).
(Dice "probablemente" porque no sabemos con certeza cómo se implementa; pero una posible implementación es que después de eliminar el objeto en el índice X, el método recorre cada elemento desde el índice X + 1 hasta el último objeto en la matriz , moviéndolos al índice anterior. Si ese es el caso, entonces ese es el rendimiento O (N).)
¿Entonces lo que hay que hacer? Depende de la situación. Si tiene una matriz grande y solo espera una pequeña cantidad de duplicados, la desduplicación en el lugar funcionará bien y le ahorrará tener que construir una matriz duplicada. Si tiene una matriz donde espera muchos duplicados, entonces construir una matriz separada y duplicada es probablemente el mejor enfoque. La conclusión aquí es que la notación big-O solo describe las características de un algoritmo, no le dirá definitivamente cuál es el mejor para cualquier circunstancia.
Si está apuntando a iOS 5+ (lo que cubre todo el mundo de iOS), mejor uso NSOrderedSet
. Elimina duplicados y conserva el orden de su NSArray
.
Solo haz
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
Ahora puede volver a convertirlo en una matriz NSA única
NSArray *uniqueArray = orderedSet.array;
O simplemente use el setSet ordenado porque tiene los mismos métodos como un NSArray objectAtIndex:
, firstObject
y así sucesivamente.
Una verificación de membresía contains
es aún más rápida en lo NSOrderedSet
que sería en unNSArray
Para obtener más información, consulte la referencia NSOrderedSet
Disponible en OS X v10.7 y posterior.
Si está preocupado por el pedido, la forma correcta de hacerlo
NSArray *no = [[NSOrderedSet orderedSetWithArray:originalArray]allObjects];
Aquí está el código para eliminar valores duplicados de NSArray en orden.
necesita orden
NSArray *yourarray = @[@"a",@"b",@"c"];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourarray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
NSLog(@"%@",arrayWithoutDuplicates);
o no necesita orden
NSSet *set = [NSSet setWithArray:yourarray];
NSArray *arrayWithoutOrder = [set allObjects];
NSLog(@"%@",arrayWithoutOrder);
Aquí eliminé valores de nombre duplicados de mainArray y almacené el resultado en 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]];
}
}
Tenga en cuenta que si tiene una matriz ordenada, no necesita verificar con cada otro elemento de la matriz, solo con el último elemento. Esto debería ser mucho más rápido que verificar todos los artículos.
// 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]];
}
}
Parece que las NSOrderedSet
respuestas que también se sugieren requieren mucho menos código, pero si NSOrderedSet
por alguna razón no puede usar una y tiene una matriz ordenada, creo que mi solución sería la más rápida. No estoy seguro de cómo se compara con la velocidad de las NSOrderedSet
soluciones. También tenga en cuenta que mi código está comprobando isEqualToString:
, por lo que la misma serie de letras no aparecerá más de una vez newArray
. No estoy seguro de si las NSOrderedSet
soluciones eliminarán duplicados según el valor o la ubicación de la memoria.
Mi ejemplo supone que sortedSourceArray
contiene solo NSString
s, solo NSMutableString
s o una mezcla de los dos. Si en su sortedSourceArray
lugar contiene solo NSNumber
s o solo NSDate
s, puede reemplazar
if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])
con
if ([[sortedSourceArray objectAtIndex:i] compare:[sortedSourceArray objectAtIndex:(i-1)]] != NSOrderedSame)
y debería funcionar perfectamente. Si sortedSourceArray
contiene una mezcla de NSString
s, NSNumber
sy / o NSDate
s, probablemente se bloqueará.
Hay un operador de objetos KVC que ofrece una solución más elegante. uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
Aquí hay una categoría NSArray .
Una forma más simple de probar que no agregará valor duplicado antes de agregar un objeto en la matriz: -
// Supongamos que mutableArray está asignado e inicializado y contiene algún valor
if (![yourMutableArray containsObject:someValue])
{
[yourMutableArray addObject:someValue];
}
Eliminar valores duplicados de NSMutableArray en Objective-C
NSMutableArray *datelistArray = [[NSMutableArray alloc]init];
for (Student * data in fetchStudentDateArray)
{
if([datelistArray indexOfObject:data.date] == NSNotFound)
[datelistArray addObject:data.date];
}
Aquí está el código para eliminar valores duplicados de NSMutable Array. Funcionará para usted. myArray es su matriz mutable que desea eliminar valores duplicados.
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
solo usa este código simple:
NSArray *hasDuplicates = /* (...) */;
NSArray *noDuplicates = [[NSSet setWithArray: hasDuplicates] allObjects];
dado que nsset no permite valores duplicados y todos los objetos devuelven una matriz
NSOrderedSet
lugar de NSSet
.