Лучший способ удалить повторяющиеся значения (NSString
) из NSMutableArray
в Objective-C?
Это самый простой и правильный способ сделать это?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
Лучший способ удалить повторяющиеся значения (NSString
) из NSMutableArray
в Objective-C?
Это самый простой и правильный способ сделать это?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
Ваш подход NSSet
является лучшим, если вы не беспокоитесь о порядке объектов, но опять же, если вас не беспокоит порядок, то почему вы не храните их в NSSet
для начала?
Я написал ответ ниже в 2009 году; в 2011 году Apple добавила NSOrderedSet
в iOS 5 и Mac OS X 10.7. Алгоритм теперь состоит из двух строк кода:
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
Если вы беспокоитесь о заказе, и вы работаете на iOS 4 или более ранней версии, переместите копию массива:
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];
Я знаю, что это старый вопрос, но есть более элегантный способ удаления дубликатов в NSArray
, если вам не нужен порядок.
Если мы используем Операторы объектов из кодирования ключевых значений, мы можем сделать это:
uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
Да, использование NSSet - разумный подход.
Чтобы добавить к ответу Джима Пулса, здесь альтернативный подход к удалению дубликатов при сохранении порядка:
// Initialise a new, empty mutable array
NSMutableArray *unique = [NSMutableArray array];
for (id obj in originalArray) {
if (![unique containsObject:obj]) {
[unique addObject:obj];
}
}
По существу тот же подход, что и Jim, но копирует уникальные элементы в новый измененный массив, а не удаляет дубликаты из оригинала. Это делает его немного более эффективным с точки зрения памяти в случае большого массива с большим количеством дубликатов (нет необходимости делать копию всего массива) и, на мой взгляд, немного читаем.
Обратите внимание, что в любом случае проверка того, что элемент уже включен в целевой массив (используя containsObject:
в моем примере или indexOfObject:inRange:
в Jim's), недостаточно хорошо масштабируется для больших массивов. Эти проверки выполняются в O (N) раз, что означает, что если вы удвоите размер исходного массива, то каждая проверка займет в два раза больше времени для запуска. Поскольку вы выполняете проверку для каждого объекта в массиве, вы также будете запускать больше тех более дорогих чеков. Общий алгоритм (как my, так и Jim's) работает в O (N 2) времени, который быстро становится дорогим по мере роста исходного массива.
Чтобы получить это до O (N), вы можете использовать NSMutableSet
для хранения записи элементов, уже добавленных в новый массив, поскольку NSSet ищет O (1), а не O (N). Другими словами, проверка того, является ли элемент членом NSSet, принимает одно и то же время независимо от количества элементов в наборе.
Код с использованием этого подхода будет выглядеть примерно так:
NSMutableArray *unique = [NSMutableArray array];
NSMutableSet *seen = [NSMutableSet set];
for (id obj in originalArray) {
if (![seen containsObject:obj]) {
[unique addObject:obj];
[seen addObject:obj];
}
}
Это все еще кажется немного расточительным; мы все еще генерируем новый массив, когда возникает вопрос о том, что исходный массив изменчив, поэтому мы должны иметь возможность обмануть его на месте и сохранить некоторую память. Что-то вроде этого:
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++;
}
}
UPDATE: Юрий Ниязов указал, что мой последний ответ на самом деле выполняется в O (N 2), потому что removeObjectAtIndex:
, вероятно, работает в O (N) времени.
(Он говорит "возможно", потому что мы точно не знаем, как это реализовано, но одна возможная реализация заключается в том, что после удаления объекта в индексе X метод затем циклически перебирает каждый элемент из индекса X + 1 в последний объект в массиве, перемещая их в предыдущий индекс. Если это случай, то это действительно производительность O (N).)
Итак, что делать? Это зависит от ситуации. Если у вас большой массив, и вы ожидаете всего лишь небольшого количества дубликатов, то устранение дублирования на месте будет очень хорошо работать, и вы должны создать дублирующий массив. Если у вас есть массив, в котором вы ожидаете много дубликатов, тогда создание отдельного, обнуленного массива, вероятно, является наилучшим подходом. Вывод здесь состоит в том, что запись в формате "большой О" описывает только характеристики алгоритма, она не даст вам окончательного определения, которое лучше всего подходит для любого данного обстоятельства.
Доступно в OS X v10.7 и более поздних версиях.
Если вас беспокоит порядок, верный способ сделать
NSArray *no = [[NSOrderedSet orderedSetWithArray:originalArray]allObjects];
Вот код удаления дубликатов значений из NSArray в порядке.
Если вы ориентируетесь на iOS 5+ (что охватывает весь мир iOS), лучше всего используйте NSOrderedSet
. Он удаляет дубликаты и сохраняет порядок NSArray
.
Просто сделай
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
Теперь вы можете преобразовать его в уникальный NSArray
NSArray *uniqueArray = orderedSet.array;
Или просто используйте упорядоченный набор, потому что он имеет те же методы, что и NSArray, например objectAtIndex:
, firstObject
и т.д.
Проверка членства с contains
выполняется еще быстрее на NSOrderedSet
, чем на NSArray
Для получения дополнительной справки Справка NSOrderedSet
нужен заказ
NSArray *yourarray = @[@"a",@"b",@"c"];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourarray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
NSLog(@"%@",arrayWithoutDuplicates);
или не нуждается в заказе
NSSet *set = [NSSet setWithArray:yourarray];
NSArray *arrayWithoutOrder = [set allObjects];
NSLog(@"%@",arrayWithoutOrder);
Здесь я удалил повторяющиеся значения имени из mainArray и сохранил результат в 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]];
}
}
Обратите внимание, что если у вас есть отсортированный массив, вам не нужно проверять все остальные элементы массива, только последний элемент. Это должно быть намного быстрее, чем проверка всех элементов.
// 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]];
}
}
Похоже, что ответы NSOrderedSet
, которые также предлагаются, требуют намного меньше кода, но если вы по какой-то причине не можете использовать NSOrderedSet
, и у вас есть отсортированный массив, я считаю, что мое решение будет быстрый. Я не уверен, как он сравнивается со скоростью решений NSOrderedSet
. Также обратите внимание, что мой код проверяется с помощью isEqualToString:
, поэтому одна и та же серия букв не будет отображаться более одного раза в newArray
. Я не уверен, будут ли решения NSOrderedSet
удалять дубликаты на основе значения или на основе расположения памяти.
В моем примере предполагается, что sortedSourceArray
содержит только NSString
s, просто NSMutableString
s или их сочетание. Если sortedSourceArray
вместо этого содержит только NSNumber
или просто NSDate
s, вы можете заменить
if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])
с
if ([[sortedSourceArray objectAtIndex:i] compare:[sortedSourceArray objectAtIndex:(i-1)]] != NSOrderedSame)
и он должен работать отлично. Если sortedSourceArray
содержит комбинацию NSString
s, NSNumber
s и/или NSDate
s, это, вероятно, сбой.
Здесь есть оператор объекта KVC, который предлагает более элегантное решение uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
Здесь категория NSArray.
Еще один простой способ попробовать, который не будет добавлять повторяющееся значение перед добавлением объекта в массив: -
//Предположим, что mutableArray выделен и инициализирован и содержит некоторое значение
if (![yourMutableArray containsObject:someValue])
{
[yourMutableArray addObject:someValue];
}
Вот код удаления дубликатов значений из NSMutable Array..it будет работать для вас. myArray - это ваш Mutable Array, который вы хотите удалить значения дубликатов.
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
Использование Orderedset
сделает трюк. Это приведет к удалению дубликатов из массива и поддержанию порядка, который обычно устанавливается, не выполняет
просто используйте этот простой код:
NSArray *hasDuplicates = /* (...) */;
NSArray *noDuplicates = [[NSSet setWithArray: hasDuplicates] allObjects];
поскольку nsset не позволяет дублировать значения, а все объекты возвращают массив