0

本周我面临的挑战是与 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)??

我不太明白如何让它发挥作用,但为了自学,想知道那些在块和数组方面学得更多的人是否有更优雅的解决方案。

4

2 回答 2

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

如果一行中没有足够的对象,上面将引发异常。您可以事先检查它或让程序崩溃,这取决于您。

于 2013-04-19T15:28:21.967 回答
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(您在代码中称为)作为键。如有任何进一步的疑问,请随时通过评论询问任何澄清。

于 2013-04-19T15:28:43.837 回答