» Objective-c key paths

Key paths are an insanely helpful concept in objective-c. What are they? Basically they are like key-value coding but nested... plus a touch of magic. With key-value coding you can get at the variables and methods of an object.

NSString *name = @"Corey";
NSLog(@"Output: %@", [name valueForKey:@"length"]);

// Output: 5

That example was pretty boring since we can get at the length property in simpler ways, but it's a simple introduction to key-value coding.

Hello Key Paths

What if you have an array of strings and you want to get an array of their lengths (this example is contrived, but just stick with me.) You could loop through the strings and store the lengths in an array or you could use key paths!

NSArray *animals = [NSArray arrayWithObjects:@"pig", 
  @"dog", 
  @"human", 
  @"bear", 
  @"frog", 
  nil];
  
NSLog(@"Output: %@", [animals valueForKeyPath:@"length"]);  

// Output: (3, 3, 5, 4, 4)

The same technique applies to dictionaries as well. It's super helpful if you have nested dictionaries. For example assume you have an NSDictionary called animals where the animal name is the key, and the value is an NSDictionary of that animals traits (i.e. legs, teeth, eyes, nipples). Then...

NSLog(@"Legs: %@", [animals valueForKeyPath:@"pig.legs"]

// Output: 4

But that's not all! key paths have magic keywords you can throw in like sum, distinctUnionOfObjects and avg.

NSArray *words = [NSArray arrayWithObjects:@"bob", 
  @"dog", 
  @"human", 
  @"bear", 
  @"frog", 
  nil];
  
NSLog(@"Sum: %@", [words valueForKeyPath:@"@sum.length"]); // Add up all the string lengths
NSLog(@"Avg: %@", [words valueForKeyPath:@"@avg.length"]); // Average length of all strings

// Sum: 19
// Avg: 3.8

Take a look at the Apple Docs for information about all the operators.

Real World Example

How would you use this in the real world? What if you had a shockingly descriptive object model of your friends, and you wanted to find everyone who had a dad named "Bob". No problem with key paths! Just throw in this NSArray category and you are set!

@implementation NSArray (Find)

- (NSArray *)findAllWhereKeyPath:(NSString *)keyPath equals:(id)value {
  NSMutableArray *matches = [NSMutableArray array];
    for (id object in self) {
      id objectValue = [object valueForKeyPath:keyPath];
      if ([objectValue isEqual:value] || objectValue == value) [matches addObject:object];         
    }
   
    return matches;
}

@end

// Implementation example
NSArray *friendsWithDadsNamedBob = [friends findAllWhereKeyPath:@"father.name" equals:@"Bob"]

User Comments

Recent Posts

    Archive

    • Wax talks to Twitter - October 20, 2009
    • How does iPhone Wax work? - October 19, 2009
    • Setting up iPhone Wax - October 18, 2009
    • Ruby (tinyrb) on iPhone - May 03, 2009
    • Building PCRE static lib for the iPhone - May 02, 2009
    • Amazon EC2 + Chef = Mmmmm - March 29, 2009
    • Objective-c key paths - February 13, 2009
    • POW! - December 26, 2008
    • Abusing Ruby's question mark methods. - November 28, 2008
    • Git hooks make me giddy - November 07, 2008
    • Ruby Equality! equal? eql? == and === - October 26, 2008
    • Ruby, Rails and Google Sitemaps - October 20, 2008
    • Projects

      • Wax Obj-C to Lua bridge for iPhone.
      • Pow a Ruby library for making file & directory manipulation easy.
      • MiniMagick a tiny RMagick replacement.