4

这个问题类似于从 NSArray of objects 中提取属性,但用于更深层次的提取。

为简单起见,我在以下示例中所指的对象是 NSString。

我有两个要解决的案例。

带有对象的 NSDictionary 的 NSArray 的 NSArray

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

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

期望的结果将是例如:

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

NSDictionary 的 NSArray 和对象的 NSArray

最好使用键值编码,从以下结构中,我想将“标题”列表提取到单个可枚举列表中:

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

期望的结果将是例如:

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

笔记

  • 我的真实案例涉及第一个列表NSOrderedSet的对象和第二个列表的对象,但这不应该影响我认为的答案。NSOrderedSetNSOrderedSetNSDictionaryNSOrderedSet

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

  • 同样,这无关紧要,但对象是来自获取请求的 NSManagedObject 。所以我知道我可以直接优化获取请求,但我仍然想知道是否有不错的选择。

4

2 回答 2

3

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

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

第二个非常相似,但是您必须以相反的顺序进行。首先提取所有titles数组,然后将它们展平。

[list2 valueForKeyPath:@"titles.@unionOfArrays.self"]

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

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

对于NSOrderedSet,您似乎可以array在关键路径中使用其属性在对内部集合进行操作之前对其进行转换,但由于某种原因,这会产生错误。然而,我发现了这个有趣的花絮,由 Nicolas Bouilleaud 在 GitHub 上发布:

// 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"]);

我不知道为什么。我不知道这是从哪里来的或者它在做什么(特别是为什么它在最后)。

于 2015-08-03T07:27:04.467 回答
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操作员来解决这个问题。康特拉兹!]

于 2015-08-03T09:01:48.313 回答