使用enumerateObjectsUsingBlock实现更优雅的NSArray合并:?

时间:2013-04-19 15:07:08

标签: objective-c nsarray objective-c-blocks

本周我的挑战是与objective-c中的块一起达成协议。我的脑子里有一些关于语法的东西。到那里。

我有以下代码以特定方式实现两个数组的合并(请参阅下面的代码中的注释)。

NSArray *keys = @[@"name", @"age"];
NSArray *data = @[
                  @[@"mark", @"36 years"],
                  @[@"matt", @"35 years"],
                  @[@"zoe", @"7 years"]
                  ];

// desired outcome is
// @ {  @"name" : @[@"mark", @"matt", @"zoe"],
//      @"age"  : @[@"36 years", @"35 years", @"7 years"]
//   }

NSMutableArray *mergedData = [[NSMutableArray alloc] initWithCapacity:keys.count];

for (NSString *key in keys) {
    NSLog(@"key: %@", key);
    NSInteger keyIndex = [keys indexOfObject:key];
    NSMutableArray *dataItemsForKey = [[NSMutableArray alloc] initWithCapacity:data.count];
    for (NSArray *row in data) {
        // double check the array count for row equals the expected count for keys - otherwise we have a 'match up' issue
        if (row.count == keys.count) {
            [dataItemsForKey addObject:[row objectAtIndex:keyIndex]];
        }
    }
    [mergedData addObject:[NSDictionary dictionaryWithObject:dataItemsForKey forKey:key]];
}
NSLog (@"mergedData: %@", mergedData);

虽然这段代码运行良好,但为了我的挑战和学习,我想知道是否有一个更“优雅”(也就是更少的代码,更容易阅读)的方式来使用enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)

我无法看到让它发挥作用的方法,但为了自我教育的利益,想知道那些在块和阵列中学到的东西是否可能有更优雅的解决方案。

2 个答案:

答案 0 :(得分:1)

首先要说的是你的代码没有产生所需的输出。你得到一个包含两个词典的数组,每个词典都有一个键。

解决问题的一种方法是:

NSMutableDictionary* mergedData = [[NSMutableDictionary alloc] init];

[keys enumerateObjectsUsingBlock: ^(id key, NSUInteger keyIndex, BOOL *stop)
{
    NSMutableArray* keyValues = [[NSMutableArray alloc] init];
    for (NSArray* row in data)
    {
        [keyValues addObject: [row objectAtIndex: keyIndex]];
    }
    [mergedData setObject: keyValues forKey: key];
}];

如果行中没有足够的对象,则上面会抛出异常。您可以事先检查它或允许程序崩溃,这取决于您。

答案 1 :(得分:1)

我注意到的第一个问题是在枚举数组时要求当前对象的索引。这是浪费操作,因为在每次循环迭代时,您必须查看所有数组元素(可能是O(N))以查找对象的位置。

你可以这样做:

for(NSUInteger i=0; i<keys.count; i++)
{
    NSString* key= keys[i];
    <Rest of the code>
}

或者只是跟踪手动递增的索引:

NSUInteger i=0;
for (NSString *key in keys)
{
    <Your code>
    i++;
}

或者你想要的,使用 enumerateObjectsUsingBlock:,这是IMO在这种情况下最优雅的方式。这是一个例子:

NSMutableDictionary* dict=[NSMutableDictionary new];
[keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
    NSMutableArray* fields=[NSMutableArray new];
    for(NSArray* array in data)
    {
        [fields addObject: array[idx]];
    }
    [dict setObject: fields forKey: obj];
}];

如果您还不了解它的工作原理,可以进一步解释:

这样,在每次执行块时,您都可以知道哪个是当前对象( obj )和它的索引( idx )。 stop 仅用于停止枚举数组,但在这种情况下你不需要它(比如你要停止枚举,你设置* stop = YES)。在我的代码中,我只是将每个元素都放在 data 的索引 idx 中,然后构建一个数组,这是我放入字典的值,它有 obj (您在代码中称为 key )作为键。如有任何疑问,请随时通过评论提出任何澄清。