Naveen
Naveen

Reputation: 1251

Remove object from NSMutableArray?

I have an array with following elements in ViewDidLoad method

inputArray = [NSMutableArray arrayWithObjects:@"car", @"bus", @"helicopter", @"cruiz", @"bike", @"jeep", nil];

I have another UITextField for searching the elements .So once i type some thing in UITextField i want to check whether that string is present in "inputArray" or not.If it is not matching with elements in inputArray then remove the corresponding elements from inputArray .

 for (NSString* item in inputArray)
   {
        if ([item rangeOfString:s].location == NSNotFound) 
        {
            [inputArray removeObjectIdenticalTo:item];//--> Shows Exception
            NSLog(@"Contains :%@",containsAnother);
        }

  }

but this code shows exception , something related to "removeobject:"

Exception :

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSCFConstantString rangeOfString:options:range:locale:]: nil argument'
*** First throw call stack:
`

Upvotes: 5

Views: 25098

Answers (8)

Rob
Rob

Reputation: 437442

+1 to Anoop for pointing out that you can use filteredArrayUsingPredicate. Thus, if you wanted to create a new array based upon the matches in inputArray, you could also use something like:

NSArray *matchingArray = [inputArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF contains[c] %@", s]];

Alternatively, given that inputArray is a NSMutableArray you can simply filter the array with this single line:

[inputArray filterUsingPredicate:[NSPredicate predicateWithFormat:@"SELF contains[c] %@", s]];

Or, if you like blocks:

[inputArray filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
    return ([evaluatedObject rangeOfString:s].location != NSNotFound);
}]];

Upvotes: 0

Pedro Mancheno
Pedro Mancheno

Reputation: 5237

This is a clean solution that I like to use. You define a NSArray category to extend it and create a map method. This method creates a new NSArray based on what you return within your block:

@interface NSArray (BlockExtensions)
/*! 
 Invokes block once for each element of self, returning a new array containing the
 values returned by the block.
 */
- (NSArray *)map:(id (^)(id obj))block;

@end

@implementation NSArray (BlockExtensions)

- (NSArray *)map:(id (^)(id obj))block
{
    return [self mapWithOptions:0 usingBlock:^id(id obj, NSUInteger idx) {
        return block(obj);
    }];
} 

- (NSArray *)mapWithOptions:(NSEnumerationOptions)options usingBlock:(id (^)(id obj, NSUInteger idx))block
{
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count]];
    [self enumerateObjectsWithOptions:options usingBlock:^(id obj, NSUInteger idx, BOOL *stop)     {

        id newobj = block? block(obj, idx) : obj;
        if (newobj)
            [array addObject:newobj];
    }];
    return array;
}
    @end

The block will be called once for every item in your original array, passing this object as a parameter:

NSArray *newArray = [inputArray map:^id(NSString *item) {
    if ([item rangeOfString:s].location == NSNotFound) {
        return item;
    }
    return nil;
}];

newArray will contain your filtered out items!

Upvotes: 0

Anoop Vaidya
Anoop Vaidya

Reputation: 46533

In fast enumeration you can NOT modify the collection.

The enumerator object becomes constant and immutable.

If you want to do updation on the array

You should like this :

NSMutableArray *inputArray = [NSMutableArray arrayWithObjects:@"car", @"bus", @"helicopter", @"cruiz", @"bike", @"jeep", nil];
NSString *s=@"bus";

for (int i=inputArray.count-1; i>-1; i--) {
    NSString *item = [inputArray objectAtIndex:i];
    if ([item rangeOfString:s].location == NSNotFound) {
        [inputArray removeObject:item];
    }
}

EDIT:

The above works similar as this :

NSArray *array=[inputArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF CONTAINS[c] %@",s]]; 

Upvotes: 10

James P
James P

Reputation: 4836

As others have said you can't mutate an array while it is being enumerated. The easiest way to do what you want and keep the convenience of fast enumeration is to copy the array.

for (NSString* item in [inputArray copy]) {
   ...
}

Upvotes: 0

iPatel
iPatel

Reputation: 47049

Use following Code. (This Code is use for filter Array base on input string/text of UITextField )

Take Two NSMutableArray and add one array to another array in ViewDidLoad method such like,

self.listOfTemArray = [[NSMutableArray alloc] init]; // array no - 1
self.ItemOfMainArray = [[NSMutableArray alloc] initWithObjects:@"YorArrayList", nil]; // array no - 2 

[self.listOfTemArray addObjectsFromArray:self.ItemOfMainArray]; // add 2array to 1 array

And Write following delegate Method of UISearchBar

- (BOOL) textFieldDidChange:(UITextField *)textField
 {
        NSString *name = @"";
        NSString *firstLetter = @"";

    if (self.listOfTemArray.count > 0)
         [self.listOfTemArray removeAllObjects];

        if ([searchText length] > 0)
        {
                for (int i = 0; i < [self.ItemOfMainArray count] ; i = i+1)
                {
                        name = [self.ItemOfMainArray objectAtIndex:i];

                        if (name.length >= searchText.length)
                        {
                                firstLetter = [name substringWithRange:NSMakeRange(0, [searchText length])];
                                //NSLog(@"%@",firstLetter);

                                if( [firstLetter caseInsensitiveCompare:searchText] == NSOrderedSame )
                                {
                                    // strings are equal except for possibly case
                                    [self.listOfTemArray addObject: [self.ItemOfMainArray objectAtIndex:i]];
                                    NSLog(@"=========> %@",self.listOfTemArray);
                                }
                         }
                 }
         }
         else
         {
             [self.listOfTemArray addObjectsFromArray:self.ItemOfMainArray ];
         }

        [self.tblView reloadData];
}

}

Output Show in your Console.

Upvotes: 0

Muhammad Nabeel Arif
Muhammad Nabeel Arif

Reputation: 19310

You can use the following code

for (int i=0;i<[inputArray count]; i++) {
        NSString *item = [inputArray objectAtIndex:i];
        if ([item rangeOfString:s].location == NSNotFound) {
            [inputArray removeObject:item];
            i--;
        }
    }

Upvotes: 9

Vignesh
Vignesh

Reputation: 10251

The s in your question is probably nil. So your are getting the exception.Please check that out.

Upvotes: 0

LJ Wilson
LJ Wilson

Reputation: 14427

That needs to be an NSMutableArray. You can't modify an NSArray once created (except to start all over).

Change this:

inputArray = [NSArray arrayWithObjects:@"car", @"bus", @"helicopter", @"cruiz", @"bike", @"jeep", nil];

to this:

inputArray = [NSMutableArray arrayWithObjects:@"car", @"bus", @"helicopter", @"cruiz", @"bike", @"jeep", nil];

and also change the property to NSMutableArray also:

@property(nonatomic, strong) NSMutableArray *inputArray;

Upvotes: 0

Related Questions