从NSMutableArray中删除对象?

5

我有一个数组,其中包含以下元素,在ViewDidLoad方法中:

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

我有另一个UITextField用于搜索元素。因此,一旦我在UITextField中键入某些内容,我想检查该字符串是否存在于“inputArray”中。如果与inputArray中的元素不匹配,则从inputArray中删除相应的元素。

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

  }

但是这段代码显示了异常,与“removeobject:”有关。

异常:

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

你得到了什么异常?它必须是可变的。但根据你的问题,它不是可变的。 - Vignesh
由于未捕获的异常 'NSInvalidArgumentException',应用程序终止,原因是:'*** -[__NSCFConstantString rangeOfString:options:range:locale:]: nil argument' *** 第一次抛出调用堆栈: - Naveen
@Naveen 如果这是你的异常,你应该查看你的参数(可能是s)并查看哪一个是nil - Rob
8个回答

10

在快速枚举中,您不可以修改集合。

枚举器对象将变为常量和不可变的。

如果您想对数组进行更新,您应该像这样做:

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];
    }
}

编辑:

上述工作原理类似于这个:

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

你可能应该在 if 中打破循环,从而保证只删除第一个出现的元素,或者将索引计数器减一,因为你已经删除了一个元素,因此数组中的其余部分“下降”了一个位置。由于这在 for 循环中可能看起来很奇怪,所以你可以改用 while 循环。 - Monolo
我只是将你的代码转换为传统的for循环。其他的你比我更清楚 :) - Anoop Vaidya
@Monolo:我没有深入研究它的工作原理,只是找到了错误并将快速枚举转换为常规枚举。 - Anoop Vaidya
1
@AnoopVaidya 指出 filteredArrayUsingPredicate 的用法是正确的,点赞。如果使用 NSMutableArray,你也可以直接使用 filterUsingPredicate 进行筛选。 - Rob
3
我喜欢直接使用filterUsingPredicate:的想法 - 这可以在一行易于理解的代码中完成OP所要求的功能。 - Monolo
显示剩余7条评论

9
您可以使用以下代码。
for (int i=0;i<[inputArray count]; i++) {
        NSString *item = [inputArray objectAtIndex:i];
        if ([item rangeOfString:s].location == NSNotFound) {
            [inputArray removeObject:item];
            i--;
        }
    }

1
这很有道理。或者,您可以以相反的顺序迭代指标_i,这样就不需要调整指标来删除项目了。但我喜欢这个。 - Rob
另外,您需要说明为什么要采用这种适应方式,而不是快速枚举。 - Anoop Vaidya
1
如果您正在对一个对象执行快速枚举,那么您无法从中删除对象。如果您尝试从正在进行快速枚举的NSMutableArray中删除对象,它将抛出异常。这就是为什么我使用传统的for循环。 - Muhammad Nabeel Arif

0

那需要是一个NSMutableArray。一旦创建了NSArray,就无法修改它(除非重新开始)。

更改为:

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

转换为:

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

并且将属性也更改为NSMutableArray:

@property(nonatomic, strong) NSMutableArray *inputArray;

使用您的编码后,我得到了“NSInvalidArgumentException”,原因是:“*** -[__NSCFConstantString rangeOfString:options:range:locale:]: nil argument”。 - Naveen

0

这是一个我喜欢使用的干净的解决方案。您可以定义一个NSArray类别进行扩展并创建一个map方法。该方法根据您在块中返回的内容创建一个新的NSArray:

@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

该代码块将针对原始数组中的每个项目调用一次,并将此对象作为参数传递:

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

newArray 将包含您筛选出的项目!


mapWithOptions 是从哪里来的? - Martin R
是的,抱歉。给你。 :) - Pedro Mancheno

0

使用以下代码。(该代码用于根据UITextField的输入字符串/文本过滤数组

ViewDidLoad方法中使用两个NSMutableArray,并将一个数组添加到另一个数组中,例如:

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

并编写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];
}

}

输出结果在你的控制台中显示。


0

正如其他人所说,您不能在枚举数组时对其进行改变。想要实现您想要的功能并保持快速枚举的便利性,最简单的方法是复制该数组。

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

1
请注意,一旦从inputArray中移除一个元素,inputArray[inputArray copy]的索引将不再匹配。 - Martin R

0
+1 给 Anoop,他指出你可以使用 filteredArrayUsingPredicate。因此,如果你想要基于 inputArray 中的匹配项创建一个新数组,你也可以使用类似以下的代码:

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

或者,鉴于inputArray是一个NSMutableArray,您可以使用以下单行代码轻松过滤数组:

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

或者,如果你喜欢块:

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

0

你问题中的s可能是空值。因此你得到了异常。请检查一下。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接