Equivalent to NSArray

Given an NSArray of NSDictionary (containing similar objects and keys), is it possible to write map execution to an array of the specified key? For example, in Ruby, this can be done with:

 array.map(&:name) 
+62
objective-c iphone cocoa-touch cocoa macos
May 25 '11 at 16:20
source share
10 answers

Update: If you are using Swift, see map .




BlocksKit is an option:

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



Underscore is another option. There is a map function, here is an example from the site:

 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; 
+12
Jun 27 '13 at 2:43 on
source share

It only saves a couple of lines, but I use a category in NSArray. You need to ensure that your block never returns zero, but in addition it stores time for cases when -[NSArray valueForKey:] will not work.

 @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 

Usage is very similar to -[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) 
+112
Aug 30 '11 at 18:50
source share

I don't know what this Ruby bit does, but I think you're looking for an implementation of NSArray - valueForKey:. This sends -valueForKey: each element of the array and returns an array of results. If the elements in the receiving array are NSDictionaries, -valueForKey: almost matches -objectForKey: It will work until the key starts with @

+73
May 25 '11 at 16:39
source share

To summarize all the other answers:

Ruby (as in the question):

 array.map{|o| o.name} 

Obj-C (with valueForKey ):

 [array valueForKey:@"name"]; 

Obj-C (with value ForKeyPath, see KVC Collection Operators ):

 [array valueForKeyPath:@"[collect].name"]; 

Obj-C (with enumerateObjectsUsingBlock ):

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

Swift (with map , see closures )

 array.map { $0.name } 

And there are several libraries that allow you to process arrays more functionally. Cocoa Pods is recommended to install other libraries.

+24
Mar 30 '15 at 10:22
source share

I think valueForKeyPath is a good choice.

Sitting below are very cool examples. Hope this will be helpful.

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

Example:

 NSArray *names = [allEmployees valueForKeyPath: @"[collect].{daysOff<10}.name"]; NSArray *albumCovers = [records valueForKeyPath:@"[collect].{artist like 'Bon Iver'}.<NSUnarchiveFromDataTransformerName>.albumCoverImageData"]; 
+6
Aug 08 '14 at 7:05
source share

I am not a Ruby expert, so I am not 100% sure. I answer correctly, but based on the interpretation that 'map' does something to everything in the array and creates a new array with the results, I think what you probably want is something like:

 NSMutableArray *replacementArray = [NSMutableArray array]; [existingArray enumerateObjectsUsingBlock: ^(NSDictionary *dictionary, NSUInteger idx, BOOL *stop) { NewObjectType *newObject = [something created from 'dictionary' somehow]; [replacementArray addObject:newObject]; } ]; 

So, you are using the new support for β€œblocks” (which are closures in a more general terms) in OS X 10.6 / iOS 4.0 to execute material in a block throughout the array. You decide to perform some operation and then add the result to a separate array.

If you are looking for support for 10.5 or iOS 3.x, you probably want to explore the issue of placing the appropriate code in the object and using makeObjectsPerformSelector: or, in the worst case, manually iterate the array using for(NSDictionary *dictionary in existingArray) .

+4
May 25 '11 at 16:40
source share
 @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 


A slight improvement on the two posted answers.

  • Checks for nil - you can use nil to delete objects when displayed
  • The name of the method better reflects the fact that the method does not change the array that it called on
  • It is rather a style, but I IMO improved the block argument names
  • Point syntax for counting
+2
Jan 26 '13 at 17:27
source share

For Objective-C, I would add the ObjectiveSugar library to this answer list: https://github.com/supermarin/ObjectiveSugar

Plus, its tagline is "ObjectiveC add-ons for people. Ruby style." which should fit the OP well ,-)

The most common use case is mapping a dictionary returned by a server call into an array of simpler objects, for example. getting NSArray from NSString ID from your messages in NSDictionary:

 NSArray *postIds = [results map:^NSString*(NSDictionary* post) { return [post objectForKey:@"post_id"]; }]; 
0
Feb 08 '17 at 12:48 on
source share

For Objective-C, I would add higher order functions to this list of answers: https://github.com/fanpyi/Higher-Order-Functions ;

There is a JSON array 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"]; }]; 
0
Jun 22 '17 at 10:36
source share

Swift introduces a new map function.

Here is an example from the documentation :

 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"] 

The map function takes a closure that returns a value of any type and maps the existing values ​​in the array to instances of this new type.

-one
Jun 04 '14 at 21:12
source share



All Articles