使用谓词根据另一个数组过滤 NSArray
Filter NSArray based on another array using predicate
考虑下面的数组。数组包含 'Alpha' 类型的对象。我们只关心类型为 NSString
.
的属性 username
NSArray *some_usernames = @[ <multiple values of type Alpha> ]
NSArray *all_usernames = @[ <multiple values of type Alpha> ]
我基本上想要一个不在数组 some_usernames
中的所有用户名的列表,即
NSArray *remaining_usernames = @[ <all_usernames but not in some_usernames> ];
我打算采用的方式是:
NSPredicates *predicates;
for (Alpha *alpha in some_usernames)
{
predicate = [predicate with @"username != %@", alpha.username];
predicates.add(predicate)
}
create compound predicate
filter all_usernames
但这感觉不太好。有没有办法在两行中做到这一点?我以前看过,但是我不能再指向代码参考了。
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"not (self.username IN %@)", [some_usernames valueForKey:@"username"]];
NSArray *remaining_usernames = [all_usernames filteredArrayUsingPredicate:predicate];
完整示例
@interface Alpha : NSObject
@property (nonatomic, copy) NSString *username;
-(instancetype) initWithUsername:(NSString *)username;
@end
@implementation Alpha
-(instancetype) initWithUsername:(NSString *)username
{
self = [super init];
if (self) {
self.username = username;
}
return self;
}
-(NSString *)description{
return [NSString stringWithFormat:@"%@: %@", NSStringFromClass([self class]), self.username];
}
@end
NSArray *all_usernames = @[[[Alpha alloc] initWithUsername:@"a"], [[Alpha alloc] initWithUsername:@"b"], [[Alpha alloc] initWithUsername:@"z"], [[Alpha alloc] initWithUsername:@"f"], [[Alpha alloc] initWithUsername:@"e"]];
NSArray *some_usernames = @[[[Alpha alloc] initWithUsername:@"b"], [[Alpha alloc] initWithUsername:@"f"]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"not (self.username IN %@)", [some_usernames valueForKey:@"username"]];
NSArray *remaining_usernames = [all_usernames filteredArrayUsingPredicate:predicate];
NSLog(@"%@", remaining_usernames);
打印
(
"Alpha: a",
"Alpha: z",
"Alpha: e"
)
我想添加另一个答案:
如果不需要对象的排序(并且很可能不需要相等的对象),您可以使用集合和集合算术来代替对数组使用谓词过滤。为此,我们必须教导 Alpha
相等的含义并提供哈希方法。在这种情况下,我们只使用 NSStrings 实现:
@implementation Alpha
-(instancetype) initWithUsername:(NSString *)username
{
self = [super init];
if (self) {
self.username = username;
}
return self;
}
-(NSString *)description{
return [NSString stringWithFormat:@"%@: %@", NSStringFromClass([self class]), self.username];
}
-(BOOL)isEqual:(id)object
{
return [self.username isEqual:[object username]];
}
-(NSUInteger)hash
{
return [self.username hash];
}
@end
NSArray *all_usernames = @[[[Alpha alloc] initWithUsername:@"a"],
[[Alpha alloc] initWithUsername:@"b"],
[[Alpha alloc] initWithUsername:@"z"],
[[Alpha alloc] initWithUsername:@"f"],
[[Alpha alloc] initWithUsername:@"e"]];
NSArray *some_usernames = @[[[Alpha alloc] initWithUsername:@"b"],
[[Alpha alloc] initWithUsername:@"f"]];
NSSet *allSet = [NSSet setWithArray:all_usernames];
NSSet *someSet = [NSSet setWithArray:some_usernames];
NSMutableSet *remainingSet = [allSet mutableCopy];
[remainingSet minusSet:someSet];
NSLog(@"%@", remainingSet);
打印
{(
Alpha: z,
Alpha: e,
Alpha: a
)}
此代码对于更多数据应该更快。请收看WWDC 2013: Designing Code for Performance
考虑下面的数组。数组包含 'Alpha' 类型的对象。我们只关心类型为 NSString
.
username
NSArray *some_usernames = @[ <multiple values of type Alpha> ]
NSArray *all_usernames = @[ <multiple values of type Alpha> ]
我基本上想要一个不在数组 some_usernames
中的所有用户名的列表,即
NSArray *remaining_usernames = @[ <all_usernames but not in some_usernames> ];
我打算采用的方式是:
NSPredicates *predicates;
for (Alpha *alpha in some_usernames)
{
predicate = [predicate with @"username != %@", alpha.username];
predicates.add(predicate)
}
create compound predicate
filter all_usernames
但这感觉不太好。有没有办法在两行中做到这一点?我以前看过,但是我不能再指向代码参考了。
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"not (self.username IN %@)", [some_usernames valueForKey:@"username"]];
NSArray *remaining_usernames = [all_usernames filteredArrayUsingPredicate:predicate];
完整示例
@interface Alpha : NSObject
@property (nonatomic, copy) NSString *username;
-(instancetype) initWithUsername:(NSString *)username;
@end
@implementation Alpha
-(instancetype) initWithUsername:(NSString *)username
{
self = [super init];
if (self) {
self.username = username;
}
return self;
}
-(NSString *)description{
return [NSString stringWithFormat:@"%@: %@", NSStringFromClass([self class]), self.username];
}
@end
NSArray *all_usernames = @[[[Alpha alloc] initWithUsername:@"a"], [[Alpha alloc] initWithUsername:@"b"], [[Alpha alloc] initWithUsername:@"z"], [[Alpha alloc] initWithUsername:@"f"], [[Alpha alloc] initWithUsername:@"e"]];
NSArray *some_usernames = @[[[Alpha alloc] initWithUsername:@"b"], [[Alpha alloc] initWithUsername:@"f"]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"not (self.username IN %@)", [some_usernames valueForKey:@"username"]];
NSArray *remaining_usernames = [all_usernames filteredArrayUsingPredicate:predicate];
NSLog(@"%@", remaining_usernames);
打印
(
"Alpha: a",
"Alpha: z",
"Alpha: e"
)
我想添加另一个答案:
如果不需要对象的排序(并且很可能不需要相等的对象),您可以使用集合和集合算术来代替对数组使用谓词过滤。为此,我们必须教导 Alpha
相等的含义并提供哈希方法。在这种情况下,我们只使用 NSStrings 实现:
@implementation Alpha
-(instancetype) initWithUsername:(NSString *)username
{
self = [super init];
if (self) {
self.username = username;
}
return self;
}
-(NSString *)description{
return [NSString stringWithFormat:@"%@: %@", NSStringFromClass([self class]), self.username];
}
-(BOOL)isEqual:(id)object
{
return [self.username isEqual:[object username]];
}
-(NSUInteger)hash
{
return [self.username hash];
}
@end
NSArray *all_usernames = @[[[Alpha alloc] initWithUsername:@"a"],
[[Alpha alloc] initWithUsername:@"b"],
[[Alpha alloc] initWithUsername:@"z"],
[[Alpha alloc] initWithUsername:@"f"],
[[Alpha alloc] initWithUsername:@"e"]];
NSArray *some_usernames = @[[[Alpha alloc] initWithUsername:@"b"],
[[Alpha alloc] initWithUsername:@"f"]];
NSSet *allSet = [NSSet setWithArray:all_usernames];
NSSet *someSet = [NSSet setWithArray:some_usernames];
NSMutableSet *remainingSet = [allSet mutableCopy];
[remainingSet minusSet:someSet];
NSLog(@"%@", remainingSet);
打印
{(
Alpha: z,
Alpha: e,
Alpha: a
)}
此代码对于更多数据应该更快。请收看WWDC 2013: Designing Code for Performance