给定一个NSArray
的NSDictionary
对象(含类似的对象和密钥)是有可能写出执行地图指定的键的阵列?例如,在Ruby中,可以使用:
array.map(&:name)
Answers:
更新:如果您使用的是Swift,请参见map。
BlocksKit是一个选项:
NSArray *new = [stringArray bk_map:^id(NSString *obj) {
return [obj stringByAppendingString:@".png"];
}];
下划线是另一种选择。有一个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;
mapObjectsUsingBlock:
不是标准函数,而是另一个答案建议的扩展
它只保存几行,但我在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
用法很像-[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)
mapObjectsUsingBlock
。如果您传入nil块,它将当前崩溃。
MutableArray
。这是更好的做法return [result copy]
吗?
NSNull
。这就是Obj-C对象,用于表示集合类中的nil。NSNull
因为Obj-C不会取消装箱,所以这种用法非常不常见且麻烦NSNull != nil
。但是,如果您只想从数组中过滤掉某些项目,则可以进行修改mapObjectsUsingBlock
以检查是否有零响应,并跳过它们。
我不知道Ruby会做什么,但是我认为您正在寻找NSArray的-valueForKey:的实现。这将发送-valueForKey:
到数组的每个元素,并返回结果数组。如果接收数组中的元素是NSDictionaries,-valueForKey:
则与几乎相同-objectForKey:
。只要键不是以@
valueForKey:
可以通过调用相应的getter来工作。因此,如果您事先知道您可能希望对象执行的各种操作,则可以将所需的吸气剂注入到其中(例如,使用类别),然后使用NSArrayvalueForKey:
作为通过数组将调用传递给特定吸气剂的方式到每个对象并获得结果数组。
map
方法的人,请使用贾斯汀·安德森答案中的类别。
总结所有其他答案:
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 }
而且,有几个库可让您以更实用的方式处理数组。建议使用CocoaPods安装其他库。
我认为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"];
我不是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中对“块”(更一般的说法是闭包)的新支持,以对数组中的所有内容执行块中的内容。您选择执行一些操作,然后将结果添加到单独的数组中。
如果您希望支持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 ;-)
我最常见的用例是将服务器调用返回的字典映射到较简单对象的数组,例如,从您的NSDictionary帖子中获取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"];
}];
为此有一个特殊的键路径运算符:@unionOfObjects
。可能是[collect]
从以前的版本替代的。
想象一个Transaction
具有payee
属性的类:
NSArray *payees = [self.transactions valueForKeyPath:@"@unionOfObjects.payee"];
Apple关于键值编码中的数组运算符的文档。
Swift引入了新的地图功能。
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函数采用闭包,该闭包返回任何类型的值,并将数组中的现有值映射到此新类型的实例。
[1,2,3].map {$0 * 2} //=> [2,4,6]
。AND,问题是关于Obj-C NSArray,而不是Swift;)