【问题标题】:filtering NSArray into a new NSArray in Objective-C在 Objective-C 中将 NSArray 过滤成一个新的 NSArray
【发布时间】:2010-09-11 17:45:01
【问题描述】:

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

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

有没有更好的办法?

【问题讨论】:

    标签: objective-c cocoa nsarray


    【解决方案1】:

    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" }
    

    【讨论】:

    • 我听了 Papa Smurf 的播客,Papa Smurf 说答案应该保存在 StackOverflow 中,以便社区可以对它们进行评分和改进。
    • @mmalc - 也许更合适,但在这里查看肯定更方便。
    • NSPredicate 已死,块万岁!参看。我的回答如下。
    • 包含[c] 是什么意思?我总是看到 [c] 但我不明白它的作用?
    • @user1007522,[c] 使匹配不区分大小写
    【解决方案2】:

    有很多方法可以做到这一点,但到目前为止,最简洁的肯定是使用[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.
    }]];
    

    我认为这已经很简洁了。


    斯威夫特:

    对于那些在 Swift 中使用 NSArrays 的人,您可能更喜欢这个简洁的版本:

    let filteredArray = array.filter { $0.shouldIKeepYou() }
    

    filter 只是 Array 上的一个方法(NSArray 隐式桥接到 Swift 的 Array)。它接受一个参数:一个闭包,它接受数组中的一个对象并返回一个Bool。在您的闭包中,只需为您想要过滤数组中的任何对象返回true

    【讨论】:

    • NSDictionary *bindings 在这里扮演什么角色?
    • @Kaitain 绑定是NSPredicate predicateWithBlock: API 所必需的。
    • @Kaitain bindings 字典可以包含模板的变量绑定。
    • 在处理复杂对象时比公认的答案有用得多:)
    【解决方案3】:

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

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

    【讨论】:

    • 最后一个答案不仅提到了使用块进行过滤,而且还给出了一个很好的例子来说明如何做到这一点。谢谢。
    • 我喜欢这个答案;尽管filteredArrayUsingPredicate 更精简,但您不使用任何谓词的事实有点模糊了目的。
    • 这是最现代的方法。就我个人而言,我渴望在某个时刻从 API 中消失的旧速记“objectsPassingTest”。这仍然运行得又快又好。我确实喜欢 NSPredicate - 但对于其他事情,需要更重的锤子
    • 你现在会收到错误Implicit conversion of 'NSUInteger' (aka 'unsigned long') to 'NSIndexSet * _Nonnull' is disallowed with ARC... 它需要 NSIndexSets
    • @anoop4real,我认为您提到的警告的原因是您错误地使用了indexOfObjectPassingTest 而不是indexesOfObjectsPassingTest。容易错过,但差别很大:)
    【解决方案4】:

    如果您是 OS X 10.6/iOS 4.0 或更高版本,则使用块可能比使用 NSPredicate 更好。查看-[NSArray indexesOfObjectsPassingTest:] 或编写您自己的类别以添加方便的-select:-filter: 方法(example)。

    希望其他人编写该类别、对其进行测试等?查看BlocksKit (array docs)。还有 许多 更多示例可以通过搜索来找到,例如"nsarray block category select".

    【讨论】:

    • 你介意用一个例子来扩展你的答案吗?博客网站往往会在您最需要它们时消亡。
    • 防止链接腐烂的保护是从链接的文章中摘录相关代码等,而不是添加更多链接。保留链接,但添加一些示例代码。
    • @ClayBridges 我发现这个问题正在寻找过滤可可的方法。由于您的答案中没有任何代码示例,因此我花了大约 5 分钟的时间来挖掘您的链接,以发现这些块无法满足我的需要。如果代码在答案中,则可能需要 30 秒。如果没有实际的代码,这个答案的用处要小得多。
    • @mydogisbox 等:这里只有 4 个答案,因此有足够的空间来提供您自己的闪亮且出色的代码采样答案。我对我的很满意,所以请别管它。
    • mydogisbox 在这一点上是正确的;如果您所做的只是提供一个链接,那么即使您添加更多链接,它也不是一个真正的答案。见meta.stackexchange.com/questions/8231。试金石:您的答案能否独立存在,还是需要单击链接才能具有任何价值? 特别是,您说“使用块可能比使用 NSPredicate 更好”但你并没有真正解释原因。
    【解决方案5】:

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

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

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

    然后无论您要过滤:

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

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

    【讨论】:

    • 我喜欢这个答案,因为它优雅而简短,并且减少了再次学习如何为最基本的东西形式化 NSPredicate 的需要 - 访问属性和布尔方法。我什至认为不需要包装器。一个简单的 [myArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"myMethod = TRUE"]];就足够了。谢谢! (我也喜欢其他选择,但这个很好)。
    【解决方案6】:

    NSPredicate 是 nextstep 构造条件以过滤集合的方式(NSArrayNSSetNSDictionary)。

    例如考虑两个数组arrfilteredarr

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];
    
    filteredarr = [NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];
    

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

    为了方便记住那些很少有sql背景的人

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

    1)select * from tbl --> 集合

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

    3)select * from tbl where column1 like '%a%' -->

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

    希望对你有帮助

    【讨论】:

      【解决方案7】:

      查看这个库

      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;
      }];
      

      【讨论】:

        【解决方案8】:

        最好和最简单的方法是创建这个方法并传递数组和值:

        - (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;
        }
        

        【讨论】:

          【解决方案9】:

          您可以使用的另一种分类方法:

          - (NSArray *) filteredArrayUsingBlock:(BOOL (^)(id obj))block {
              NSIndexSet *const filteredIndexes = [self indexesOfObjectsPassingTest:^BOOL (id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
                                                 return block(obj);
                                             }];
          
              return [self objectsAtIndexes:filteredIndexes];
          }
          

          【讨论】:

            猜你喜欢
            • 2019-06-29
            • 1970-01-01
            • 1970-01-01
            • 2023-03-29
            • 1970-01-01
            • 2010-11-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多