从包含NSDictionary的NSArray的NSArray中提取属性

4

这个问题类似于从对象数组中提取属性,但是需要更深层次的提取。

为了简单起见,在下面的示例中,我所指的对象是NSStrings。

我有两个要解决的情况。

带有对象的NSArray的NSArray of NSDictionary

最好使用键值编码,从以下结构中,我想将所有的“title”提取到一个可枚举列表中:

NSArray *list1 = @[
    @[
        @{@"title":@"A", @"description":...},
        @{@"title":@"B", @"description":...},
    ],
    @[
        @{@"title":@"C", @"description":...},
        @{@"title":@"D", @"description":...},
    ],
];

期望的结果是,例如:

@[@"A", @"B", @"C", @"D"]

NSArray包含NSDictionary和NSArray中的对象

最好使用键值编码,从以下结构中,我想提取"titles"列表并将其合并为一个可枚举的列表:

NSArray *list2 = @[
    @{
        @"titles":@[
            @"A",
            @"B",
        ],
        @"descriptions":...,
    },
    @{
        @"titles":@[
            @"C",
            @"D",
        ],
        @"descriptions":...,
    },
];

期望的结果可以举例说明:
@[@"A", @"B", @"C", @"D"]

注意事项

  • 我的实际情况涉及到第一个列表中的NSOrderedSet对象的NSOrderedSet,以及第二个列表中带有NSOrderedSet对象的NSDictionaryNSOrderedSet,但我认为这不会影响答案。

  • 我写过我更喜欢使用键值编码,但这不是必须的。如果可能的话,我只想避免编写for (... in ...)enumateObjectWithBlock:

  • 再次强调,这并不重要,但对象是来自获取请求的NSManagedObject。所以我知道我可以直接优化获取请求,但我仍然想知道是否有好的替代方案。

2个回答

3

KVC可以通过名为@unionOfArrays集合运算符在此情况下确实达到您的目的。该运算符的一个效果是将数组展平,因此您的第一个示例非常简单。

[list1 valueForKeyPath:@"@unionOfArrays.title"]

第二种方法非常类似,但需要按相反的顺序进行。首先提取所有的titles数组,然后将它们展平。
[list2 valueForKeyPath:@"titles.@unionOfArrays.self"]

"

self是必需的,尽管它看起来多余,因为根据上面链接的文档:

除了@count之外的所有集合运算符都需要在集合运算符右侧使用键路径。

对于NSOrderedSet,似乎可以在键路径中使用其array属性将内部集合转换后再操作它们,但由于某些原因,这会产生错误。然而,我在GitHub上Nicolas Bouilleaud发布的有趣的小贴士中发现了这一点:

"
// Convert each OrderedSet to an Array to mute the error.
NSLog(@"union : %@",[data valueForKeyPath:@"@distinctUnionOfArrays.values.@array"]);

这个奇怪的 @array 运算符 适用于你的 NSOrderedSet 示例输入:

NSOrderedSet *list1 = [NSOrderedSet orderedSetWithObjects:[NSOrderedSet orderedSetWithArray:@[ @{@"title":@"A"}, @{@"title":@"B"} ]], [NSOrderedSet orderedSetWithArray:@[ @{@"title":@"C"}, @{@"title":@"D"} ]], nil];

// Note also converting outer set to array first    
NSLog(@"%@", [list1.array valueForKeyPath:@"@unionOfArrays.title.@array"]);

NSOrderedSet *list2 = [NSOrderedSet orderedSetWithArray:@[ @{ @"titles":[NSOrderedSet orderedSetWithObjects: @"A", @"B", nil] }, @{ @"titles":[NSOrderedSet orderedSetWithObjects: @"C", @"D", nil] } ]];

// Note also converting outer set to array first
NSLog(@"%@", [list2.array valueForKeyPath:@"titles.@unionOfArrays.self.@array"]);

但是我不知道为什么。我想不出这是从哪里来的,也不知道它在做什么(特别是为什么在结尾)。

不错。有没有支持NSOrderedSet的想法? - Cœur
如果您需要在结果中消除重复项,请使用@distinctUnionOfArrays。 无论是NSArray还是NSSet,都可以使用其中任何一个。 - jscs
对于“外部容器”,是的,一个简单的转换就足够了。 但是对于作为NSOrderedSet的“内部容器”,我已经尽力了,但无法找到符合KVC规范的语法。请查看我的回答。 - Cœur
我更新了一些关于内部有序集的信息,@Cœur。 - jscs
如果我找到更可靠的解释,我会进行更新;确实可能会出现意外情况,但也可能不会。我想@array本身就可以成为一个很好的SO问题... - jscs
显示剩余3条评论

1

谢谢Josh关于NSArray的回答。当我提出问题时,我使用NSArray来描述它,因为使用现代的@[]语法更容易书写。我想不到NSArray的最合适答案会与我的真实情况NSOrderedSet不兼容。

所以,我的真实情况更接近于:

NSOrderedSet *list1 = [NSOrderedSet orderedSetWithObjects:[NSOrderedSet orderedSetWithArray:@[ @{@"title":@"A"}, @{@"title":@"B"} ]], [NSOrderedSet orderedSetWithArray:@[ @{@"title":@"C"}, @{@"title":@"D"} ]], nil];
NSOrderedSet *list2 = [NSOrderedSet orderedSetWithArray:@[ @{ @"titles":[NSOrderedSet orderedSetWithObjects: @"A", @"B", nil] }, @{ @"titles":[NSOrderedSet orderedSetWithObjects: @"C", @"D", nil] } ]];

我能找到的关于NSOrderedSet的唯一解决方案是循环遍历,令人遗憾:

NSMutableOrderedSet *listA = [NSMutableOrderedSet orderedSet];
for (NSOrderedSet *set in [list1 valueForKey:@"title"]) {
    [listA unionOrderedSet:set];
}

NSMutableOrderedSet *listB = [NSMutableOrderedSet orderedSet];
for (NSDictionary *object in list2) {
    [listB unionOrderedSet:object[@"titles"]];
}

[编辑:显然,Josh找到了一个未记录的@array运算符来解决这个问题。恭喜!]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接