Variadic Lists and Blocks
Matt Gallagher posted an great article about veriadic lists. In his conclusion he writes
In your own code, variadic methods should be used sparingly — passing variables in an NSArray or NSDictionary is safer (if slightly slower and syntactically more verbose) due to the fact that these classes do offer introspection.
And I think he is right. But now I found a situation, where it makes much more sense to have a veridic list of arguments rather a NSArray or NSDictionary.
I wrote a Category method on NSArray, that allows the user, to filter the array by blocks and create a dictionary with given keys.
- (NSDictionary *) dictionaryByFilteringWithBlocks:(NSArray *)filterBlocks
forKeys:(NSArray *)keys
And it is used like
NSArray *blocks = [NSArray arrayWithObjects:
^BOOL(id element) {return [element hasPrefix:@"a"];},
^BOOL(id element) {return [element hasPrefix:@"c"];},
^BOOL(id element) {return [element hasPrefix:@"z"];},
nil];
NSArray *keys = [NSArray arrayWithObjects:@"a",@"c", @"z", nil];
NSDictionary *dict = [array dictionaryByFilteringWithBlocks:blocks forKeys:keys];
it works as expected, but has two flaws:
- the corresponding blocks and key are separated — I don't like that
- Xcode's code completion won't help you to create your blocks, as it will just tell you that you need two arrays
I think, that is reason enough to give veriadic methods a try — and here we go:
- (NSDictionary *) dictionaryByFilteringWithBlocksAndKeys:(BOOL (^)(id element))firstBlock, id firstKey,... NS_REQUIRES_NIL_TERMINATION;
-(NSDictionary *)dictionaryByFilteringWithBlocksAndKeys:(TestBlock)firstBlock, id firstKey,...
{
NSMutableDictionary *results = [NSMutableDictionary dictionary];
NSMutableArray *blocks = [[NSMutableArray alloc] initWithObjects:firstBlock, nil];
NSMutableArray *keys = [NSMutableArray arrayWithObject:firstKey];
va_list args;
va_start(args, firstKey);
while (YES) {
TestBlock block = va_arg(args, TestBlock);
if (! block)
break;
[blocks addObject:Block_copy(block)];
id key = va_arg(args, id);
if (!key) [NSException raise:@"wrong number of arguments" format:@"n blocks need n keys", nil];
[keys addObject:key];
}
va_end(args);
for(id key in keys){
NSMutableArray *filtered = [NSMutableArray array] ;
[results setObject:filtered forKey:key];
BOOL (^block)(id) = [blocks objectAtIndex:[keys indexOfObject:key]];
for (id element in self){
if (block(element)){
[filtered addObject:element];
}
}
}
for(BOOL (^block)(id) in blocks){
Block_release(block);
}
[blocks release];
return results;
}
NSArray *array = [NSArray arrayWithObjects:@"a", @"aa", @"ab", @"cc", @"cd", @"dd", nil];
NSDictionary *dict = [array dictionaryByFilteringWithBlocksAndKeys:
^BOOL(id element) {return [element hasPrefix:@"a"];},@"a",
^BOOL(id element) {return [element hasPrefix:@"c"];},@"c",
nil];
NSLog(@"%@", dict);
result:
{
a = (
a,
aa,
ab
);
c = (
cc,
cd
);
}