Списка NSArray эквивалент карте
предоставлена NSArray
of NSDictionary
объекты (содержащие аналогичные объекты и ключи) можно ли записать выполнить сопоставление с массивом указанного ключа? Например, в Ruby это можно сделать с помощью:
array.map(&:name)
10 ответов:
обновление: если вы используете Swift, см. карта.
BlocksKit вариант:
NSArray *new = [stringArray bk_map:^id(NSString *obj) { return [obj stringByAppendingString:@".png"]; }];
подчеркивание это еще один вариант. Там есть
он сохраняет только пару строк, но я использую категорию на 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)
Я понятия не имею, что этот кусок Рубина делает, но я думаю вы ищете реализацию NSArray -valueForKey:. Об этом передает
-valueForKey:
к каждому элементу массива и возвращает массив результатов. Если элементы в принимающем массиве являются NSDictionaries,-valueForKey:
Это почти то же самое, что-objectForKey:
. Он будет работать до тех пор, пока ключ не начинается с@
чтобы суммировать все остальные ответы:
Рубин (как в вопросе):
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 { .name }
и, есть несколько библиотеки, которые позволяют обрабатывать массивы более функциональным способом. Стручки Какао рекомендуется установить другие библиотеки.
Я думаю, что 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
небольшое улучшение на пару ответов написал.
- проверяет nil-вы можете использовать nil для удаления объектов при отображении
- имя метода лучше отражает, что метод не изменяет массив, который он вызывает на
- это больше стиль, но я IMO улучшил имена аргументов блока
- точечный синтаксис для count
для Objective-C я бы добавил библиотеку ObjectiveSugar в этот список ответов:https://github.com/supermarin/ObjectiveSugar
плюс, его слоган "ObjectiveC дополнения для людей. Рубиновый стиль."что должно хорошо устраивать ОП; -)
мой наиболее распространенный вариант использования-это сопоставление словаря, возвращаемого вызовом сервера, с массивом более простых объектов, например, получение NSArray идентификаторов NSString из ваших сообщений 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 вводит новый карта.
здесь пример из документации:
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 принимает замыкание, которое возвращает значение любого типа и сопоставляет существующие значения в массив экземпляров этого нового типа.