hachinoBlog

hachinobuのエンジニアライフ

配列内で集計関数(最大値、最小値、平均値、合計値など)を使用する方法

背景

配列内の値の合計や最大値などを取得したい場合に何か良い方法はないかと調べた。

方法

NSExpression を使用することで集計関数の式を定義できる。

//最大値
NSArray *numArray = @[@3, @6, @12, @8];
NSExpression *maxExpression = [NSExpression expressionForFunction:@"max:" arguments:@[[NSExpression expressionForConstantValue:numArray]]];
id maxValue = [maxExpression expressionValueWithObject:nil context:nil];
NSLog(@"最大値:%f", [maxValue floatValue]); //最大値:12.000000
    
//最小値
NSExpression *minExpression = [NSExpression expressionForFunction:@"min:" arguments:@[[NSExpression expressionForConstantValue:numArray]]];
id minValue = [minExpression expressionValueWithObject:nil context:nil];
NSLog(@"最小値:%f", [minValue floatValue]); //最大値:3.000000
    
//平均値
NSExpression *averageExpression = [NSExpression expressionForFunction:@"average:" arguments:@[[NSExpression expressionForConstantValue:numArray]]];
id averageValue = [averageExpression expressionValueWithObject:nil context:nil];
NSLog(@"平均値:%f", [averageValue floatValue]); //平均値:7.250000
    
//合計値
NSExpression *sumExpression = [NSExpression expressionForFunction:@"sum:" arguments:@[[NSExpression expressionForConstantValue:numArray]]];
id sumValue = [sumExpression expressionValueWithObject:nil context:nil];
NSLog(@"合計値:%f", [sumValue floatValue]); //合計値:29.000000
    
//カウント
NSExpression *countExpression = [NSExpression expressionForFunction:@"count:" arguments:@[[NSExpression expressionForConstantValue:numArray]]];
id countValue = [countExpression expressionValueWithObject:nil context:nil];
NSLog(@"カウント:%d", [countValue integerValue]); //カウント:4

expressionForFunction:の引数に関数を指定してあげる。

上記以外にも偏差を求める関数など沢山ある。

NSExpression Class Reference

ちなみにNSDictionaryや自作オブジェクトの配列でも同じことができる。

NSDictionary *person1 = @{@"name": @"A", @"age": @20};
NSDictionary *person2 = @{@"name": @"B", @"age": @30};
NSDictionary *person3 = @{@"name": @"C", @"age": @35};
NSDictionary *person4 = @{@"name": @"D", @"age": @40};
NSArray *personArray = @[person1, person2, person3, person4];

//最大値
NSExpression *maxExpression = [NSExpression expressionForFunction:@"max:" arguments:@[[NSExpression expressionForKeyPath:@"age"]]];
id maxValue = [maxExpression expressionValueWithObject:personArray context:nil];
NSLog(@"最大値:%f", [maxValue floatValue]); //最大値:40.000000

//最小値
NSExpression *minExpression = [NSExpression expressionForFunction:@"min:" arguments:@[[NSExpression expressionForKeyPath:@"age"]]];
id minValue = [minExpression expressionValueWithObject:personArray context:nil];
NSLog(@"最小値:%f", [minValue floatValue]); //最大値:20.000000

//平均値
NSExpression *averageExpression = [NSExpression expressionForFunction:@"average:" arguments:@[[NSExpression expressionForKeyPath:@"age"]]];
id averageValue = [averageExpression expressionValueWithObject:personArray context:nil];
NSLog(@"平均値:%f", [averageValue floatValue]); //平均値:31.250000

//合計値
NSExpression *sumExpression = [NSExpression expressionForFunction:@"sum:" arguments:@[[NSExpression expressionForKeyPath:@"age"]]];
id sumValue = [sumExpression expressionValueWithObject:personArray context:nil];
NSLog(@"合計値:%f", [sumValue floatValue]); //合計値:125.000000

//カウント
NSExpression *countExpression = [NSExpression expressionForFunction:@"count:" arguments:@[[NSExpression expressionForKeyPath:@"age"]]];
id countValue = [countExpression expressionValueWithObject:personArray context:nil];
NSLog(@"カウント:%d", [countValue integerValue]); //カウント:4

また、expressionForFunction:に指定する関数のmax:やmin:はNSPredicateのpredicateWithFormat:でも下記のように使用可能。

NSArray *numArray = @[@3, @6, @12, @8];

//最大値
NSPredicate *maxPredicate = [NSPredicate predicateWithFormat:@"SELF == max:(%@)", numArray];
id maxValue = [[numArray filteredArrayUsingPredicate:maxPredicate] firstObject];
NSLog(@"最大値:%f", [maxValue floatValue]); //最大値:12.000000

//最小値
NSPredicate *minPredicate = [NSPredicate predicateWithFormat:@"SELF == min:(%@)", numArray];
id minValue = [[numArray filteredArrayUsingPredicate:minPredicate] firstObject];
NSLog(@"最小値:%f", [minValue floatValue]); //最大値:3.000000

集計関数でお気づきのようにNSExpressionを使用すればCoreDataで集計関数を使用してデータをフェッチすることが可能。

CoreDataでの使い方は次回。

※2014/7/7 追記 こんな便利な書き方ができた

NSDictionary *person1 = @{@"age": @20, @"firstName": @"Takahiro"};
NSDictionary *person2 = @{@"age": @20, @"firstName": @"Ryutaro"};
NSDictionary *person3 = @{@"age": @23, @"firstName": @"Yuuichi"};
NSDictionary *person4 = @{@"age": @50, @"firstName": @"Hiroaki"};
NSDictionary *person5 = @{@"age": @30, @"firstName": @"Kosuke"};
NSArray *persons = @[person1, person2, person3, person4, person5];
 
//最年少の人材を取得
NSPredicate *minAgePredicate = [NSPredicate predicateWithFormat:@"age == %@.@min.age", persons];
NSArray *results = [persons filteredArrayUsingPredicate:minAgePredicate];

NSLog(@"results:%@", results);

出力結果

results:( { age = 20; firstName = Takahiro; }, { age = 20; firstName = Ryutaro; } )