Il modo migliore per rimuovere valori duplicati ( NSString
) da NSMutableArray
in Objective-C?
È questo il modo più semplice e giusto per farlo?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
Il modo migliore per rimuovere valori duplicati ( NSString
) da NSMutableArray
in Objective-C?
È questo il modo più semplice e giusto per farlo?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
Risposte:
Il tuo NSSet
approccio è il migliore se non sei preoccupato per l'ordine degli oggetti, ma di nuovo, se non sei preoccupato per l'ordine, allora perché non li memorizzi in un NSSet
inizio?
Ho scritto la risposta qui sotto nel 2009; nel 2011, Apple ha aggiunto NSOrderedSet
iOS 5 e Mac OS X 10.7. Quello che era stato un algoritmo è ora due righe di codice:
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
Se sei preoccupato per l'ordine e stai eseguendo su iOS 4 o versioni precedenti, passa in rassegna una copia dell'array:
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];
Puoi quindi recuperare un array tramite array = [orderedSet allObjects];
o semplicemente usare NSOrderedSet
s invece che NSArray
in primo luogo.
[orderedSet allObjects]
con [orderedSet array]
!
NSArray
e dovremmo creare temp NSMutableArray
. Nel tuo esempio lavori viceversa
NSSet
) o il link @Simon Whitaker impedire prima di aggiungere un valore duplicato che è un modo efficace?
So che questa è una vecchia domanda, ma c'è un modo più elegante per rimuovere i duplicati in un NSArray
se non ti interessa l'ordine .
Se utilizziamo gli operatori oggetto dalla codifica dei valori chiave , possiamo fare questo:
uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
Come notato anche AnthoPak , è possibile rimuovere i duplicati in base a una proprietà. Un esempio potrebbe essere:@distinctUnionOfObjects.name
@distinctUnionOfObjects.property
rimuovere i duplicati per proprietà di una matrice di oggetti personalizzati. Ad esempio@distinctUnionOfObjects.name
Sì, l'utilizzo di NSSet è un approccio sensato.
Per aggiungere alla risposta di Jim Puls, ecco un approccio alternativo per rimuovere i duplicati mantenendo l'ordine:
// Initialise a new, empty mutable array
NSMutableArray *unique = [NSMutableArray array];
for (id obj in originalArray) {
if (![unique containsObject:obj]) {
[unique addObject:obj];
}
}
È essenzialmente lo stesso approccio di Jim, ma copia elementi unici in un nuovo array mutabile piuttosto che eliminare duplicati dall'originale. Ciò rende leggermente più efficiente la memoria nel caso di un array di grandi dimensioni con molti duplicati (non è necessario eseguire una copia dell'intero array) ed è, a mio avviso, un po 'più leggibile.
Si noti che in entrambi i casi, verificando se un elemento è già incluso nell'array di destinazione (usando containsObject:
nel mio esempio o indexOfObject:inRange:
in quello di Jim) non si adatta bene per array di grandi dimensioni. Tali controlli vengono eseguiti nel tempo O (N), il che significa che se si raddoppia la dimensione dell'array originale, ciascun controllo richiederà il doppio del tempo per l'esecuzione. Dal momento che stai eseguendo il controllo per ogni oggetto dell'array, eseguirai anche più di quei controlli più costosi. L'algoritmo generale (sia mio che di Jim) viene eseguito nel tempo O (N 2 ), che diventa costoso rapidamente man mano che l'array originale cresce.
Per arrivare a O (N) tempo è possibile utilizzare a NSMutableSet
per memorizzare un record di elementi già aggiunti al nuovo array, poiché le ricerche NSSet sono O (1) anziché O (N). In altre parole, verificare se un elemento è un membro di un NSSet richiede lo stesso tempo, indipendentemente dal numero di elementi nel set.
Il codice che utilizza questo approccio sarebbe simile al seguente:
NSMutableArray *unique = [NSMutableArray array];
NSMutableSet *seen = [NSMutableSet set];
for (id obj in originalArray) {
if (![seen containsObject:obj]) {
[unique addObject:obj];
[seen addObject:obj];
}
}
Questo sembra comunque un po 'dispendioso; stiamo ancora generando un nuovo array quando la domanda ha chiarito che l'array originale è mutabile, quindi dovremmo essere in grado di de-duplicarlo sul posto e risparmiare un po 'di memoria. Qualcosa come questo:
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++;
}
}
AGGIORNAMENTO : Yuri Niyazov ha sottolineato che la mia ultima risposta in realtà viene eseguita in O (N 2 ) perché removeObjectAtIndex:
probabilmente viene eseguita in O (N).
(Dice "probabilmente" perché non sappiamo con certezza come sia implementato, ma una possibile implementazione è che dopo aver eliminato l'oggetto all'indice X il metodo quindi scorre attraverso ogni elemento dall'indice X + 1 all'ultimo oggetto dell'array , spostandoli all'indice precedente. In tal caso, si tratta effettivamente delle prestazioni O (N).)
Quindi che si fa? Dipende dalla situazione. Se hai un array di grandi dimensioni e ti aspetti solo un numero limitato di duplicati, la de-duplicazione sul posto funzionerà perfettamente e ti farà risparmiare la creazione di un array duplicato. Se hai un array in cui ti aspetti molti duplicati, probabilmente costruire un array separato e non duplicato è probabilmente l'approccio migliore. Il take-away qui è che la notazione big-O descrive solo le caratteristiche di un algoritmo, non ti dirà definitivamente quale sia la migliore per una data circostanza.
Se scegli come target iOS 5+ (ciò che copre l'intero mondo iOS), è meglio utilizzarlo NSOrderedSet
. Rimuove i duplicati e mantiene l'ordine del tuo NSArray
.
Basta fare
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
Ora puoi riconvertirlo in un NSArray univoco
NSArray *uniqueArray = orderedSet.array;
O semplicemente utilizzare l'orderedSet perché ha gli stessi metodi come un NSArray come objectAtIndex:
, firstObject
e così via.
Un controllo di appartenenza con contains
è ancora più veloce NSOrderedSet
di quanto sarebbe su unNSArray
Per ulteriori informazioni, consultare il riferimento NSOrderedSet
Disponibile in OS X v10.7 e versioni successive.
Se sei preoccupato per l'ordine, il modo giusto per farlo
NSArray *no = [[NSOrderedSet orderedSetWithArray:originalArray]allObjects];
Ecco il codice per rimuovere i valori duplicati da NSArray in Order.
bisogno di ordine
NSArray *yourarray = @[@"a",@"b",@"c"];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourarray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
NSLog(@"%@",arrayWithoutDuplicates);
o non è necessario l'ordine
NSSet *set = [NSSet setWithArray:yourarray];
NSArray *arrayWithoutOrder = [set allObjects];
NSLog(@"%@",arrayWithoutOrder);
Qui ho rimosso i valori dei nomi duplicati da mainArray e archiviare i risultati in 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]];
}
}
Si noti che se si dispone di un array ordinato, non è necessario verificare con ogni altro elemento dell'array, solo l'ultimo elemento. Questo dovrebbe essere molto più veloce del confronto con tutti gli oggetti.
// 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]];
}
}
Sembra che le NSOrderedSet
risposte suggerite richiedano anche molto meno codice, ma se non riesci a usarne uno NSOrderedSet
per qualche motivo e hai un array ordinato, credo che la mia soluzione sarebbe la più veloce. Non sono sicuro di come si confronta con la velocità delle NSOrderedSet
soluzioni. Nota anche che il mio codice sta verificando isEqualToString:
, quindi la stessa serie di lettere non apparirà più di una volta newArray
. Non sono sicuro se le NSOrderedSet
soluzioni rimuoveranno i duplicati in base al valore o in base alla posizione della memoria.
Il mio esempio presuppone che sortedSourceArray
contenga solo NSString
s, solo NSMutableString
s o un mix dei due. Se sortedSourceArray
invece contiene solo NSNumber
s o solo NSDate
s, puoi sostituirlo
if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])
con
if ([[sortedSourceArray objectAtIndex:i] compare:[sortedSourceArray objectAtIndex:(i-1)]] != NSOrderedSame)
e dovrebbe funzionare perfettamente. Se sortedSourceArray
contiene un mix di NSString
s, NSNumber
s e / o NSDate
s, probabilmente si bloccherà.
C'è un operatore oggetti KVC che offre una soluzione più elegante uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
Ecco una categoria NSArray .
Un altro modo semplice per provare che non aggiungerà valore duplicato prima di aggiungere un oggetto nella matrice: -
// Supponiamo che mutableArray sia allocato e inizializzato e contenga un valore
if (![yourMutableArray containsObject:someValue])
{
[yourMutableArray addObject:someValue];
}
Rimuovere i valori duplicati da NSMutableArray in Objective-C
NSMutableArray *datelistArray = [[NSMutableArray alloc]init];
for (Student * data in fetchStudentDateArray)
{
if([datelistArray indexOfObject:data.date] == NSNotFound)
[datelistArray addObject:data.date];
}
Ecco il codice per rimuovere i valori duplicati dall'array NSMutable. .it funzionerà per te. myArray è il tuo array mutabile che desideri rimuovere i valori duplicati.
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
usa questo semplice codice:
NSArray *hasDuplicates = /* (...) */;
NSArray *noDuplicates = [[NSSet setWithArray: hasDuplicates] allObjects];
poiché nsset non consente valori duplicati e tutti gli oggetti restituiscono un array
NSOrderedSet
rospo NSSet
.