配列内で集計関数(最大値、最小値、平均値、合計値など)を使用する方法
背景
配列内の値の合計や最大値などを取得したい場合に何か良い方法はないかと調べた。
方法
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:の引数に関数を指定してあげる。
上記以外にも偏差を求める関数など沢山ある。
ちなみに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; } )
NSURLConnectionでオレオレ証明書(自己署名証明書)を許可する方法
背景
オレオレ証明書を使用している開発機にNSURLConnectionでhttps接続しようとしたら下記エラーが出力された。
NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)
解決方法
NSURLConnectionのデリゲートの - (void)connection:(NSURLConnection )connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge )challenge を実装してやる。
#pragma mark - NSURLConnection Delegate //開発環境のオレオレ証明書(自己署名証明書)を許可するため - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { //SSL認証だった場合の処理(NSURLAuthenticationMethodHTTPBasicやNSURLAuthenticationMethodHTTPDigestもある) if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { // "hachinobu-test.elasticbeanstalk.com"か確認 if ([challenge.protectionSpace.host isEqualToString:@"hachinobu-test.elasticbeanstalk.com"]) { [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; } } [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; }
こうすることでオレオレ証明書を使用している開発機にもアクセスできる。
CocoaPodsで[overrides the `HEADER_SEARCH_PATHS` build setting defined in `Pods/Pods.xcconfig'.]の対処方法
背景
CocoaPodsでライブラリをインストールしようとしてpod installコマンドを叩いた際に下記が出力されたので調べた。
[!] The target `MyProject [Debug]` overrides the `HEADER_SEARCH_PATHS` build setting defined in `Pods/Pods.xcconfig'. - Use the `$(inherited)` flag, or - Remove the build settings from the target. [!] The target `MyProject [Debug - Release]` overrides the `HEADER_SEARCH_PATHS` build setting defined in `Pods/Pods.xcconfig'.
対処方法
対処方法は出力されている通りで自分のprojectの[TARGETS]-[Build Settings]-[Header Search Paths]に$(inherited)を追加してやれば良い。
ここで注意すべきは上記画像のように[Header Search Paths]の一番上に$(inherited)を追加すること。
そうしないとビルドしても継承が読み込まれずエラーとなる。
ちなみに今回はoverrides the HEADER_SEARCH_PATHS
だったがOTHER_LDFLAGS
の場合がある。
この場合でも今回と同じように[Other Linker Flags]に$(inherited)を適宜追加してやることで対処できる模様。
iOS7でステータスバーの領域が黒く塗りつぶされてしまう場合の対処方法
背景
iOS7でNavigationControllerのNavigationBarを使用しているにも関わらず下記のようにステータスバーが黒く塗りつぶされてしまい対応するのに時間を要してしまったのでメモ。
原因
この現象になる原因としてはiOS6以前でUINavigaitonBarに画像を下記のように設定していた。
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"header-44.png"] forBarMetrics:UIBarMetricsDefault];
header-44.pngは名前の通り高さが44ptなので、iOS7からはステータスバー領域も含めてsetBackgroundImage:に指定した画像が適用されるためステータスバー領域が黒く塗りつぶされていた。
setBackgroundImage: forBarPosition: barMetrics:の検証
iOS7用に setBackgroundImage: forBarPosition: barMetrics: メソッドが新しく使えるようになりforBarPosition:に指定するUIBarPositionを適切に設定することで背景がステータスバー領域の上まで延びて表示されるようになるとドキュメントに記載されていたので試してみた。
しかしどれも変わらずステータスバー領域が黒く塗りつぶされたまま。。
UIBarPositionBottomを指定した場合には画像を認識してくれなかった。
対応方法
結局のところ素直にUINavigaitonBarに設定する画像の高さをステータスバー領域も含めた64ptの画像を作成してOSのバージョンによってsetBackgroundImage: forBarMetrics:メソッドで指定する画像を切り替えて対応した。
NSString *headerImageName = @"header-64"; //iOS6以下である場合は高さ44ptの画像を設定 if ([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0f) { headerImageName = @"header-44"; } [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:headerImageName] forBarMetrics:UIBarMetricsDefault];
NSDateで取得したGMT時間のままNSStringに変換する方法
背景
NSDateのdateメソッドで時刻を取得すると日本時間とは9時間差のGMT時間が返されることは知っていたのだがNSDateFormatterで取得した時間をNSString型に変換したらGMT時間でなく現地時間に変換されてしまっていたので調べた。
現象
NSDate *now = [NSDate date]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; NSString* dateString = [formatter stringFromDate:now]; NSLog(@"now:%@", now); //now:2014-04-04 11:16:16 +0000 NSLog(@"dateString:%@", dateString); //dateString:2014-04-04 20:16:16
GMT時間のままNSStringに変換されない。
解決方法
NSDate *now = [NSDate date]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; NSString* dateString = [formatter stringFromDate:now]; NSLog(@"now:%@", now); //2014-04-04 11:22:48 +0000 NSLog(@"dateString:%@", dateString); //dateString:2014-04-04 11:22:48
このようにNSDateFormatterにタイムゾーンを設定してやることでGMT時間でNSStringに変換できる。 タイムゾーンはNSTimeZoneクラスのtimeZoneForSecondsFromGMTというGMT時間からの経過秒数を引数にもつメソッドに0を指定したタイムゾーンをセットしてあげる。
NSDictionaryの配列からNSPredicateを使って該当のNSDictionaryを抜き出す方法
背景
NSDictionaryの配列から該当のNSDictionaryを取得する際に今まではforループで1つ1つ判定して取得していたのだが速度が遅いのでNSPredicateを使って該当のNSDictionaryを取得する方法を調べた。
やり方
NSArrayのfilteredArrayUsingPredicateで取得したいNSDictionaryのKeyとValueを検索条件に入れて抽出できる。
NSDictionary *personDic1 = @{@"name": @"hachinobu", @"age": @28}; NSDictionary *personDic2 = @{@"name": @"suzuki", @"age": @40}; NSDictionary *personDic3 = @{@"name": @"honda", @"age": @27}; NSDictionary *personDic4 = @{@"name": @"samuragochi", @"age": @50}; NSArray *personArray = @[personDic1, personDic2, personDic3, personDic4]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K == %@", @"name", @"hachinobu"]; NSDictionary *resultDic = [[personArray filteredArrayUsingPredicate:predicate] firstObject]; NSLog(@"results:%@", resultDic);
出力
results:{ age = 28; name = hachinobu; }
関連記事
16進数のカラーコードをもとにUIColorを作成する
背景
カラーコードは大体16進数表記なのでiOSのUIcolorを作る際に16進数表記のカラーコードを10進数に変換してUIColorを作成したかったので調べた。
やり方
下記メソッドをUIColorのカテゴリに追加した。
+ (UIColor *)colorWithHexString:(NSString *)hex { //先頭に#がついていた場合は#を削除 if ([hex hasPrefix:@"#"]) { hex = [hex substringFromIndex:1]; } unsigned int rgb[3]; for (int i = 0; i < 3; i++) { NSString *component = [hex substringWithRange:NSMakeRange(i * 2, 2)]; NSScanner *scanner = [NSScanner scannerWithString:component]; [scanner scanHexInt:&rgb[i]]; } return [self colorWithRed:rgb[0]/255.0 green:rgb[1]/255.0 blue:rgb[2]/255.0 alpha:1.0f]; }
これで16進数のカラーコードに対応したUIColorを取得することができる。