user1418214
user1418214

Reputation: 59

NSMutableArray removeObjectAtIndex usage

I am trying to filter out an array of strings based on their length. I'm completely new to Objective C and OOP in general.

wordList=[[stringFile componentsSeparatedByCharactersInSet:[NSCharacterSetnewlineCharacterSet]] mutableCopy];
for (int x=0; x<[wordList count]; x++) {
    if ([[wordList objectAtIndex:x] length] != 6) {
        [wordList removeObjectAtIndex:x];
    }else {
       NSLog([wordList objectAtIndex:x]);
    }
}

for (int x=0; x<[wordList count]; x++) {
    NSLog([wordList objectAtIndex:x]);
}

The NSLog in the else statement will only output 6 letter words, but the second NSLog outputs the entire array. What am I missing here? Also any general pointers to clean up/improve the code are appreciated.

Upvotes: 2

Views: 423

Answers (2)

David R&#246;nnqvist
David R&#246;nnqvist

Reputation: 56625

Depending on what you feel is the easiest to understand you could either filter the array with a predicate or iterate over the array and remove objects. You should chose the approach that you have easiest to understand and maintain.

Filter using a predicate

Predicates are a very concise way of filtering array or sets but depending on your background they may feel strange to use. You could filter your array like this:

NSMutableArray * wordList = // ...
[wordList filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
    NSString *word = evaluatedObject;
    return ([word length] == 6);
}]];

Enumerating and removing

You cannot modify the array while enumerating it but you can make a note of all the items what you want to remove and remove them all in a batch after having enumerated the entire array, like this:

NSMutableArray * wordList = // ...
NSMutableIndexSet *indicesForObjectsToRemove = [[NSMutableIndexSet alloc] init];
[wordList enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    NSString *word = obj;
    if ([word length] != 6) [indicesForObjectsToRemove addIndex:idx];
}];
[wordList removeObjectsAtIndexes:indicesForObjectsToRemove];

Upvotes: 3

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726479

The problem with your code is that when you remove an item at index x and move to the next index x++, the item that was at x+1 is never examined.

The best way of filtering a mutable array is using the filterUsingPredicate: method. Here is how you use it:

wordList=[[stringFile
    componentsSeparatedByCharactersInSet:[NSCharacterSetnewlineCharacterSet]]
    mutableCopy];
[wordList filterUsingPredicate:
    [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary * bindings) { 
        return [evaluatedObject length] == 6; // YES means "keep"
    }]];

Upvotes: 2

Related Questions