将NSArray过滤到Objective-C中的新NSArray中


121

我有一个NSArray,我想NSArray使用满足特定条件的原始数组中的对象创建一个新对象。条件由返回的函数决定BOOL

我可以创建一个NSMutableArray,遍历源数组,并复制过滤器函数接受的对象,然后创建它的不可变版本。

有没有更好的办法?

Answers:


140

NSArrayNSMutableArray提供过滤数组内容的方法。NSArray提供filteredArrayUsingPredicate:返回一个新数组,该数组包含接收器中与指定谓词匹配的对象。NSMutableArray添加filterUsingPredicate:根据指定的谓词评估接收者的内容,仅保留匹配的对象。在下面的示例中说明了这些方法。

NSMutableArray *array =
    [NSMutableArray arrayWithObjects:@"Bill", @"Ben", @"Chris", @"Melissa", nil];

NSPredicate *bPredicate =
    [NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"];
NSArray *beginWithB =
    [array filteredArrayUsingPredicate:bPredicate];
// beginWithB contains { @"Bill", @"Ben" }.

NSPredicate *sPredicate =
    [NSPredicate predicateWithFormat:@"SELF contains[c] 's'"];
[array filteredArrayUsingPredicate:sPredicate];
// array now contains { @"Chris", @"Melissa" }

84
我听了Papa Smurf的播客,Papa Smurf说答案应该存在于StackOverflow中,以便社区可以对其进行评分和改进。
willc2

10
@mmalc-也许更合适,但是在这里查看它当然更方便。
布赖恩

5
NSPredicate已死,万岁!cf. 我的回答如下。
粘土桥

包含[c]是什么意思?我总是看到[c],但我不明白它的作用是什么?
user1007522 2014年

3
@ user1007522,[c]使比赛不区分大小写
jonbauer 2014年

104

有很多方法可以做到这一点,但是到目前为止,最肯定的方法是使用[NSPredicate predicateWithBlock:]

NSArray *filteredArray = [array filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
    return [object shouldIKeepYou];  // Return YES for each object you want in filteredArray.
}]];

我认为这简明扼要。


迅速:

对于那些工作NSArrayS IN斯威夫特,你可能更喜欢这种简洁的版本:

nsArray = nsArray.filter { $0.shouldIKeepYou() }

filter只是ArrayNSArray隐式桥接到Swift的Array)的方法。它带有一个参数:一个闭包,该闭包在数组中使用一个对象并返回一个Bool。在关闭时,只需返回true过滤数组中所需的任何对象即可。


1
NSDictionary *绑定在这里扮演什么角色?
Kaitain

NSPredicate predicateWithBlock:API 需要@Kaitain绑定。
ThomasW

@Kaitain bindings词典可以包含模板的变量绑定。
阿敏·内格姆·阿瓦德

处理复杂对象时,比接受的答案有用得多:)
Ben Leggiero

47

根据Clay Bridges的回答,这是一个使用块进行过滤的示例(更改yourArray为数组变量名称和testFunc测试函数的名称):

yourArray = [yourArray objectsAtIndexes:[yourArray indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
    return [self testFunc:obj];
}]];

最后的答案不仅提到了使用块进行过滤,而且还给出了有关如何进行过滤的良好示例。谢谢。
Krystian

我喜欢这个答案;尽管filteredArrayUsingPredicate比较精简,但您不使用任何谓词的事实掩盖了目的。
James Perih '16

这是最现代的方法。就我个人而言,我渴望在某些时候从API消失的旧的简写形式“ objectsPassingTest”。但这仍然运行得很好。我喜欢NSPredicate -但对于其他的东西,在需要更重的锤子
Motti Shneor

您现在将得到错误Implicit conversion of 'NSUInteger' (aka 'unsigned long') to 'NSIndexSet * _Nonnull' is disallowed with ARC...它期望NSIndexSets
anoop4real

@ anoop4real,我认为您提到的警告原因是您错误地使用indexOfObjectPassingTest而不是indexesOfObjectsPassingTest。容易错过,但与众不同:)
pckill

46

如果您使用的是OS X 10.6 / iOS 4.0或更高版本,则使用块可能要比使用NSPredicate更好。见-[NSArray indexesOfObjectsPassingTest:]或写自己的类增加一个方便-select:-filter:方法(例如)。

希望其他人编写该类别,对其进行测试等吗?查看BlocksKitarray docs)。而且还有很多更多的例子来,比方说可以发现,搜索如“的NSArray块类别中选择”


5
您介意通过示例扩展答案吗?当您最需要博客网站时,它就会死掉。
Dan Abramov

8
防止链接腐烂的方法是从链接的文章中摘录相关代码和其他内容,而不是添加更多链接。保留链接,但添加一些示例代码。
toolbear 2012年

3
@ClayBridges我发现了这个问题,寻找在可可中过滤的方法。由于您的答案中没有任何代码示例,因此我花了大约5分钟的时间来浏览您的链接,以便发现这些块无法满足我的需求。如果代码在答案中,则可能需要30秒钟。如果没有实际代码,此答案的用处不大。
2012年

1
@mydogisbox等:这里只有4个答案,因此有足够的空间来容纳您自己的闪亮且出色的代码采样答案。我对我很满意,所以请不要管它。
粘土桥

2
mydogisbox在这一点上是正确的;如果您正在做的只是提供链接,即使您添加更多链接,也并不是真正的答案。请参阅meta.stackexchange.com/questions/8231。石蕊测试:您的答案可以独立存在,还是需要单击链接才能具有任何价值? 特别是,您声明“使用块可能比使用NSPredicate更好”,但是您并没有真正解释为什么。
罗伯特·哈维

16

假设您的对象都是相似的类型,则可以添加一个方法作为其基类的类别,该方法调用您用于条件的函数。然后创建一个引用该方法的NSPredicate对象。

在某些类别中定义使用函数的方法

@implementation BaseClass (SomeCategory)
- (BOOL)myMethod {
    return someComparisonFunction(self, whatever);
}
@end

然后,无论您在哪里过滤:

- (NSArray *)myFilteredObjects {
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"myMethod = TRUE"];
    return [myArray filteredArrayUsingPredicate:pred];
}

当然,如果您的函数仅与类中可访问的属性进行比较,则将函数的条件转换为谓词字符串可能会更容易。


我喜欢这个答案,因为它既优美又简短,并且简化了再次学习如何最基本地将NSPredicate形式化的需求-访问属性和布尔方法。我什至认为不需要包装器。一个简单的[myArrayfilteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@“ myMethod = TRUE”]]]; 就足够了。谢谢!(我也很喜欢其他选择,但这很不错)。
Motti Shneor

3

NSPredicate是建设条件进行筛选集合NEXTSTEP的方式(NSArrayNSSetNSDictionary)。

例如考虑两个数组arrfilteredarr

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

filteredarr = [NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];

filterarr肯定会包含仅包含字符c的项。

使容易记住那些小sql背景的人是

*--select * from tbl where column1 like '%a%'--*

1)从* tbl中选择* ->集合

2)像'%a%'这样的column1- >NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

3)从tbl中选择*,其中column1喜欢'%a%' ->

[NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];

我希望这有帮助


2

NSArray + Xh

@interface NSArray (X)
/**
 *  @return new NSArray with objects, that passing test block
 */
- (NSArray *)filteredArrayPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate;
@end

NSArray + Xm

@implementation NSArray (X)

- (NSArray *)filteredArrayPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
{
    return [self objectsAtIndexes:[self indexesOfObjectsPassingTest:predicate]];
}

@end

您可以在这里下载此类别


0

签出这个图书馆

https://github.com/BadChoice/Collection

它带有许多简单的数组函数,不再需要编写循环

因此,您可以执行以下操作:

NSArray* youngHeroes = [self.heroes filter:^BOOL(Hero *object) {
    return object.age.intValue < 20;
}];

要么

NSArray* oldHeroes = [self.heroes reject:^BOOL(Hero *object) {
    return object.age.intValue < 20;
}];

0

最好的简便方法是创建此方法并传递数组和值:

- (NSArray *) filter:(NSArray *)array where:(NSString *)key is:(id)value{
    NSMutableArray *temArr=[[NSMutableArray alloc] init];
    for(NSDictionary *dic in self)
        if([dic[key] isEqual:value])
            [temArr addObject:dic];
    return temArr;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.