A melhor maneira de remover valores duplicados ( NSString
) do NSMutableArray
Objective-C?
Essa é a maneira mais fácil e correta de fazer isso?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
A melhor maneira de remover valores duplicados ( NSString
) do NSMutableArray
Objective-C?
Essa é a maneira mais fácil e correta de fazer isso?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
Respostas:
Sua NSSet
abordagem é a melhor se você não está preocupado com a ordem dos objetos, mas, novamente, se você não está preocupado com a ordem, por que não está armazenando-os em umNSSet
para começar?
Eu escrevi a resposta abaixo em 2009; em 2011, a Apple adicionou NSOrderedSet
ao iOS 5 e Mac OS X 10.7. O que havia sido um algoritmo agora são duas linhas de código:
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
Se você estiver preocupado com o pedido e estiver executando o iOS 4 ou anterior, faça um loop sobre uma cópia da 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];
Você pode recuperar uma matriz via array = [orderedSet allObjects];
ou apenas usar NSOrderedSet
s em vez de NSArray
em primeiro lugar.
[orderedSet allObjects]
com [orderedSet array]
!
NSArray
e devemos criar temp NSMutableArray
. No seu exemplo você trabalha vice-versa
NSSet
) ou o link @Simon Whitaker impede antes de adicionar valor duplicado, o que é uma maneira eficiente?
Sei que essa é uma pergunta antiga, mas existe uma maneira mais elegante de remover duplicatas em um NSArray
caso você não se importe com o pedido .
Se usarmos Operadores de Objetos da Key Value Coding , podemos fazer o seguinte:
uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
Como a AnthoPak também observou, é possível remover duplicatas com base em uma propriedade. Um exemplo seria:@distinctUnionOfObjects.name
@distinctUnionOfObjects.property
para remover duplicatas por propriedade de uma matriz de objetos personalizados. Por exemplo@distinctUnionOfObjects.name
Sim, o uso do NSSet é uma abordagem sensata.
Para adicionar à resposta de Jim Puls, aqui está uma abordagem alternativa para remover duplicatas, mantendo a ordem:
// Initialise a new, empty mutable array
NSMutableArray *unique = [NSMutableArray array];
for (id obj in originalArray) {
if (![unique containsObject:obj]) {
[unique addObject:obj];
}
}
É essencialmente a mesma abordagem que a de Jim, mas copia itens exclusivos para uma nova matriz mutável, em vez de excluir duplicatas do original. Isso o torna um pouco mais eficiente de memória no caso de uma matriz grande com muitas duplicatas (não é necessário fazer uma cópia de toda a matriz) e, na minha opinião, é um pouco mais legível.
Observe que, em ambos os casos, verificar se um item já está incluído na matriz de destino (usando containsObject:
no meu exemplo ouindexOfObject:inRange:
no de Jim) não é adequado para grandes matrizes. Essas verificações são executadas no tempo O (N), o que significa que, se você dobrar o tamanho da matriz original, cada verificação levará o dobro do tempo para ser executada. Como você está fazendo a verificação de cada objeto na matriz, também estará executando mais dessas verificações mais caras. O algoritmo geral (tanto o meu quanto o de Jim) é executado no tempo O (N 2 ), que fica caro rapidamente à medida que a matriz original cresce.
Para reduzir o tempo O (N), você pode usar um NSMutableSet
para armazenar um registro de itens já adicionados à nova matriz, pois as pesquisas do NSSet são O (1) e não O (N). Em outras palavras, verificar se um elemento é membro de um NSSet leva o mesmo tempo, independentemente de quantos elementos estiverem no conjunto.
Código usando essa abordagem seria algo como isto:
NSMutableArray *unique = [NSMutableArray array];
NSMutableSet *seen = [NSMutableSet set];
for (id obj in originalArray) {
if (![seen containsObject:obj]) {
[unique addObject:obj];
[seen addObject:obj];
}
}
Isso ainda parece um pouco inútil; ainda estamos gerando uma nova matriz quando a pergunta deixou claro que a matriz original é mutável, para que possamos redimensioná-la no local e economizar memória. Algo assim:
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++;
}
}
ATUALIZAÇÃO : Yuri Niyazov apontou que a minha última resposta na verdade é executado em O (N 2 ), porque removeObjectAtIndex:
provavelmente é executado em O (N) tempo.
(Ele diz "provavelmente" porque não sabemos ao certo como é implementado; mas uma implementação possível é que, após excluir o objeto no índice X, o método percorre todos os elementos do índice X + 1 até o último objeto na matriz , movendo-os para o índice anterior. Se for esse o caso, esse é realmente o desempenho de O (N).)
Então o que fazer? Depende da situação. Se você possui uma matriz grande e espera apenas um pequeno número de duplicatas, a desduplicação no local funcionará perfeitamente e poupará a criação de uma matriz duplicada. Se você tem uma matriz em que espera muitas duplicatas, a criação de uma matriz separada e com dupagem é provavelmente a melhor abordagem. A conclusão aqui é que a notação big-O apenas descreve as características de um algoritmo, não lhe dirá definitivamente qual é o melhor para qualquer circunstância.
Se você está segmentando o iOS 5 ou superior (que abrange todo o mundo do iOS), use melhor NSOrderedSet
. Ele remove duplicatas e mantém a ordem do seu NSArray
.
Apenas faça
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
Agora você pode convertê-lo novamente em um NSArray exclusivo
NSArray *uniqueArray = orderedSet.array;
Ou apenas use o ordersSet porque ele possui os mesmos métodos, como um NSArray objectAtIndex:
, firstObject
e assim por diante.
Uma verificação de associação contains
é ainda mais rápida no NSOrderedSet
que seria em umNSArray
Para mais informações, consulte a Referência NSOrderedSet
Disponível no OS X v10.7 e posterior.
Se você está preocupado com o pedido, a maneira certa de fazer
NSArray *no = [[NSOrderedSet orderedSetWithArray:originalArray]allObjects];
Aqui está o código para remover valores duplicados do NSArray no pedido.
precisa de ordem
NSArray *yourarray = @[@"a",@"b",@"c"];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourarray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
NSLog(@"%@",arrayWithoutDuplicates);
ou não precisa de ordem
NSSet *set = [NSSet setWithArray:yourarray];
NSArray *arrayWithoutOrder = [set allObjects];
NSLog(@"%@",arrayWithoutOrder);
Aqui eu removi valores de nome duplicados do mainArray e armazene o resultado no 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]];
}
}
Observe que, se você tiver uma matriz classificada, não precisará verificar todos os outros itens da matriz, apenas o último item. Isso deve ser muito mais rápido do que comparar com todos os itens.
// 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 as NSOrderedSet
respostas sugeridas também exigem muito menos código, mas se você não puder usar um NSOrderedSet
por algum motivo e tiver uma matriz classificada, acredito que minha solução seria a mais rápida. Não tenho certeza de como ele se compara à velocidade das NSOrderedSet
soluções. Observe também que meu código está sendo verificado isEqualToString:
, para que a mesma série de letras não apareça mais de uma vez newArray
. Não tenho certeza se oNSOrderedSet
soluções removerão duplicatas com base no valor ou no local da memória.
Meu exemplo assume que sortedSourceArray
contém apenas NSString
s, apenasNSMutableString
s ou uma mistura dos dois. Se, em sortedSourceArray
vez disso, contiver apenas NSNumber
s ou apenas NSDate
s, você poderá substituir
if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])
com
if ([[sortedSourceArray objectAtIndex:i] compare:[sortedSourceArray objectAtIndex:(i-1)]] != NSOrderedSame)
e deve funcionar perfeitamente. Se sortedSourceArray
contiver uma mistura de NSString
s, se NSNumber
/ ou NSDate
s, provavelmente ocorrerá um erro fatal.
Há um operador de objeto KVC que oferece uma solução mais elegante. uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
Aqui está uma categoria do NSArray .
Mais uma maneira simples de testar, que não adicionará Valor duplicado antes de adicionar objeto na matriz: -
// Suponha que mutableArray esteja alocado e inicialize e contenha algum valor
if (![yourMutableArray containsObject:someValue])
{
[yourMutableArray addObject:someValue];
}
Remover valores duplicados do NSMutableArray no Objective-C
NSMutableArray *datelistArray = [[NSMutableArray alloc]init];
for (Student * data in fetchStudentDateArray)
{
if([datelistArray indexOfObject:data.date] == NSNotFound)
[datelistArray addObject:data.date];
}
Aqui está o código para remover valores duplicados do NSMutable Array. . funcionará para você. myArray é sua matriz mutável que você deseja remover 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
basta usar este código simples:
NSArray *hasDuplicates = /* (...) */;
NSArray *noDuplicates = [[NSSet setWithArray: hasDuplicates] allObjects];
como o nsset não permite valores duplicados e todos os objetos retornam uma matriz
NSOrderedSet
insteed de NSSet
.