3

Using the answer here this method achieves something similar to ruby's map in obj-c:

- (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;
}

my question is how can i skip an object if something wrong happens while applying the block? Typically to skip something in an enumerator one just use the return command, however that's not an option in the method above, since the block is expected to return something.

In this example I use return to skip but get an error:

NSArray *mappedArray = [objArray mapObjectsUsingBlock:^(id obj, NSUInteger i) {
    // i don't want this obj to be included in final array
    // so I try to skip it
    return;   // ERROR:incompatible block pointer types sending 
              // 'void(^)(__strong id, NSUInteger)' to parameter of type 
              // 'id(^)(__strong id, NSUInteger)'


    // else do some processing
    return soupedUpObj;
}];

My current way of working around it is simply returning a null object, then removing them out from the final array. But I'm sure there must be a better way than that.

Community
  • 1
  • 1
abbood
  • 23,101
  • 16
  • 132
  • 246

3 Answers3

5

If the implementation is similar to what you showed above, it would make sense to just apply the block result to an intermediate value and then check it before adding it to the result array. Something like this:

- (NSArray *)mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block {
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:[self count]];
    [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        id blockResult = block( obj, idx );
        if( result != nil ){
          [result addObject:blockResult];
        }
    }];
    return result;
}
Jeff
  • 4,751
  • 5
  • 31
  • 35
  • interesting.. the obvious thing that i would have to add to your answer is the fact that i'm not simply checking for `nil`.. rather what i'm checking for depends on what constitutes `nil` or empty depending on the return value i'm using (using a block dictates that all return values be of the same type.. so if return an `NSMutableDictionary` in one case but then return `nil` in another.. it will complain of inconsistency in return value.. anyways lemme try it out! – abbood Aug 21 '13 at 15:56
  • You could even go farther to pass in an array (or other object) containing the values you want to ignore. – Jeff Aug 21 '13 at 16:15
1

One quick remedy: Write your own variant, which uses an NSPointerArray rather than NSArray. NSPointerArray can hold nil. Then order can be preserved, and you may use nil to indicate error (assuming NSNull is a valid value which cannot be used to indicate error). With the NSPointerArray, your block would just return nil.

justin
  • 104,054
  • 14
  • 179
  • 226
  • mmm.. wouldn't that freak out the guys looking at my code later on? i've never heard about `NSPointerArray` before, although it does seem to fit the bill here.. – abbood Aug 21 '13 at 16:12
  • @abbood just give the method which returns NSPointerArray a unique name and the correct return type. They should not freak out... unless you need it on iOS 5 :) and do create it so it holds strong references to elements, like NSArray. – justin Aug 21 '13 at 16:16
0

This is simply a limitation of whatever framework or library mapObjectsUsingBlock: comes from (it's not a standard Apple API).

Implementing array map functionality is not difficult however, so you can easily write your own version which handles nil return values from the block argument.

Mike Weller
  • 45,401
  • 15
  • 131
  • 151