Лучший способ удалить дубликаты значений ( 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];
[NSOrderedSet orderedSetWithArray:array];
Вы можете затем вернуть массив через array = [orderedSet allObjects];
или просто использовать NSOrderedSet
s вместо NSArray
первого.
[orderedSet allObjects]
на [orderedSet array]
!
NSArray
и должны создавать временные NSMutableArray
. В вашем примере вы работаете наоборот
NSSet
) или ссылка @Simon Whitaker предотвращают перед добавлением дубликатов, что является эффективным способом?
Я знаю, что это старый вопрос, но есть более элегантный способ удалить дубликаты, NSArray
если вы не заботитесь о порядке .
Если мы используем объектные операторы из Key Value Coding, мы можем сделать это:
uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
Как отметил AnthoPak, можно удалить дубликаты на основе свойства. Примером может быть:@distinctUnionOfObjects.name
@distinctUnionOfObjects.property
для удаления дубликатов по свойству массива пользовательских объектов. Например@distinctUnionOfObjects.name
Да, использование NSSet - разумный подход.
Чтобы добавить к ответу Джима Пулса, вот альтернативный подход к удалению дубликатов при сохранении порядка:
// Initialise a new, empty mutable array
NSMutableArray *unique = [NSMutableArray array];
for (id obj in originalArray) {
if (![unique containsObject:obj]) {
[unique addObject:obj];
}
}
По сути, это тот же подход, что и у Джима, но он копирует уникальные элементы в новый изменяемый массив, а не удаляет дубликаты из оригинала. Это делает его немного более эффективным в отношении памяти в случае большого массива с большим количеством дубликатов (не нужно делать копию всего массива) и, на мой взгляд, немного более читабельным.
Обратите внимание, что в любом случае проверка того, включен ли элемент в целевой массив (используется containsObject:
в моем примере или indexOfObject:inRange:
в Jim's), плохо масштабируется для больших массивов. Эти проверки выполняются за время O (N), что означает, что если вы удвоите размер исходного массива, то для каждой проверки потребуется вдвое больше времени. Так как вы делаете проверку для каждого объекта в массиве, вы также будете выполнять больше этих более дорогих проверок. Общий алгоритм (как мой, так и Джима) выполняется за время O (N 2 ), что быстро растет с ростом исходного массива.
Чтобы получить это время O (N), вы можете использовать a 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++;
}
}
ОБНОВЛЕНИЕ : Юрий Ниязов указал, что мой последний ответ на самом деле работает в O (N 2 ), потому что, removeObjectAtIndex:
вероятно, работает в O (N) времени.
(Он говорит «вероятно», потому что мы не знаем наверняка, как это реализовано; но одна из возможных реализаций состоит в том, что после удаления объекта с индексом X метод затем проходит по каждому элементу от индекса X + 1 до последнего объекта в массиве , перемещая их в предыдущий индекс. Если это так, то это действительно производительность O (N).)
Так что делать? Это зависит от ситуации. Если у вас большой массив и вы ожидаете только небольшое количество дубликатов, то дедупликация на месте будет работать нормально и избавит вас от необходимости создавать дублирующий массив. Если у вас есть массив, в котором вы ожидаете много дубликатов, то, вероятно, лучше всего создать отдельный дедуплицированный массив. Вывод здесь заключается в том, что нотация big-O описывает только характеристики алгоритма, он не будет однозначно сообщать вам, какой вариант лучше всего подходит для любого конкретного обстоятельства.
Если вы нацелены на iOS 5+ (что охватывает весь мир iOS), лучше всего использовать NSOrderedSet
. Он удаляет дубликаты и сохраняет ваш порядок NSArray
.
Просто сделать
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
Теперь вы можете преобразовать его обратно в уникальный NSArray
NSArray *uniqueArray = orderedSet.array;
Или просто используйте orderSet, потому что он имеет те же методы, что и 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
s или просто 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 .
Еще один простой способ, который вы можете попробовать, который не добавит дубликат Value перед добавлением объекта в массив:
// Предположим, что mutableArray выделен и инициализирован и содержит некоторое значение
if (![yourMutableArray containsObject:someValue])
{
[yourMutableArray addObject:someValue];
}
Удалите повторяющиеся значения из NSMutableArray в Objective-C
NSMutableArray *datelistArray = [[NSMutableArray alloc]init];
for (Student * data in fetchStudentDateArray)
{
if([datelistArray indexOfObject:data.date] == NSNotFound)
[datelistArray addObject:data.date];
}
Вот код удаления значений дубликатов из NSMutable Array. Это будет работать для вас. myArray - это ваш изменяемый массив, который вы хотите удалить дублирующиеся значения.
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
просто используйте этот простой код:
NSArray *hasDuplicates = /* (...) */;
NSArray *noDuplicates = [[NSSet setWithArray: hasDuplicates] allObjects];
так как nsset не допускает повторяющихся значений и все объекты возвращают массив
NSOrderedSet
застрахованный NSSet
.