NSArray Equivalent of Map

后端 未结 11 1771
萌比男神i
萌比男神i 2020-11-28 05:06

Given an NSArray of NSDictionary objects (containing similar objects and keys) is it possible to write perform a map to an array of specified key?

相关标签:
11条回答
  • 2020-11-28 05:44

    To summarize all other answers:

    Ruby (as in the question):

    array.map{|o| o.name}
    

    Obj-C (with valueForKey):

    [array valueForKey:@"name"];
    

    Obj-C (with valueForKeyPath, 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 a couple of libraries that allow you to handle arrays in a more functional way. CocoaPods is recommended to install other libraries.

    0 讨论(0)
  • 2020-11-28 05:49

    There is a special key-path operator for this: @unionOfObjects. Probably it replaced [collect] from previous versions.

    Imagine a Transaction class with payee property:

    NSArray *payees = [self.transactions valueForKeyPath:@"@unionOfObjects.payee"];
    

    Apple docs on Array Operators in Key-Value coding.

    0 讨论(0)
  • 2020-11-28 05:50

    It only saves a couple lines, but I use a category on NSArray. You need to ensure your block never returns nil, but other than that it's a time saver for cases where -[NSArray valueForKey:] won't 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 much like -[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)
    
    0 讨论(0)
  • 2020-11-28 05:53
    @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 upon a couple of the answers posted.

    1. Checks for nil—you can use nil to remove objects as you're mapping
    2. Method name better reflects that the method doesn't modify the array it's called on
    3. This is more a style thing but I've IMO improved the argument names of the block
    4. Dot syntax for count
    0 讨论(0)
  • 2020-11-28 05:55

    I'm no Ruby expert so I'm not 100% confident I'm answering correctly, but based on the interpretation that 'map' does something to everything in the array and produces 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're using the new support for 'blocks' (which are closures in more general parlance) in OS X 10.6/iOS 4.0 to perform the stuff in the block on everything in the array. You're choosing to do some operation and then add the result to a separate array.

    If you're looking to support 10.5 or iOS 3.x, you probably want to look into putting the relevant code into the object and using makeObjectsPerformSelector: or, at worst, doing a manual iteration of the array using for(NSDictionary *dictionary in existingArray).

    0 讨论(0)
提交回复
热议问题