地图的NSArray等价物
给定一个NSArray
的NSDictionary
对象(包含相似的对象和键)是否可以写一个指定的键的数组执行映射? 例如,在Ruby中可以通过以下方式完成:
array.map(&:name)
更新:如果你正在使用Swift,请看地图 。
BlocksKit是一个选项:
NSArray *new = [stringArray bk_map:^id(NSString *obj) { return [obj stringByAppendingString:@".png"]; }];
下划线是另一种select。 有一个map
function,这里是一个来自网站的例子:
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;
它只保存了几行,但是我在NSArray上使用了一个类别。 你需要确保你的块永远不会返回零,但除此之外,对于-[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
用法很像-[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)
我不知道Ruby有什么用,但我认为你正在寻找NSArray的-valueForKey:的实现。 这将向数组的每个元素发送-valueForKey:
并返回一个结果数组。 如果接收数组中的元素是NSDictionaries,则-valueForKey:
与-objectForKey:
几乎相同。 只要密钥不以@
开头,
总结所有其他答案:
Ruby(如在问题中):
array.map{|o| o.name}
Obj-C(带有valueForKey ):
[array valueForKey:@"name"];
Obj-C(带有valueForKeyPath,请参阅KVC集合运算符 ):
[array valueForKeyPath:@"[collect].name"];
Obj-C(带有enumerateObjectsUsingBlock ):
NSMutableArray *newArray = [NSMutableArray array]; [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [newArray addObject:[obj name]]; }];
斯威夫特(有地图 ,见封闭 )
array.map { $0.name }
而且,还有一些库允许你以更有效的方式处理数组。 cocoa豆推荐安装其他库。
我认为valueForKeyPath是一个不错的select。
坐在下面有非常酷的例子。 希望它是有帮助的。
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"];
我不是Ruby的专家,所以我不是100%自信,我正确回答,但基于“映射”对数组中的所有内容做了一些事情,并产生一个新的数组与结果,我想你可能想要的是这样的:
NSMutableArray *replacementArray = [NSMutableArray array]; [existingArray enumerateObjectsUsingBlock: ^(NSDictionary *dictionary, NSUInteger idx, BOOL *stop) { NewObjectType *newObject = [something created from 'dictionary' somehow]; [replacementArray addObject:newObject]; } ];
所以你在OS X 10.6 / iOS 4.0中使用新的'blocks'支持(这是更为一般的说法),在数组中的所有内容块中执行这些东西。 你select做一些操作,然后将结果添加到一个单独的数组。
如果你想要支持10.5或者iOS 3.x,你可能需要考虑把相关的代码放入对象中,并使用makeObjectsPerformSelector:或者,最坏的情况是使用for(NSDictionary *dictionary in existingArray)
。
@implementation NSArray (BlockRockinBeats) - (NSArray*)mappedWithBlock:(id (^)(id obj, NSUInteger idx))block { NSMutableArray* result = [NSMutableArray arrayWithCapacity:self.count]; [self enumerateObjectsUsingBlock:^(id currentObject, NSUInteger index, BOOL *stop) { id mappedCurrentObject = block(currentObject, index); if (mappedCurrentObject) { [result addObject:mappedCurrentObject]; } }]; return result; } @end
发布了几个答案略有改善。
- 检查零 – 你可以使用零来删除对象,因为你正在映射
- 方法名称更好地反映了该方法不会修改它所调用的数组
- 这是更具风格的东西,但我已经改善了块的参数名称
- 计数的点语法
对于Objective-C,我会将ObjectiveSugar库添加到这个答案列表中: https : //github.com/supermarin/ObjectiveSugar
另外,它的标语是“ObjectiveC为人类增加了ruby风格”。 这应该适合OP 😉
我最常见的用例是将由服务器调用返回的字典映射到一个简单对象数组,例如从NSDictionarypost获取NSString ID的NSArray:
NSArray *postIds = [results map:^NSString*(NSDictionary* post) { return [post objectForKey:@"post_id"]; }];
对于Objective-C,我将高级函数添加到这个答案列表: https : //github.com/fanpyi/Higher-Order-Functions ;
有一个像这样的JSON数组studentJSONList:
[ {"number":"100366","name":"Alice","age":14,"score":80,"gender":"female"}, {"number":"100368","name":"Scarlett","age":15,"score":90,"gender":"female"}, {"number":"100370","name":"Morgan","age":16,"score":69.5,"gender":"male"}, {"number":"100359","name":"Taylor","age":14,"score":86,"gender":"female"}, {"number":"100381","name":"John","age":17,"score":72,"gender":"male"} ] //studentJSONList map to NSArray<Student *> NSArray *students = [studentJSONList map:^id(id obj) { return [[Student alloc]initWithDictionary:obj]; }]; // use reduce to get average score NSNumber *sum = [students reduce:@0 combine:^id(id accumulator, id item) { Student *std = (Student *)item; return @([accumulator floatValue] + std.score); }]; float averageScore = sum.floatValue/students.count; // use filter to find all student of score greater than 70 NSArray *greaterthan = [students filter:^BOOL(id obj) { Student *std = (Student *)obj; return std.score > 70; }]; //use contains check students whether contain the student named 'Alice' BOOL contains = [students contains:^BOOL(id obj) { Student *std = (Student *)obj; return [std.name isEqual:@"Alice"]; }];
Swift引入了一个新的地图function。
以下是文档中的一个示例 :
let digitNames = [ 0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine" ] let numbers = [16, 58, 510] let strings = numbers.map { (var number) -> String in var output = "" while number > 0 { output = digitNames[number % 10]! + output number /= 10 } return output } // strings is inferred to be of type String[] // its value is ["OneSix", "FiveEight", "FiveOneZero"]
map函数使用一个闭包,它返回任何types的值,并将数组中的现有值映射到这个新types的实例。