LoginSignup
1

More than 5 years have passed since last update.

posted at

updated at

NSArray+Where

※修正。pluckとwhereを間違えてましたorz
Underscore.jsにarray.whereというとても便利なメソッドがある。

_.where(listOfPlays, {author: "Shakespeare", year: 1611});
//=> [{title: "Cymbeline", author: "Shakespeare", year: 1611},
    {title: "The Tempest", author: "Shakespeare", year: 1611}]

あまりにも便利すぎるのでObjective-Cでも使いたくなったので実装した。
多分n番煎じだと思う。
cocoaにはvalueForKeyPath:という便利なアクセッサがあるので、入れ子になったNSDictionaryやオブジェクトなどのプロパティも比較できる。

NSArray+Where.h
@interface NSArray (Where)

- (NSArray*)whereUsingSelector:(SEL)selector value:(id)value;
- (NSArray*)whereForKeyPath:(NSString*)keyPath comparingSelector:(SEL)selector value:(id)value;

@end

NSArray+Where.m
#import <objc/message.h>

@implementation NSArray (Where)

- (NSArray *)whereUsingSelector:(SEL)selector value:(id)value
{
    return [self whereForKeyPath:nil comparingSelector:selector value:value];
}

- (NSArray *)whereForKeyPath:(NSString *)keyPath comparingSelector:(SEL)selector value:(id)value
{
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:self.count];
    for (id obj in self) {
        id target = keyPath ? [obj valueForKeyPath:keyPath] : obj;
        if ([target respondsToSelector:selector]){
            if(objc_msgSend(target, selector, value)) {
                [array addObject:target];
            }
        }
    }
    return [NSArray arrayWithArray:array];
}

@end

こんな感じで使えます。

NSARray+WhereTests.m
- (void)testBasic
{
    NSArray *a = @[@"a",@"b",@"c",@"ab",@"cd",@"a"];
    NSArray *p = [a whereUsingSelector:@selector(isEqualToString:) value:@"a"];
    XCTAssert(p.count == 2, );
    for (NSString *s in p) {
        XCTAssert([s isEqualToString:@"a"], );
    }
}

- (void)testKeyPath
{
    NSArray *a = @[
  @{@"hoge": @"hogeval"},
  @{@"fuga" : @"fugaval"},
  @{@"var" : @"varval"},
  @{@"hoga" : @"hogaval"},
  @{@"hoge": @"hogeval2"}
  ];
    NSArray *p1 = [a whereUsingSelector:@selector(objectForKey:) value:@"hoge"];
    XCTAssert(p1.count == 2, @"key hogeを持っているのは一つだけ");
    NSArray *p2 = [a whereUsingSelector:@selector(isKindOfClass:) value:[NSDictionary class]];
    XCTAssert(p2.count == a.count, @"全部辞書形");
    NSArray *p3 = [a whereForKeyPath:@"hoge" comparingSelector:@selector(isEqualToString:) value:@"hogeval"];
    XCTAssert(p3.count == 1, @"key hogeの値がhogevalなのは1つだけ");
}

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1