class: center, middle # KVC Collection Operators #### `@distinctUnionOfHacks` .footnote[[n-b.github.com/custom-kvc-operators](http://github.com/n-b/custom-kvc-operators)] --- layout: true # Key-Value Coding Basics --- `valueForKey:@"key"` appelle la méthode "key", si elle existe. -- [[@"hello"] uppercaseString] // -> @"HELLO" [@"hello" valueForKey:@"uppercaseString"] // -> @"HELLO" --- Les Collections retournent une collection en appelant `valueForKey:` sur chaque objet. [@[@"hello",@"goodbye"] valueForKey:@"uppercaseString"] // -> @[@"HELLO",@"GOODBYE"] --- `-[NSObject self]` est une méthode comme les autres. [@"hello" valueForKey:@"self"] // -> @"hello" --- layout: true # KVC Paths --- Les composants sont séparés par ".", et évalués de gauche à droite. [object valueForKeyPath:@"a.b.c"] -- revient au même que : [[[object valueForKey:@"a"] valueForKey:@"b"] valueForKey:@"c"] --- layout:true # Collections “Operators” --- Un composant de keypath qui démarre par "@" permet d'utiliser une propriété de l'*objet* collection. -- **Comportement non documenté !** -- [@[@"a",@"b"] count] // -> 2 [@[@"a",@"b"] valueForKey:@"@count"] // -> @(2) [@[@"a",@"b"] valueForKey:@"@lastObject"] // -> @"b" --- @implementation NSArray (Reverse) - (instancetype) reverse { return [[self reverseObjectEnumerator] allObjects]; } @end [array valueForKey:@"@reverse"] // -> @[@"b",@"a"] --- layout:true # “Simple” Collections Operators --- L'exemple de la documentation : [object valueForKeyPath: @"keypathToCollection.@collectionOperator.keypathToProperty"] -- revient au même que : [[object valueForKeyPath:@"keyPathToCollection"] valueForKeyPath:@"@collectionOperator.keypathToProperty"] -- Etape par étape : [[[object valueForKeyPath:@"keyPathToCollection"] valueForKeyPath:@"keypathToProperty"] performTheCollectionOperator] --- Opérateurs “simples” : **@avg, @sum, @min, @max** [company valueForKeyPath:@"employees.@avg.salary"] [[company valueForKeyPath:@"employees.salary"] average] // (just implement -[NSArray average]) -- Une implémentation de @avg : 1. appelle `[self valueForKeyPath:@"
"]`
Le résultat ***doit*** être une collection de NSNumbers. -- 2. calcule la moyenne et la retourne sous forme de NSNumber. --- On peut utiliser `self` : [[@0,@2,@8,@10] valueForKeyPath:@"@avg.self"] // -> @5 -- Ou même `@self` : [array valueForKeyPath:@"@avg.@self"] --- layout:true # Custom Collections Operators --- -- - (id) _
ForKeyPath:(NSString*)keyPath **Non documenté !** --- `@standardDeviation` - (NSNumber*) _standardDeviationForKeyPath:(NSString*)keyPath [@[@0, @5, @5, @5, @5] valueForKeyPath:@"@standardDeviation.self"] // -> @2 --- `@differential` - (NSArray*) _differentialForKeyPath:(NSString*)keyPath [@[@0, @1, @3, @0] valueForKeyPath:@"@differential.self"] // -> @[@1, @2, @(-3)] --- layout:true # (distinct)UnionOfObjects --- -- `@unionOfObjects` ne sert à rien ! id pencils = @[@{@"color": @"blue"}, @{@"color": @"red"}, @{@"color": @"blue"}, @{@"color": @"green"}]; [pencils valueForKeyPath:@"@unionOfObjects.color"] // -> @[@"blue", @"red", @"blue", @"green"] --- `@distinctUnionOfObjects` élimine les doublons : id pencils = @[@{@"color": @"blue"}, @{@"color": @"red"}, @{@"color": @"blue"}, @{@"color": @"green"}]; [pencils valueForKeyPath:@"@distinctUnionOfObjects.color"] // -> @[@"blue", @"red", @"green"] --- layout:true # (distinct)UnionOfArrays/Sets Utilisent une **collection de collections**. --- -- `@unionOfArrays` : id pencils = @[@{@"color": @"blue"}, @{@"color": @"red"}, @{@"color": @"blue"}]; id markers = @[@{@"color": @"purple"}, @{@"color": @"blue"}, @{@"color": @"green"}]; [@[pencils, markers] valueForKeyPath:@"@unionOfArrays.color"] // -> @[@"blue", @"red", @"blue", @"purple", @"blue", @"green"] --- `@distinctUnionOfArrays` élimine les doublons : id pencils = @[@{@"color": @"blue"}, @{@"color": @"red"}, @{@"color": @"blue"}]; id markers = @[@{@"color": @"purple"}, @{@"color": @"blue"}, @{@"color": @"green"}]; [@[pencils, markers] valueForKeyPath:@"@distinctUnionOfArrays.color"] // -> @[@"green", @"red", @"blue", @"purple"] --- layout:true # Le cas des valeurs nulles --- -- > ***Important:*** *This operator raises an exception if any of the leaf objects is nil.* -- > *This class is not key value coding-compliant for the key "key"* -- id objects = @[@{@"color": @"blue"}, @{@"color": @"red"}, @{@"color": @"green"}, @"notacolor"]; [objects valueForKeyPath:@"@unionOfObjects.color"]; // NSUnknownKeyException : "This class is not key value coding-compliant for the key color" --- **`@unionOfPresentObjects`** Similaire à @unionOfObjects, mais ignore : * les objets incompatibles, * les valeurs nil, * les NSNull. -- - (id) _unionOfPresentObjectsForKeyPath:(NSString*)keyPath { NSMutableArray * values = [NSMutableArray new]; for (id obj in self) { @try { id value = [obj valueForKeyPath:keyPath]; if(value && value!=[NSNull null]) [values addObject:value]; } @catch (NSException *exception) { } } return [NSArray arrayWithArray:values]; } --- @unionOfPresentObjects id objects = @[@{@"color": @"blue"}, @{@"color": @"red"}, @{@"color": @"green"}, @"notacolor"]; [objects valueForKeyPath:@"@unionOfPresentObjects.color"] // -> @[@"blue",@"red",@"green"] --- layout:true # Paramètres spécifiques --- La partie de droite du keyPath peut servir à autre chose : -- **`@convertDistanceFromTo`** [arrayOfMiles valueForKeyPath:@"@convertDistanceFromTo.miles.KM"] -- **`@filterWithRegexp`** [arrayOfMiles valueForKeyPath:@"@filterWithRegexp.[Mm]ozilla"] -- **`@resizedTo`** [iamges valueForKeyPath:@"@resizedTo.{128,128}"] --- layout:false # Liens * [Apple docs](http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueCoding/Articles/CollectionOperators.html) * [NSHipster](http://nshipster.com/kvc-collection-operators/) .footnote[Nicolas Bouilleaud - 2012
[Follow me on Twitter](http://twitter.com/_nb)]