Найкращий спосіб видалити повторювані значення ( NSString
) з NSMutableArray
об'єктива-C?
Це найпростіший і правильний спосіб зробити це?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
Найкращий спосіб видалити повторювані значення ( NSString
) з NSMutableArray
об'єктива-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
якщо ви не дбаєте про замовлення .
Якщо ми використовуємо Оператори об’єктів з кодування ключових значень, ми можемо це зробити:
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:
в Джимі), не підходить для великих масивів. Ці перевірки виконуються в 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 Object Operator, який пропонує більш елегантне рішення uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
Ось категорія NSArray .
Ще один простий спосіб ви можете спробувати, який не додасть значення дубліката перед додаванням об'єкта в масив: -
// Припустимо, 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. .це буде працювати для вас. 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
insteed з NSSet
.