Лучший способ удалить из NSMutableArray во время итерации? - PullRequest
194 голосов
/ 21 сентября 2008

В Какао, если я хочу перебрать NSMutableArray и удалить несколько объектов, которые соответствуют определенным критериям, каков наилучший способ сделать это без перезапуска цикла при каждом удалении объекта?

Спасибо

Изменить: Просто чтобы уточнить - я искал лучший путь, например. что-то более элегантное, чем обновление индекса вручную. Например, в C ++ я могу сделать;

iterator it = someList.begin();

while (it != someList.end())
{
    if (shouldRemove(it))   
        it = someList.erase(it);
}

Ответы [ 20 ]

6 голосов
/ 28 августа 2013

Вот простой и чистый способ. Мне нравится дублировать мой массив прямо в вызове быстрого перечисления:

for (LineItem *item in [NSArray arrayWithArray:self.lineItems]) 
{
    if ([item.toBeRemoved boolValue] == YES) 
    {
        [self.lineItems removeObject:item];
    }
}

Таким образом, вы перечисляете копию удаляемого массива, в котором хранятся одинаковые объекты. NSArray содержит только объектные указатели, так что это совершенно нормально с точки зрения памяти и производительности.

5 голосов
/ 21 сентября 2008

это должно сделать это:

    NSMutableArray* myArray = ....;

    int i;
    for(i=0; i<[myArray count]; i++) {
        id element = [myArray objectAtIndex:i];
        if(element == ...) {
            [myArray removeObjectAtIndex:i];
            i--;
        }
    }

надеюсь, это поможет ...

5 голосов
/ 21 сентября 2008

Добавьте объекты, которые вы хотите удалить, во второй массив и после цикла используйте -removeObjectsInArray:.

1 голос
/ 22 сентября 2008

Как насчет замены элементов, которые вы хотите удалить, на 'n'th element,' n-1'th element и так далее?

Когда вы закончите, вы измените размер массива на «предыдущий размер - количество свопов»

1 голос
/ 27 августа 2015

Хорошей реализацией может быть использование метода категории ниже в NSMutableArray.

@implementation NSMutableArray(BMCommons)

- (void)removeObjectsWithPredicate:(BOOL (^)(id obj))predicate {
    if (predicate != nil) {
        NSMutableArray *newArray = [[NSMutableArray alloc] initWithCapacity:self.count];
        for (id obj in self) {
            BOOL shouldRemove = predicate(obj);
            if (!shouldRemove) {
                [newArray addObject:obj];
            }
        }
        [self setArray:newArray];
    }
}

@end

Блок предикатов может быть реализован для обработки каждого объекта в массиве. Если предикат возвращает true, объект удаляется.

Пример массива дат для удаления всех дат, которые были в прошлом:

NSMutableArray *dates = ...;
[dates removeObjectsWithPredicate:^BOOL(id obj) {
    NSDate *date = (NSDate *)obj;
    return [date timeIntervalSinceNow] < 0;
}];
1 голос
/ 22 сентября 2008

Если все объекты в вашем массиве уникальны или вы хотите удалить все вхождения объекта при его обнаружении, вы можете быстро перечислить копию массива и использовать [NSMutableArray removeObject:] для удаления объекта из оригинала.

NSMutableArray *myArray;
NSArray *myArrayCopy = [NSArray arrayWithArray:myArray];

for (NSObject *anObject in myArrayCopy) {
    if (shouldRemove(anObject)) {
        [myArray removeObject:anObject];
    }
}
1 голос
/ 21 сентября 2008

Почему бы вам не добавить объекты, которые нужно удалить, в другой NSMutableArray. Когда вы закончите итерацию, вы можете удалить собранные вами объекты.

1 голос
/ 18 июня 2010

Ответ Бензадо выше - это то, что вы должны сделать для преформирования. В одном из моих приложений метод removeObjectsInArray занял 1 минуту, просто добавление в новый массив заняло 0,023 секунды.

1 голос
/ 23 января 2012

Я определяю категорию, которая позволяет мне фильтровать, используя блок, например:

@implementation NSMutableArray (Filtering)

- (void)filterUsingTest:(BOOL (^)(id obj, NSUInteger idx))predicate {
    NSMutableIndexSet *indexesFailingTest = [[NSMutableIndexSet alloc] init];

    NSUInteger index = 0;
    for (id object in self) {
        if (!predicate(object, index)) {
            [indexesFailingTest addIndex:index];
        }
        ++index;
    }
    [self removeObjectsAtIndexes:indexesFailingTest];

    [indexesFailingTest release];
}

@end

, который затем можно использовать так:

[myMutableArray filterUsingTest:^BOOL(id obj, NSUInteger idx) {
    return [self doIWantToKeepThisObject:obj atIndex:idx];
}];
0 голосов
/ 10 августа 2015

Итерации в обратном направлении были моими любимыми годами, но долгое время я никогда не сталкивался со случаем, когда самый глубокий (самый высокий счет) объект был удален первым. За мгновение до того, как указатель переходит к следующему индексу, ничего не происходит, и он падает.

Путь Бензадо ближе всего к тому, что я делаю сейчас, но я никогда не думал, что после каждого удаления будет перестановка стека.

под Xcode 6 это работает

NSMutableArray *itemsToKeep = [NSMutableArray arrayWithCapacity:[array count]];

    for (id object in array)
    {
        if ( [object isNotEqualTo:@"whatever"]) {
           [itemsToKeep addObject:object ];
        }
    }
    array = nil;
    array = [[NSMutableArray alloc]initWithArray:itemsToKeep];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...