Search code examples
iosobjective-cnsset

NSSet of nested subproperty from set


I have a set of someProtocol objects, I'd like to make a set from a NSString nested property on this object:

// Protocol SomeProtocol
@protocol SomeProtocol<NSObject>

@property(nonatomic, readonly) id<SomeSubProtocol> someSubProtocolObject;

@end

// Protocol SomeSubProtocol
@protocol SomeSubProtocol<NSObject>

@property(nonatomic, readonly) NSString *Id;

@end

I have a set of SomeProtocols:

NSSet<id<SomeProtocol>> *setOfSomeSubProtocols;

I'd like to get a NSSet of the Id properties:

NSSet<NSString *> *idSet = ?; // Calculate from arrayOfSomethings.

I've tried:

idSet = [setOfSomeSubProtocols valueForKeyPath @"someSubProtrocolObject.id"];

But I'd prefer something that throws a compiler error if the properties change...


Solution

  • In Swift, I would have suggested to use map(), but it doesn't exists in Objective-C. You can search or implement yourself a version of map in Objective-C.

    But, what you can do, is a manual loop, since it's a normal approach of the issue.

    __block NSMutableSet *ids = [[NSMutableSet alloc] init];
    [setOfSomeSubProtocols enumerateObjectsUsingBlock:^(id<SomeProtocol>  _Nonnull obj, BOOL * _Nonnull stop) {
        [ids addObject:[[obj someSubProtocolObject] identifier]];
    }];
    

    It's quite simple and does the job.

    You could as said before, implement a map on NSSet, something like this:

    @implementation NSSet(Map)

    -(NSSet *)mapWithBlock:(id (^)(id))block {
        __block NSMutableSet *set = [[NSMutableSet alloc] init];
        [self enumerateObjectsUsingBlock:^(id  _Nonnull obj, BOOL * _Nonnull stop) {
            id mappedObject = block(obj);
            [set addObject:mappedObject];
        }];
        return set;
    }
    

    With call:

    NSSet *ids = [setOfSomeSubProtocols mapWithBlock:^id (id<SomeProtocol> object) {
        return [[object someSubProtocolObject] identifier];
    }];
    NSLog(@"ids: %@", ids);