iOSでUnderscore.jsライクに処理できるUnderscore.m〜後編〜

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

前回のおさらい

前回の記事「iOSでUnderscore.jsライクに処理できるUnderscore.m〜前編〜」では、Underscore.mの導入からNSArrayに対しての処理の仕方を解説しました。今回はNSDictionaryに対しての処理の仕方とヘルパーについて解説していきたいと思います。

Underscore.mの使い方〜NSDictonary〜

NSArrayと同様、Underscore.mではNSDictionaryに対して直説処理を行うのではなく、「対象となるNSDictionaryインスタンスをUSDictionaryWrapperインスタンスに変換したものに対して処理を行い、その結果をNSDictionaryに戻す」といった感じに使用します。

NSDictionaryとUSDictionaryWrapperの相互変換

NSDictionaryインスタンスをUSDictionaryWrapperインスタンスに変換するにはUnderscore.dict(NSDictionary *dictionary)を使用します。

USDictionaryWrapper *dictWrapper = Underscore.dict(dict);

逆にUSDictionaryWrapperインスタンスからNSDictionaryへの変換はwrapper.unwrapを使用します。

NSDictionary *dict = dictWrapper.unwrap(dictWrapper);

keys〜キーの配列を取得する〜

キーの配列を取得するにはUnderscore.keys(NSDictionary *dictionary)を使用します。NSDictionaryのallKeysメソッドとの違いはUSArrayWrapperを返すことです。これにより、プロトタイプチェーンを利用することができます。

NSDictionary *dict = @{
                       @"en": @"Hello world!",
                       @"sv": @"Hej världen!",
                       @"de": @"Hallo Welt!",
                       @"ja": [NSNull null]
                       };

NSArray *languages = Underscore.dict(dict).keys.unwrap; // @[@"en", @"de", @"ja", @"sv"]が返る

クラスメソッドを使用する場合

Underscore+Functionalに定義されるクラスメソッドを使用する場合は以下のように書きます。

NSArray *languages = Underscore.keys(dict); // @[@"en", @"de", @"ja", @"sv"]が返る

values〜値の配列を取得する〜

値の配列を取得するにはUnderscore.values(NSDictionary *dictionary)を使用します。これもkeysと同様、USArrayWrapperが返されるのでプロトタイプチェーンを利用することができます。

NSDictionary *dict = @{
                       @"en": @"Hello world!",
                       @"sv": @"Hej världen!",
                       @"de": @"Hallo Welt!",
                       @"ja": [NSNull null]
                       };

NSArray *languages = Underscore.dict(dict).values.unwrap; // @[@"Hello world!", @"Hej världen!", @"Hallo Welt!", [NSNull null]]が返る

クラスメソッドを使用する場合

Underscore+Functionalに定義されるクラスメソッドを使用する場合は以下のように書きます。

NSArray *languages = Underscore.values(dict); // @[@"Hello world!", @"Hej världen!", @"Hallo Welt!", [NSNull null]]が返る

each(dictEach)〜Dictionaryのそれぞれのキー・値に処理を適用する〜

Dictionaryのそれぞれのキー・値に処理を適用するにはwrapper.each(UnderscoreDictionaryIteratorBlock block)(Underscore.each(NSDictionary *dictionary, UnderscoreDictionaryIteratorBlock block)) を使用します。元のDictionaryの変更は行われません。

NSDictionary *dict = @{
                       @"en": @"Hello world!",
                       @"sv": @"Hej världen!",
                       @"de": @"Hallo Welt!",
                       @"ja": [NSNull null]
                       };

Underscore.dict(dict).each(^(id key, id obj) {
    NSLog(@"%@: %@", key, obj); // 順番に出力される
});

クラスメソッドを使用する場合

Underscore+Functionalに定義されるクラスメソッドを使用する場合は以下のように書きます。

Underscore.dictEach(dict, ^(id key, id obj) {
    NSLog(@"%@: %@", key, obj); // 順番に出力される
});

map(dictMap)〜Dictionaryのそれぞれのキー・値に処理を適用した結果から新たにDictionaryを作成する〜

Dictionaryのそれぞれのキー・値に処理を適用した結果から新たにDictionaryを作成するにはwrapper.map(UnderscoreDictionaryMapBlock block)(Underscore.map(NSDictionary *dictionary, UnderscoreDictionaryMapBlock block)) を使用します。blockの中でnilを返すとその要素は無視されます。

NSDictionary *dict = @{
                       @"en": @"Hello world!",
                       @"sv": @"Hej världen!",
                       @"de": @"Hallo Welt!",
                       @"ja": [NSNull null]
                       };

NSDictionary *capitalized = Underscore.dict(dict)
.map(^(id key, id obj) {
    if ([obj isKindOfClass:NSString.class]) {
        return (id)[(NSString *)obj capitalizedString];
    } else {
        return obj;
    }
}).unwrap;

クラスメソッドを使用する場合

Underscore+Functionalに定義されるクラスメソッドを使用する場合は以下のように書きます。

NSDictionary *capitalized = Underscore.dictMap(dict, ^(id key, id obj) {
    if ([obj isKindOfClass:NSString.class]) {
        return (id)[(NSString *)obj capitalizedString];
    } else {
        return obj;
    }
});

pick〜指定したキーに該当するキー・値からDictionaryを作成する〜

指定したキーに該当するキー・値からDictionaryを作成するにはUnderscore.pick(NSDictionary *dictionary, NSArray *keys)を使用します。

NSDictionary *dict = @{
                       @"en": @"Hello world!",
                       @"sv": @"Hej världen!",
                       @"de": @"Hallo Welt!",
                       @"ja": [NSNull null]
                       };

NSDictionary *picked = Underscore.dict(dict).pick(@[@"en", @"ja"]).unwrap;

クラスメソッドを使用する場合

Underscore+Functionalに定義されるクラスメソッドを使用する場合は以下のように書きます。

NSDictionary *picked = Underscore.pick(dict, @[@"en", @"ja"]);

extend〜Dictionaryを結合する〜

Dictionaryを結合するにはUnderscore.extend(NSDictionary *destination, NSDictionary *source)を使用します。destinationに既に同じキーが登録されていた場合はsourceの値で上書きされます。

NSDictionary *dict = @{
                       @"en": @"Hello world!",
                       @"sv": @"Hej världen!",
                       @"de": @"Hallo Welt!",
                       @"ja": [NSNull null]
                       };

NSDictionary *newDict = Underscore.dict(dict).extend(@{@"fr": @"bonjour le monde!"}).unwrap;	

クラスメソッドを使用する場合

Underscore+Functionalに定義されるクラスメソッドを使用する場合は以下のように書きます。

NSDictionary *newDict = Underscore.extend(dict, @{@"fr": @"bonjour le monde!"});

defaults〜Dictionaryに初期値を設定する〜

Dictionaryに初期値を設定するにはUnderscore.defaults(NSDictionary *dictionary, NSDictionary *defaults)を使用します。dictionaryに既に同じキーが登録されていた場合は無視します。

NSDictionary *dict = @{
                       @"en": @"Hello world!",
                       @"sv": @"Hej världen!",
                       @"de": @"Hallo Welt!"
                       };

NSDictionary *newDict = Underscore.dict(dict).defaults(@{@"ja": @"こんにちは、世界!"}).unwrap;

クラスメソッドを使用する場合

Underscore+Functionalに定義されるクラスメソッドを使用する場合は以下のように書きます。

NSDictionary *newDict = Underscore.defaults(dict, @{@"ja": @"こんにちは、世界!"});

filterKeys〜それぞれのキーに処理した結果、YESを返すキー・値のDictionaryを作成する〜

それぞれのキーに処理した結果、YESを返すキー・値のDictionaryを作成するにはUnderscore.filterKeys(NSDictionary *dictionary, UnderscoreTestBlock test)を使用します。

NSDictionary *dict = @{
                       @1: @"1行目",
                       @2: @"2行目",
                       @3: @"3行目",
                       @4: @"4行目",
                       };

NSDictionary *evenRows = Underscore.dict(dict).filterKeys(^BOOL (NSNumber *key) {
    return (key.integerValue % 2 == 0);
}).unwrap;

クラスメソッドを使用する場合

Underscore+Functionalに定義されるクラスメソッドを使用する場合は以下のように書きます。

NSDictionary *evenRows = Underscore.filterKeys(dict, ^BOOL (NSNumber *key) {
    return (key.integerValue % 2 == 0);
});

filterValues〜それぞれの値に処理した結果、YESを返すキー・値のDictionaryを作成する〜

それぞれの値に処理した結果、YESを返すキー・値のDictionaryを作成するにはUnderscore.filterValues(NSDictionary *dictionary, UnderscoreTestBlock test)を使用します。

NSDictionary *dict = @{
                       @"One": @1,
                       @"Two": @2,
                       @"Three": @3,
                       @"Four": @4
                       };

NSDictionary *odd = Underscore.dict(dict).filterValues(^BOOL (NSNumber *value) {
    return (value.integerValue % 2 != 0);
}).unwrap;

クラスメソッドを使用する場合

Underscore+Functionalに定義されるクラスメソッドを使用する場合は以下のように書きます。

NSDictionary *odd = Underscore.filterValues(dict, ^BOOL (NSNumber *value) {
    return (value.integerValue % 2 != 0);
});

rejectKeys〜それぞれのキーに処理した結果、NOを返すキー・値のDictionaryを作成する〜

filterKeysとは逆にそれぞれのキーに処理した結果、NOを返すキー・値のDictionaryを作成するにはUnderscore.rejectKeys(NSDictionary *dictionary, UnderscoreTestBlock test)を使用します。

NSDictionary *dict = @{
                       @1: @"1行目",
                       @2: @"2行目",
                       @3: @"3行目",
                       @4: @"4行目",
                       };

NSDictionary *oddRows = Underscore.dict(dict).rejectKeys(^BOOL (NSNumber *key) {
    return (key.integerValue % 2 == 0);
}).unwrap;

クラスメソッドを使用する場合

Underscore+Functionalに定義されるクラスメソッドを使用する場合は以下のように書きます。

NSDictionary *oddRows = Underscore.rejectKeys(dict, ^BOOL (NSNumber *key) {
    return (key.integerValue % 2 == 0);
});

rejectValues〜それぞれの値に処理した結果、NOを返すキー・値のDictionaryを作成する〜

それぞれの値に処理した結果、NOを返すキー・値のDictionaryを作成するにはUnderscore.rejectValues(NSDictionary *dictionary, UnderscoreTestBlock test)を使用します。

NSDictionary *dict = @{
                       @"One": @1,
                       @"Two": @2,
                       @"Three": @3,
                       @"Four": @4
                       };

NSDictionary *even = Underscore.dict(dict).rejectValues(^BOOL (NSNumber *value) {
    return (value.integerValue % 2 != 0);
}).unwrap;

クラスメソッドを使用する場合

Underscore+Functionalに定義されるクラスメソッドを使用する場合は以下のように書きます。

NSDictionary *even = Underscore.rejectValues(dict, ^BOOL (NSNumber *value) {
    return (value.integerValue % 2 != 0);
});

Underscore.mの使い方〜ヘルパー〜

Underscore.mではライブラリを使いやすくするため、いくつかヘルパーが用意されています。これらのヘルパーはfindfilterなど、引数としてUnderscoreTestBlock testを取るメソッドで、文字列かどうかや、数値かどうかなど一般的な比較条件を予め定義しています。

  • Underscore.isString
  • Underscore.isNumber
  • Underscore.isArray
  • Underscore.isDictionary
  • Underscore.isNull

Underscore.negate

Underscore.negate(UnderscoreTestBlock block)UnderscoreTestBlock testブロックの否定系の結果を返します。たとえば、「文字列でない」を表したい場合は「Underscore.negate(Underscore.isString)」 とします。

Underscore.isEqual

あるオブジェクトと等しいかどうかを条件とする場合はUnderscore.isEqual(id obj)が使えます。例えば、「@"hoge"と等しい」を表したい場合は「Underscore.isEqual(@"hoge")」とします。

まとめ

前編、後編の2回に分けてUnderscore.mの使い方を解説しました。NSArrayやNSDictionaryに対していつも似たようなコードを書いていてうんざりの方、是非使ってみてください!