NSArray等效地图

Stu*_*ssa 71 iphone macos cocoa cocoa-touch objective-c

给定一个NSArrayNSDictionary对象(含类似的对象和密钥)是有可能写出执行地图指定的键的阵列?例如,在Ruby中,它可以通过以下方式完成:

array.map(&:name)
Run Code Online (Sandbox Code Playgroud)

Jus*_*son 125

它只保存了几行,但我在NSArray上使用了一个类别.您需要确保您的块永远不会返回nil,但除此之外,对于-[NSArray valueForKey:]无法工作的情况,它可以节省时间.

@interface NSArray (Map)

- (NSArray *)mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block;

@end

@implementation NSArray (Map)

- (NSArray *)mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block {
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:[self count]];
    [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        [result addObject:block(obj, idx)];
    }];
    return result;
}

@end
Run Code Online (Sandbox Code Playgroud)

用法很像-[NSArray enumerateObjectsWithBlock:]:

NSArray *people = @[
                     @{ @"name": @"Bob", @"city": @"Boston" },
                     @{ @"name": @"Rob", @"city": @"Cambridge" },
                     @{ @"name": @"Robert", @"city": @"Somerville" }
                  ];
// per the original question
NSArray *names = [people mapObjectsUsingBlock:^(id obj, NSUInteger idx) {
    return obj[@"name"];
}];
// (Bob, Rob, Robert)

// you can do just about anything in a block
NSArray *fancyNames = [people mapObjectsUsingBlock:^(id obj, NSUInteger idx) {
    return [NSString stringWithFormat:@"%@ of %@", obj[@"name"], obj[@"city"]];
}];
// (Bob of Boston, Rob of Cambridge, Robert of Somerville)
Run Code Online (Sandbox Code Playgroud)

  • http://www.mikeash.com/pyblog/friday-qa-2009-08-14-practical-blocks.html包含上述更好的(语法)变体,恕我直言. (6认同)
  • 你正在返回一个 `MutableArray`。做`return [result copy]`是更好的做法吗? (2认同)

Jer*_*myP 74

我不知道Ruby的功能是什么,但我认为你正在寻找NSArray的-valueForKey实现:.这将发送-valueForKey:到数组的每个元素并返回结果数组.如果接收数组中的元素是NSDictionaries,-valueForKey:则几乎相同-objectForKey:.只要密钥不以一个开头,它就会工作@

  • +1; 这是处理问题提示者描述的特定(相当常见)用例的最简洁方法,即使它不是问题标题中要求的完全灵活的映射函数.对于真正需要`map`方法的人来说,请使用Justin Anderson的答案中的类别. (3认同)
  • 实际上,这个解决方案比你想象的要灵活得多,因为`valueForKey:`通过调用相应的getter来工作.因此,如果您事先知道对象要做的各种事情,您可以将所需的getter注入其中(例如使用类别),然后使用NSArray的`valueForKey:`作为将调用传递给特定的方法通过数组获取每个对象并获取结果数组. (2认同)

tot*_*rio 35

总结所有其他答案:

Ruby(如问题所示):

array.map{|o| o.name}
Run Code Online (Sandbox Code Playgroud)

Obj-C(带valueForKey):

[array valueForKey:@"name"];
Run Code Online (Sandbox Code Playgroud)

Obj-C(带valueForKeyPath,参见KVC集合运算符):

[array valueForKeyPath:@"[collect].name"];
Run Code Online (Sandbox Code Playgroud)

Obj-C(使用enumerateObjectsUsingBlock):

NSMutableArray *newArray = [NSMutableArray array];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
     [newArray addObject:[obj name]];
}];
Run Code Online (Sandbox Code Playgroud)

Swift(带地图,见封闭)

array.map { $0.name }
Run Code Online (Sandbox Code Playgroud)

并且,有几个库允许您以更多功能的方式处理数组. 建议Cocoa Pods安装其他库.


Sen*_*ful 19

更新:如果您使用的是Swift,请参阅地图.


BlocksKit是一个选项:

NSArray *new = [stringArray bk_map:^id(NSString *obj) { 
    return [obj stringByAppendingString:@".png"]; 
}];
Run Code Online (Sandbox Code Playgroud)

Underscore是另一种选择.有一个map功能,这是网站上的一个例子:

NSArray *tweets = Underscore.array(results)
    // Let's make sure that we only operate on NSDictionaries, you never
    // know with these APIs ;-)
    .filter(Underscore.isDictionary)
    // Remove all tweets that are in English
    .reject(^BOOL (NSDictionary *tweet) {
        return [tweet[@"iso_language_code"] isEqualToString:@"en"];
    })
    // Create a simple string representation for every tweet
    .map(^NSString *(NSDictionary *tweet) {
        NSString *name = tweet[@"from_user_name"];
        NSString *text = tweet[@"text"];

        return [NSString stringWithFormat:@"%@: %@", name, text];
    })
    .unwrap;
Run Code Online (Sandbox Code Playgroud)

  • 恕我直言,使用Objective-C方法的点语法不是一个好主意.这不是Ruby. (8认同)
  • 除非你在整个地方进行功能操作,否则使用NSArray的标准mapObjectsUsingBlock:或valueForKey:方法(在下面的答案中建议)是一个更好的主意.避免使用更多"丑陋"的Objective-C语法字符并不能成为添加Cocoapods依赖项的理由. (7认同)
  • mapObjectsUsingBlock:不是标准函数,而是另一个答案建议的扩展 (2认同)

Ste*_*ang 6

我认为valueForKeyPath是一个不错的选择.

坐在下面有非常酷的例子.希望它有所帮助.

http://kickingbear.com/blog/archives/9

一些例子:

NSArray *names = [allEmployees valueForKeyPath: @"[collect].{daysOff<10}.name"];
NSArray *albumCovers = [records valueForKeyPath:@"[collect].{artist like 'Bon Iver'}.<NSUnarchiveFromDataTransformerName>.albumCoverImageData"];
Run Code Online (Sandbox Code Playgroud)