Keypath for first element in embedded NSArray
Asked Answered
S

2

11

This example is contrived, but it shows my point.

So, if I have an object graph like the following:

{
sex = male;
uid = 637650940;
work = ({
    employer = {
        id = 116420715044499;
        name = "Software Engineer";
    };
    "end_date" = "0000-00";
    "start_date" = "0000-00";
}, {
    employer = {
        id = 188733137832278;
        name = "Apple";
    };
});
},
//Some more objects

(This is an NSArray containing NSDictionarys that have an object of type NSArray).

The key field is work. I want a Key Path that will take the first object in the work array.

If I do this:

NSArray* work = [outerArrayObject objectForKey: @"work"];
id name = [work valueForKeyPath: @"employer.name"];

I get an array containing each name (In the above case, Software Engineer & Apple). Is there a collection operator or something to return the first object? Bonus points if you can develop a Key Path to sort each work by start_date also :)

Stevens answered 21/8, 2012 at 12:28 Comment(0)
S
17

Well to answer my own question, one way to do it is this:

1) Define the following category

@implementation NSArray (CustomKVOOperators)

- (id) _firstForKeyPath: (NSString*) keyPath {
    NSArray* array = [self valueForKeyPath: keyPath];
    if( [array respondsToSelector: @selector(objectAtIndex:)] &&
        [array respondsToSelector: @selector(count)]) {
        if( [array count] )
            return [array objectAtIndex: 0];
        else
            return nil;
    }
    else {
        return nil;
    }
}

@end

2) Use this KeyPath syntax

NSArray* work = [outerArrayObject objectForKey: @"work"];
id name = [work valueForKeyPath: @"@first.employer.name"];

Thanks to this clever person.

Stevens answered 21/8, 2012 at 12:42 Comment(3)
In the case of outerArrayObject is that its parent?Cyclades
@PauldeLange what is _firstForKeyPath, is it private API ?Curiosa
I wonder if somebody invented how to access by index f.e: 4.employer.nameViolaceous
C
18

@PauldeLange - Your answer and links were helpful.
The following simpler version works too (at least as of Xcode 6)

id name = [work valueForKeyPath: @"employer.name.@firstObject”];

In the above 'firstObject' refers to the predefined method on NSArray. If the second object is needed, you can define the following:

@implementation NSArray (CustomKVOOperators)

- (id) secondObject {
    return [self count] >=2 ? self[1] : nil;
}
@end

And use:

 id name = [work valueForKeyPath: @"employer.name.@secondObject”];
Crin answered 6/5, 2015 at 2:10 Comment(3)
Good find and good update! This is a much better way of doing it nowStevens
I'm getting "this class does not implement the firstObject operation"Violaceous
@Violaceous You may have resolved this by now. In your usage likely the data object (work) being operated on is not an NSArray.Crin
S
17

Well to answer my own question, one way to do it is this:

1) Define the following category

@implementation NSArray (CustomKVOOperators)

- (id) _firstForKeyPath: (NSString*) keyPath {
    NSArray* array = [self valueForKeyPath: keyPath];
    if( [array respondsToSelector: @selector(objectAtIndex:)] &&
        [array respondsToSelector: @selector(count)]) {
        if( [array count] )
            return [array objectAtIndex: 0];
        else
            return nil;
    }
    else {
        return nil;
    }
}

@end

2) Use this KeyPath syntax

NSArray* work = [outerArrayObject objectForKey: @"work"];
id name = [work valueForKeyPath: @"@first.employer.name"];

Thanks to this clever person.

Stevens answered 21/8, 2012 at 12:42 Comment(3)
In the case of outerArrayObject is that its parent?Cyclades
@PauldeLange what is _firstForKeyPath, is it private API ?Curiosa
I wonder if somebody invented how to access by index f.e: 4.employer.nameViolaceous

© 2022 - 2024 — McMap. All rights reserved.