检查字符串是否包含数组中的任何一个字符串

3

我知道可以像这样检查一个字符串是否包含另一个字符串

NSString *string = @"hello bla bla";
if ([string rangeOfString:@"bla"].location == NSNotFound) {
  NSLog(@"string does not contain bla");
} else {
  NSLog(@"string contains bla!");
}

但是如果我有一个NSArray *arary = @[@"one",@"two", @"three", @"four"],我想检查一个字符串是否包含其中任意一个,而不只是循环或拥有一堆或运算符(||)。所以代码应该是这样的:

if (array contains one or two or three or four) {
//do something
}

但如果我的数组长度很长,这样做会变得很繁琐,那么是否有其他方法,而不是仅仅通过循环来完成呢?

编辑

我想检查myArray中是否有valuesArray中的任何一个值。

valuesArray =@[@"one",@"two", @"three", @"four"];
myArray = [@"I have one head", @"I have two feet", @"I have five fingers"]

输出

outputArray = @[@"I have one head", @"I have two feet"]

2
你不想使用循环的原因是什么? - luk2302
1
这需要一个循环,你不会找到更高效的解决方案,因为基本上你必须检查数组中的每个元素。 - luk2302
1
没关系。你必须循环遍历你想要测试的字符串,每次比较都是一个循环比较字符串。你可以将其抽象出来,但无论你调用什么函数或使用正则表达式还是其他方法,都会执行循环。你有测量过并确保这对你的应用程序实际上是一个性能问题吗?N有多大? - i_am_jorf
1
即使你在不编写循环的情况下找到了解决方案,你也可以肯定该方法内部会循环遍历数组——否则你如何检查每个值?“2个循环”和“对数组运行它”是什么意思? - luk2302
1
如果可能的话,为什么不将数组存储到NSDictionary中或进行缓存呢?您可以检查键/值对是否存在,这将是一个常数时间操作。随着您要检查的值越来越多,这将变得更加重要。 - JJC
显示剩余10条评论
3个回答

4

请看下面:

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 包含了恰好两个所需的字符串。

稍微加点魔法,您就能够在不写循环的情况下完成代码 :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;
}]];

嗯,我认为我的映射是更好的作弊方式,因为它还隐藏了枚举。 - vikingosegundo
@vikingosegundo,你有没有想过使用谓词是否会提高性能?只是出于好奇。 - luk2302
我对谓词性能不是很了解,但说实话:无论如何,它仍然比你的神经元快得多。如果你真的想提高性能,请看看我刚刚发布的另一个答案:使用集合算术。 - vikingosegundo
@vikingosegundo 嗯,我认为对于他的用例来说,两个版本都有点过度设计了,至少是关于集合算术的那个。至于性能,我指的是扩大规模 - 如果他测试1000个字符串来寻找100个子字符串,会发生什么等等。我太懒了不想尝试 :P 但谓词答案看起来确实很有趣 +1 :) - luk2302
只是为了消除对几百个字符串的for循环而在Stackoverflow上发布问题已经过度优化了!这个问题和答案都有过早优化的问题。 - vikingosegundo
我该如何获取匹配项的索引? - Jevgenij Kononov

2
您可以使用NSCompoundPredicate。
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];

您的子谓词必须符合以下格式:
(
    SELF CONTAINS[c] "one",
    SELF CONTAINS[c] "two",
    SELF CONTAINS[c] "three",
    SELF CONTAINS[c] "four"
)

从那里开始,

要到达那里

NSArray *array = @[@"one", @"two", @"three", @"four"]

你可以使用for循环,但既然你对此反感,让我们来“作弊”一下:通过使用每个NSArray的类别I功能映射,但不是使用循环,而是使用枚举。
@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

现在我可以创建子谓词,如下所示:
NSArray *subPredicates = [arary vs_map:^id(NSString *obj) {
        return [NSPredicate predicateWithFormat:@"SELF contains[c] %@", obj];
}];

并创建类似的复合谓词
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];

并使用它。

BOOL doesContain = [predicate evaluateWithObject:string];

现在,针对这个问题,您基本上是要求过滤。您可以使用相同的谓词进行过滤:

et voilà:没有(明显的)循环,尽管枚举中隐藏着一个循环,可能也存在于谓词中。


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 现在包含

(
    I have one head,
    I have two feet
)

完整代码
#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;
}

0

除了我作弊的问题,这里有一个真正避免耗时循环的想法:使用集合计算魔法!

  • 创建一个名为“Sentence”的类,用要测试的字符串进行实例化
  • 创建一个名为“Word”的类,用要搜索的单词进行实例化
  • 覆盖这两个类的isEqual:方法,以匹配句子中是否有单词(也在那里使用集合!)
  • 将它们放入数组中。
  • 从此数组创建一个NS(*)Set对象
  • 将所有单词放入一个集合中
  • 执行联合操作。

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