Учитывая объект NSArray
of NSDictionary
(содержащий похожие объекты и ключи), можно ли написать выполнение карты в массив указанного ключа? Например, в Ruby это можно сделать с помощью:
array.map(&:name)
Учитывая объект NSArray
of NSDictionary
(содержащий похожие объекты и ключи), можно ли написать выполнение карты в массив указанного ключа? Например, в Ruby это можно сделать с помощью:
array.map(&:name)
Обновление: Если вы используете Swift, см. map.
BlocksKit - это вариант:
NSArray *new = [stringArray bk_map:^id(NSString *obj) {
return [obj stringByAppendingString:@".png"];
}];
Underscore - еще один вариант. Существует функция map
, вот пример с сайта:
NSArray *tweets = Underscore.array(results)
// Let 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 (со значениемForKeyPath, см. Операторы коллекции 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 Pods рекомендуется устанавливать другие библиотеки.
Я думаю, что 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%. Я отвечаю правильно, но на основе интерпретации, что 'map' делает что-то ко всему в массиве и создает новый массив с результатами, я думаю то, что вы, вероятно, хотите, это что-то вроде:
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
Плюс, его tagline - "дополнения ObjectiveC для людей. Ruby style". который должен хорошо подходить OP, -)
Самый распространенный случай использования - это отображение словаря, возвращаемого вызовом сервера, в массив более простых объектов, например. получение NSArray из NSString ID из ваших сообщений в NSDictionary:
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 вводит новую функцию map.
Вот пример из документации:
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"]
Функция карты принимает замыкание, которое возвращает значение любого типа и сопоставляет существующие значения в массиве экземплярам этого нового типа.