This question is similar to extracting properties from NSArray of objects, but for deeper extraction.
For the sake of simplicity, the objects I am referring to in the below examples are NSStrings.
I have two cases I want to solve.
Preferably with Key-Value-coding, from the following structure, I'd like to extract all the "title" into a single enumerable list:
NSArray *list1 = @[
@[
@{@"title":@"A", @"description":...},
@{@"title":@"B", @"description":...},
],
@[
@{@"title":@"C", @"description":...},
@{@"title":@"D", @"description":...},
],
];
Desired result would be for example:
@[@"A", @"B", @"C", @"D"]
Preferably with Key-Value-coding, from the following structure, I'd like to extract the lists of "titles" into a single enumerable list:
NSArray *list2 = @[
@{
@"titles":@[
@"A",
@"B",
],
@"descriptions":...,
},
@{
@"titles":@[
@"C",
@"D",
],
@"descriptions":...,
},
];
Desired result would be for example:
@[@"A", @"B", @"C", @"D"]
My real cases involve NSOrderedSet
of NSOrderedSet
of objects for first list and NSOrderedSet
of NSDictionary
with NSOrderedSet
of objects for second list, but that shouldn't affect the answers I think.
I wrote I would prefer to use key-value-coding, but that is not a must. I just want to avoid, if possible, writing for (... in ...)
or enumateObjectWithBlock:
.
Again that shouldn't matter, but objects are NSManagedObject from fetch requests. So I know I could just optimise the fetch requests directly, but I still want to know if there are nice alternatives.
KVC can indeed do your bidding in this case, via a Collection Operator called @unionOfArrays
. One effect of this operator is to flatten arrays, so your first example is very simple.
[list1 valueForKeyPath:@"@unionOfArrays.title"]
The second is quite similar, but you have to do it in the reverse order. First extract all the titles
arrays, then flatten them.
[list2 valueForKeyPath:@"titles.@unionOfArrays.self"]
The self
is necessary -- although it seems redundant -- because, per the doc linked above
All the collection operators, with the exception of
@count
, require a key path to the right of the collection operator.
For NSOrderedSet
, it would seem that you could use its array
property in the key path to convert the inner collections before operating on them, but for some reason this produces errors. I found this interesting tidbit, however, posted on GitHub by Nicolas Bouilleaud:
// Convert each OrderedSet to an Array to mute the error.
NSLog(@"union : %@",[data valueForKeyPath:@"@distinctUnionOfArrays.values.@array"]);
and this weird @array
operator works on your NSOrderedSet
sample input:
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"]);
but I have no idea why. I can't figure out where this comes from or what it's doing (why it's at the end, in particular).