ios 从Objective-C中的数组中删除项目
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19107905/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Removing an item from an array in Objective-C
提问by Choppin Broccoli
This question might seem easy, but I'm looking for the most efficient and memory friendly way.
这个问题可能看起来很简单,但我正在寻找最有效和内存友好的方式。
Let's say I have an array of Person objects. Each person has a hair color represented by an NSString
. Let's then say I want to remove all Person objects from the array where their hair color is brown.
假设我有一个 Person 对象数组。每个人都有一种由 代表的头发颜色NSString
。然后假设我想从数组中删除所有 Person 对象,其中它们的头发颜色为棕色。
How do I do this?
我该怎么做呢?
Keep in mind that you cannot remove an object from an array that is being enumerated upon.
请记住,您不能从正在枚举的数组中删除对象。
回答by Carl Veazey
There are two general approaches. We can test each element and then immediately remove the element if it meets the test criteria, or we can test each element and store the indexes of elements meeting the test criteria, and then remove all such elements at once. With memory usage a real concern, the storage requirements for the latter approach may render it undesirable.
有两种通用方法。我们可以对每个元素进行测试,如果满足测试条件则立即删除该元素,也可以测试每个元素并存储满足测试条件的元素的索引,然后一次性删除所有这些元素。由于内存使用是一个真正的问题,后一种方法的存储要求可能使其不受欢迎。
With the "store all indexes to remove, then remove them" approach off the table, we need to consider the details involved in the former approach, and how they will effect the correctness and speed of the approach. There are two fatal errors waiting in this approach. The first is to remove the evaluated object not based on its index in the array, but rather with the removeObject:
method. removeObject:
does a linear search of the array to find the object to remove. With a large, unsorted data set, this will destroy our performance as the time increases with the square of the input size. By the way, using indexOfObject:
and then removeObjectAtIndex:
is just as bad, so we should avoid it also. The second fatal error would be starting our iteration at index 0. NSMutableArray
rearranges the indexes after adding or removing an object, so if we start with index 0, we'll be guaranteed an index out of bounds exception if even one object is removed during the iteration. So, we have to start from the back of the array, and only remove objects that have lower indexes than every index we've checked so far.
对于“存储所有要删除的索引,然后删除它们”的方法,我们需要考虑前一种方法所涉及的细节,以及它们将如何影响方法的正确性和速度。这种方法有两个致命错误在等待。第一种是不基于其在数组中的索引,而是根据removeObject:
方法删除评估对象。removeObject:
对数组进行线性搜索以找到要删除的对象。对于大型未排序的数据集,随着时间随输入大小的平方增加,这将破坏我们的性能。顺便说一句,使用indexOfObject:
thenremoveObjectAtIndex:
也同样糟糕,所以我们也应该避免它。第二个致命错误是在索引 0 处开始迭代。NSMutableArray
在添加或删除对象后重新排列索引,因此如果我们从索引 0 开始,我们将保证索引越界异常,即使在迭代过程中删除了一个对象。因此,我们必须从数组的后面开始,并且只删除索引低于我们迄今为止检查过的每个索引的对象。
Having laid that out, there's really two obvious choices: a for
loop that starts at the end rather than the beginning of the array, or the NSArray
method enumerateObjectsWithOptions:usingBlock:
method. Examples of each follow:
说完之后,确实有两个明显的选择:for
从数组的末尾而不是开头开始的循环,或者NSArray
方法enumerateObjectsWithOptions:usingBlock:
方法。每个示例如下:
[persons enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(Person *p, NSUInteger index, BOOL *stop) {
if ([p.hairColor isEqualToString:@"brown"]) {
[persons removeObjectAtIndex:index];
}
}];
NSInteger count = [persons count];
for (NSInteger index = (count - 1); index >= 0; index--) {
Person *p = persons[index];
if ([p.hairColor isEqualToString:@"brown"]) {
[persons removeObjectAtIndex:index];
}
}
My tests seem to show the for
loop marginally faster - maybe about a quarter second faster for 500,000 elements, which is a difference between 8.5 and 8.25 seconds, basically. So I would suggest using the block approach, as it's safer and feels more idiomatic.
我的测试似乎显示for
循环稍微快一点 - 对于 500,000 个元素,可能快四分之一秒,基本上是 8.5 和 8.25 秒之间的差异。所以我建议使用块方法,因为它更安全并且感觉更惯用。
回答by Rob
Assuming you're dealing with a mutable array and it isn't sorted/indexed (i.e. you have to scan through the array), you can iterate through the array in reverse order using enumerateObjectsWithOptions
with the NSEnumerationReverse
option:
假设您正在处理一个可变数组并且它没有排序/索引(即您必须扫描数组),您可以使用enumerateObjectsWithOptions
以下NSEnumerationReverse
选项以相反的顺序遍历数组:
[array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// now you can remove the object without affecting the enumeration
}];
By going in reverse order, you can remove an object from the array being enumerated.
通过以相反的顺序进行,您可以从被枚举的数组中删除一个对象。
回答by AdamG
NSMutableArray * tempArray = [self.peopleArray mutableCopy];
for (Person * person in peopleArray){
if ([person.hair isEqualToString: @"Brown Hair"])
[tempArray removeObject: person]
}
self.peopleArray = tempArray;
Or NSPredicate also works: http://nshipster.com/nspredicate/
或者 NSPredicate 也有效:http://nshipster.com/nspredicate/
回答by Xcoder
The key is to use predicates for filtering array. See the code below;
关键是使用谓词过滤数组。看下面的代码;
- (NSArray*)filterArray:(NSArray*)list
{
return [list filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings){
People *currentObj = (People*)evaluatedObject;
return (![currentObj.hairColour isEqualToString:@"brown"]);
}]];
}
回答by Balu
try like this,
试试这样
NSIndexSet *indices = [personsArray indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return [[obj objectForKey:@"hair"] isEqual:@"Brown Hair"];
}];
NSArray *filtered = [personsArray objectsAtIndexes:indices];
OR
或者
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.hair=%@ ",@"Brown Hair"];
NSArray* myArray = [personsArray filteredArrayUsingPredicate:predicate];
NSLog(@"%@",myArray);
回答by Pierre Houston
If you're making a copy of an array with certain items filtered out, then make a new mutable array, iterate over the original and add to the copy on the fly, as others have suggested for this answer. But your question said removing from an existing (presumably mutable) array.
如果您正在制作过滤掉某些项目的数组的副本,则制作一个新的可变数组,迭代原始数组并动态添加到副本,正如其他人对此答案所建议的那样。但是您的问题是从现有(可能是可变的)数组中删除。
While iterating, you could build an array of objects to remove, and then remove them afterwards:
在迭代时,您可以构建要删除的对象数组,然后再删除它们:
NSMutableArray *thePeople = ...
NSString *hairColorToMatch = ...
NSMutableArray *matchingObjects = [NSMutableArray array];
for (People *person in thePeople) {
if (person.hairColor isEqualToString:hairColorToMatch])
[matchingObjects addObject:person];
[thePeople removeObjects:matchingObjects];
But this creates a temporary array that you might think is wasteful and, more importantly, it's hard to see removeObjects:
being very efficient. Also, someone mentioned something about arrays with duplicate items, this should work in that case but wouldn't be the best, with each duplicate also in the temporary array and redundant matching within removeObjects:
.
但这会创建一个您可能认为是浪费的临时数组,更重要的是,很难看出removeObjects:
它非常有效。此外,有人提到了有关具有重复项的数组的内容,这在那种情况下应该有效,但不是最好的,每个重复项也在临时数组中,并且在removeObjects:
.
One can iterate by index instead and remove as you goes, but that makes the loop logic rather awkward. Instead, I would collect the indexes in an index set and again, remove afterwards:
可以改为按索引迭代并随时删除,但这使得循环逻辑相当尴尬。相反,我会收集索引集中的索引,然后再删除:
NSMutableIndexSet *matchingIndexes = [NSMutableIndexSet indexSet];
for (NSUInteger n = thePeople.count, i = 0; i < n; ++i) {
People *person = thePeople[i];
if ([person.hairColor isEqualToString:hairColorToMatch])
[matchingIndexes addIndex:i];
}
[thePeople removeObjectsAtIndexes:matchingIndexes];
I believe index sets have really low overhead so this is nearly as efficient as you'll get and hard to screw up. Another thing about removing in a batch at the end like this is that it's possible that Apple has optimized removeObjectsAtIndexes:
to be better than a sequence of removeObjectAtIndex:
. So even with the overhead of creating the index set data structure, this may beat out removing on the fly while iterating. This one works pretty well too if the array has duplicates.
我相信索引集的开销非常低,所以这几乎和你得到的一样有效,而且很难搞砸。像这样在最后批量删除的另一件事是,Apple 可能已经优化removeObjectsAtIndexes:
为比一系列removeObjectAtIndex:
. 因此,即使存在创建索引集数据结构的开销,这也可能会在迭代时即时删除。如果数组有重复,这个也能很好地工作。
If instead, you really are making a filtered copy, then I thought there was some KVC
collection operator you can use (I was reading about those recently, you can do some crazy things with those according NSHipster& Guy English). Apparently no, but close, one needs to use KVC andNSPredicate in this somewhat wordy line:
相反,如果您确实在制作过滤副本,那么我认为KVC
您可以使用一些集合运算符(我最近正在阅读这些运算符,根据NSHipster和Guy English,您可以使用这些运算符做一些疯狂的事情)。显然没有,但很接近,在这个有点冗长的行中需要使用 KVC和NSPredicate:
NSArray *subsetOfPeople = [allPeople filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:@"SELF.hairColor != %@", hairColorToMatch]];
Do go ahead and create a category on NSArray
to make things more concise for your code, filterWithFormat:
or something.
请继续创建一个类别,NSArray
以使您的代码filterWithFormat:
或其他内容更加简洁。
(all untested, typed directly into SO)
(全部未经测试,直接输入 SO)
回答by GigiManco
NSMutableArray *arrayForStuff = ...
[arrayForStuff removeObjectAtIndex:[arrayForStuff indexOfObject:objectToRemove]];