问题
I know I can check if a string contains another string like this
NSString *string = @"hello bla bla";
if ([string rangeOfString:@"bla"].location == NSNotFound) {
NSLog(@"string does not contain bla");
} else {
NSLog(@"string contains bla!");
}
But what if I have an NSArray *arary = @[@"one",@"two", @"three", @"four"] and I wanted to check if a string contains either one of these without just loop or have a bunch of or's (|| ). So it would be something like this
if (array contains one or two or three or four) {
//do something
}
But if I have a longer array this becomes tedious so is there another way, without just looping through?
EDIT
I want to check if myArray has any of theses values in valuesArray
valuesArray =@[@"one",@"two", @"three", @"four"];
myArray = [@"I have one head", @"I have two feet", @"I have five fingers"]
OUTPUT
outputArray = @[@"I have one head", @"I have two feet"]
回答1:
There you go:
NSArray* arrRet = [myArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id __nonnull evaluatedObject, NSDictionary<NSString *,id> * __nullable bindings) {
for(NSString* val in valuesArray) {
if ([evaluatedObject rangeOfString:val].location != NSNotFound)
return true;
}
return false;
}]];
arrRet contains exactly the two desired strings.
A little bit more magic later you have your code without writing a loop :P
NSArray* arrRet = [myArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary<NSString *,id> * bindings) {
BOOL __block match = false;
[valuesArray enumerateObjectsUsingBlock:^(id __nonnull obj, NSUInteger idx, BOOL * __nonnull stop) {
*stop = match = [evaluatedObject rangeOfString:obj].location != NSNotFound;
}];
return match;
}]];
回答2:
You could use a NSCompoundPredicate
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
Where your subPredicates must look like
(
SELF CONTAINS[c] "one",
SELF CONTAINS[c] "two",
SELF CONTAINS[c] "three",
SELF CONTAINS[c] "four"
)
To get there from
NSArray *array = @[@"one", @"two", @"three", @"four"]
You could use a for loop, but as you are opposed to that, let's cheat:
by using a category I each NSArray functional mapping, but instead of looping, I use enumerating
@interface NSArray (Map)
-(NSArray *) vs_map:(id(^)(id obj))mapper;
@end
@implementation NSArray (Map)
-(NSArray *)vs_map:(id (^)(id))mapper
{
NSMutableArray *mArray = [@[] mutableCopy];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id mapped = mapper(obj);
[mArray addObject:mapped];
}];
return [mArray copy];
}
@end
Now I can create the subPredicates like
NSArray *subPredicates = [arary vs_map:^id(NSString *obj) {
return [NSPredicate predicateWithFormat:@"SELF contains[c] %@", obj];
}];
and create the compound predicate like
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
and use it
BOOL doesContain = [predicate evaluateWithObject:string];
et voilà: No (obvious) looping, while there is one hidden in the enumeration and probably in the predicate as-well.
Now with the changed question you basically ask for filtering. You can use the same predicate for that:
NSArray *testarray = @[@"I have one head", @"I have two feet", @"I have five fingers"];
NSArray *arary = @[@"one",@"two", @"three", @"four"];
NSArray *subPredicates = [arary vs_map:^id(NSString *obj) {
return [NSPredicate predicateWithFormat:@"SELF contains[c] %@", obj];
}];
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
NSArray *results = [testarray filteredArrayUsingPredicate:predicate];
results now contains
(
I have one head,
I have two feet
)
the complete code
#import <Foundation/Foundation.h>
@interface NSArray (Map)
-(NSArray *) vs_map:(id(^)(id obj))mapper;
@end
@implementation NSArray (Map)
-(NSArray *)vs_map:(id (^)(id))mapper
{
NSMutableArray *mArray = [@[] mutableCopy];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id mapped = mapper(obj);
[mArray addObject:mapped];
}];
return [mArray copy];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *testarray = @[@"I have one head", @"I have two feet", @"I have five fingers"];
NSArray *arary = @[@"one",@"two", @"three", @"four"];
NSArray *subPredicates = [arary vs_map:^id(NSString *obj) {
return [NSPredicate predicateWithFormat:@"SELF contains[c] %@", obj];
}];
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
NSArray *results = [testarray filteredArrayUsingPredicate:predicate];
}
return 0;
}
回答3:
Besides my cheating the my other question, here an idea how really to avoid time costly looping: Use Set computation magic!
- created a class 'Sentence', instantiate it with the strings to test
- created a 'Word' class that takes a word to search for
- overwrite both classes'
isEqual:method to match for if a word is in the sentence (use sets there too!) - put those into an array.
- from this array create a
NS(*)Setobject - put all word in a set
- execute union on it.
来源:https://stackoverflow.com/questions/30987329/check-if-string-contains-any-string-from-array