Modernize your Objective-C // Speaker Deck のスライドを読んで初めて知ったので、調べてみたら便利そうだったのでそれのメモです
公式ドキュメント Key-Value Coding Programming Guide: Collection Operators
概要
コレクションのvalueForKeyPath
メソッドで特殊演算子を文字列で指定することで合計や平均を取得することができます。
演算子は @
から始まります。
なんか言葉で説明するのが苦手なのでサンプルをだらだらと書いておきます。
Simple Collection Operators
例として次のような配列があるとします
// 数値配列 NSArray *numbers = @[@3, @2, @5, @1, @4]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"YYYY/MM/dd"]; [formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"ja_JP"]]; // 日付配列 NSArray *dates = @[[formatter dateFromString:@"2014/03/01"], [formatter dateFromString:@"2014/02/01"], [formatter dateFromString:@"2014/05/01"], [formatter dateFromString:@"2014/01/01"], [formatter dateFromString:@"2014/04/01"]]; // プロパティ name と age を持っているオブジェクト Person *p1 = [[Person alloc] initWithName:@"hoge" age:18]; Person *p2 = [[Person alloc] initWithName:@"foo" age:23]; Person *p3 = [[Person alloc] initWithName:@"piyo" age:16]; // オブジェクト配列 NSArray *people = @[p1, p2, p3];
@count
配列件数を取得
[numbers valueForKeyPath:@"@count"]; // 5 [dates valueForKeyPath:@"@count"]; // 5 [people valueForKeyPath:@"@count"]; // 3
@avg
平均値を取得
[numbers valueForKeyPath:@"@avg.self"]; // 3 [dates valueForKeyPath:@"@avg.self"]; // 実行時エラー [people valueForKeyPath:@"@avg.age"]; // 19
※ @演算子.プロパティ
のように記述する。単純な配列はプロパティにself
を指定。
@max
最大値を取得
[numbers valueForKeyPath:@"@max.self"]; // 5 [dates valueForKeyPath:@"@max.self"]; // 2014/05/01 [people valueForKeyPath:@"@max.age"]; // 23
@min
最小値を取得
[numbers valueForKeyPath:@"@min.self"]; // 1 [dates valueForKeyPath:@"@min.self"]; // 2014/01/01 [people valueForKeyPath:@"@min.age"]; // 16
@sum
合計を取得
[numbers valueForKeyPath:@"@sum.self"]; // 15 [dates valueForKeyPath:@"@sum.self"]; // 実行時エラー [people valueForKeyPath:@"@sum.age"]; // 57
Object Operators
サンプル配列
// プロパティ name と age を持っているオブジェクト Person *p1 = [[Person alloc] initWithName:@"hoge" age:18]; Person *p2 = [[Person alloc] initWithName:@"foo" age:23]; Person *p3 = [[Person alloc] initWithName:@"piyo" age:16]; Person *p4 = [[Person alloc] initWithName:@"piyo" age:27]; // オブジェクト配列 NSArray *people = @[p1, p2, p3, p4];
@distinctUnionOfObjects
指定したプロパティの配列を取得
- 重複は除外される
- 順番は不確定っぽい(?)
[people valueForKeyPath:@"@distinctUnionOfObjects.name"]; // @[@"foo", @"hoge", @"piyo"]
@unionOfObjects
指定したプロパティの配列を取得
- 重複は除外しない
- 順番は不確定っぽい(?)
[people valueForKeyPath:@"@unionOfObjects.name"]; // @[@"hoge", @"foo", @"piyo", @"piyo"]
Array and Set Operators
サンプル配列
// 配列の中に更に配列 NSArray *arrays = @[@[@1, @2], @[@3, @4], @[@2, @4]]; // 配列の中にNSSet NSArray *sets = @[ [[NSSet alloc] initWithObjects:@1, @2, nil], [[NSSet alloc] initWithObjects:@3, @4, nil], [[NSSet alloc] initWithObjects:@2, @4, nil], ];
@distinctUnionOfArrays
中にある配列を結合した配列を取得
- 重複は除外される
- 順番は不確定っぽい(?)
[arrays valueForKeyPath:@"@distinctUnionOfArrays.self"]; // @[@3, @2, @1, @4];
@unionOfArrays
中にある配列を結合した配列を取得
- 重複は除外しない
- 順番は不確定っぽい(?)
[arrays valueForKeyPath:@"@unionOfArrays.self"]; // @[@1, @2, @3, @4, @2, @4]
@distinctUnionOfSets
中にあるNSSetを結合した配列を取得
- 重複は除外される
- 順番は不確定っぽい(?)
[sets valueForKeyPath:@"@distinctUnionOfSets.self"]; // @[@3, @2, @1, @4]
@unionOfSets
中にあるNSSetを結合した配列を取得
- 重複は除外しない
- 順番は不確定っぽい(?)
[sets valueForKeyPath:@"@unionOfSets.self"]; // @[@1, @2, @3, @4, @4, @2]
NSDictionary
NSDictionary に対しても同じ感じのことができます。
キー名をうまく指定する感じです。
NSDictionary *dic = @{ @"key": @[ @{@"num": @1}, @{@"num": @2}, ] }; NSLog(@"%@", [dic valueForKeyPath:@"key.@sum.num"]); // 3
まとめ
これを使えば例えば合計値や最大値を求めるのにループを書かなくて良いのでだいぶ楽になると思います。
さらに文字列で指定するのも面倒なのでクラスを拡張してメソッドを作っておくと更に楽かもです。
// NSArray+Operation.h #import <Foundation/Foundation.h> @interface NSArray (Operation) - (NSNumber *)sum; - (NSNumber *)max; @end // NSArray+Operation.m #import "NSArray+Operation.h" @implementation NSArray (Operation) - (NSNumber *)sum { return [self valueForKeyPath:@"@sum.self"]; } - (NSNumber *)max { return [self valueForKeyPath:@"@sum.self"]; } @end
他にも Modernize your Objective-C // Speaker Deck には色々書いてあるので一度読んでおくと良いと思います。