NSPredicate 全構文解説

[NSPredicate](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSPredicate_Class/Reference/NSPredicate.html)で使用できる全構文の解説です。

確認に使用したサンプルコードは[こちら](https://github.com/yusuga/TestNSPredicate)です。

[NSPredicate Programming Guide - Predicate Format String Syntax](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Predicates/Articles/pSyntax.html#//apple_ref/doc/uid/TP40001795)に記載しているのは全て取り上げていますが、SUBQUERYが[NSExpression](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSExpression_Class/Reference/NSExpression.html#jumpTo_17)に書いてあったりしたからまだ他にもあるのかもしれません。

あと@を指定して使用する関数のドキュメントが見つからなかった・・・ドキュメントが乱雑し整理されていない感じがしました。

関連キーワード: [BNF](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Predicates/Articles/pBNF.html), [NSExpression](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSExpression_Class/Reference/NSExpression.html)

**注意: SQLと同じ用語には注意が必要です。例えばANYはSQLのANYとは異なります。**

# リテラル

## %@
文字列として扱われる(ダブルクォーテーションで囲われる)

```objectivec:%@
NSString *attributeValue = @"Anne";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"firstName LIKE %@", attributeValue];

// predicate: firstName LIKE "Anne"
```

## %K
動的プロパティとして扱われる(ダブルクォーテーションで囲われない)

```objectivec:%K
NSString *attributeName = @"firstName";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K LIKE \'Anne\'", attributeName];

// predicate: firstName LIKE "Anne"
```

## { 'comma', 'separated', 'literal', 'array' }
カンマで区切られたリテラル配列の表記

## SELF
評価されるオブジェクト自身

## FALSE, NO
false

## TRUE, YES
true

## NULL, NIL
null

## "text" , 'text'
文字列。"text"と'text'は同じ。混在時に注意("a'b'c" は a, 'b', c と相当)

## 1, 27, 2.71828, 19.75
整数と固定小数点表記

##  9.2e-5
累乗と浮動小数点表記

## 0x
16進数

## 0o
8進数

## 0b
2進数

# 比較

## =, ==
左辺値と右辺値が等しい

## >=, =>
左辺値は右辺値以上

## <=, =<
左辺値は右辺値以下

## >
左辺値は右辺値を超える

## <
左辺値は右辺値未満

## !=, <>
左辺値と右辺値は等しくない

## BETWEEN
array[0]以上、array[1]以下

```objectivec:BETWEEN
NSPredicate *betweenPredicate = [NSPredicate predicateWithFormat: @"attributeName BETWEEN %@", @[@1, @10]];
// ((1 <= attributeValue ) && ( attributeValue <= 10))
```

# BOOL値

## TRUEPREDICATE
常にTRUEで評価される

```objectivec:TRUEPREDICATE
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"TRUEPREDICATE"];
NSLog(@"evaluate: %@", @([predicate evaluateWithObject:[self users]]));
// evaluate: 1

NSLog(@"all users: %@", [[self users] filteredArrayUsingPredicate:predicate]);
/*
all users: (
    "firstName: Herbert, lastName: Korberg, age: 20, gender: 1",
    "firstName: Anne, lastName: Nordenskiold, age: 24, gender: 0",
    "firstName: Juanito, lastName: Suela, age: 35, gender: 1",
    "firstName: Rebecca, lastName: Vinci, age: 14, gender: 0",
    "firstName: %@, lastName: Vinci, age: 14, gender: 0"
)
*/
```

## FALSEPREDICATE
常にFALSEで評価される

```objectivec:FALSEPREDICATE
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"FALSEPREDICATE"];
NSLog(@"evaluate: %@", @([predicate evaluateWithObject:[self users]]));
// evaluate: 0

NSLog(@"all users: %@", [[self users] filteredArrayUsingPredicate:predicate]);
/*
all users: (
)
*/
```

# 論理演算

## AND, &&
論理積

## OR, ||
論理和

## NOT, !
否定

# 文字列の比較

## BEGINSWITH
右辺値から始まる文字列か

## CONTAINS
右辺値が含まれているか

## ENDSWITH
右辺値で終わる文字列か

## LIKE
右辺値と完全一致するか
ワイルドカードとして「\*」「?」が使用可能(\*は0文字以上一致、?は1文字以上一致)

## MATCHES
正規表現([ICU v3](http://userguide.icu-project.org/strings/regexp))

# 文字列比較のオプション

## c
大文字・小文字の区別なし(case-insensitive)

```objectivec:[c]
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"firstName LIKE[c] \'anne\'", attributeName];
NSLog(@"%@", [[self users] filteredArrayUsingPredicate:predicate]);
/*
(
    "firstName: Anne, lastName: Nordenskiold, age: 24, gender: 0"
)
*/
```

## d
[ダイアクリティカルマーク](http://ja.wikipedia.org/wiki/ダイアクリティカルマーク)を無視する(diacritic-insensitive)

```objectivec:[d]
NSPredicate *pridicate = [NSPredicate predicateWithFormat:@"firstName LIKE[d] \'Marka\'"];
NSLog(@"%@", [[self users] filteredArrayUsingPredicate:pridicate]);
/*
(
    "firstName: Márkä, lastName: Chèrêpkõv, age: 50, gender: 0"
)
*/
```

# 集計操作
比較で左辺値が多の要素の場合、ANY, ALL, NONEのいずれかを指定する必要がある。

## ANY, SOME
いずれかの要素が条件と一致する

```objectivec:ANY,SOME
// 1999年以降の書籍を1つ以上持っているか
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY books.year > 1999"];
NSLog(@"%@", [[self library].authors filteredArrayUsingPredicate:predicate]);
/*
(
    "<name: Herbert; books: {(<title: AAA; year: 1990>, <title: BBB; year: 2000>, <title: CCC; year: 2010>)}>",
    "<name: Juanito; books: {(<title: EEE; year: 2001>)}>"
)
*/
```

*※ authorなので持っているかではなくて書いているかだけど意味が伝わりやすいように変更してる*

## ALL
全ての要素が条件と一致する

```objectivec:ALL
// 1999年以降の書籍のみ持っているか
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ALL books.year > 1999"];
NSLog(@"%@", [[self library].authors filteredArrayUsingPredicate:predicate]);
/*
(
    "<name: Juanito; books: {(<title: EEE; year: 2001>)}>"
)
*/
```

## NONE
全ての要素が条件と一致しない

```objectivec:NONE
// 1999年以降の書籍を1つも持っていない
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NONE books.year > 1999"];
NSLog(@"%@", [[self library].authors filteredArrayUsingPredicate:predicate]);
/*
(
    "<name: Anne; books: {(<title: DDD; year: 1991>)}>"
)
*/
```

## IN
いずれかの条件と一致する(SQLのIN相当)

```objectivec:IN
// 名前がAnneまたはJuanitoか
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name IN %@", @[@"Anne", @"Juanito"]];
NSArray *filterdArray = [[self library].authors filteredArrayUsingPredicate:predicate];
NSLog(@"%@", filterdArray);
/*
(
    "<name: Anne; books: {(<title: DDD; year: 1991>)}>",
    "<name: Juanito; books: {(<title: EEE; year: 2001>)}>"
)
*/
```

## [SUBQUERY](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSExpression_Class/Reference/NSExpression.html#jumpTo_17)
サブクエリ

```objectivec:フォーマット
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SUBQUERY(books, $s, $s.year > 2000 AND $s.year < 2010).@count > 0"];
NSArray *filterdArray = [[self library].authors filteredArrayUsingPredicate:predicate];
NSLog(@"%s; %@", __func__, filterdArray);
/*
(
    "<name: Juanito; books: {(<title: EEE; year: 2001>)}>"
)
*/
```

# 2次元配列の条件一致

## array[index]
配列の配列でindexの要素らが条件と一致

```objectivec:array[index]
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF[1] = '2-B'"];
NSArray *filterdArray = [[self schoolClasses] filteredArrayUsingPredicate:predicate];
NSLog(@"%@", filterdArray);
/*
(
        (
        "2-A",
        "2-B"
    )
)
*/
```

## array[FIRST]
配列の配列で1番目の要素らが条件と一致

```objectivec:array[FIRST]
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF[FIRST] = '1-A'"];
NSArray *filterdArray = [[self schoolClasses] filteredArrayUsingPredicate:predicate];
NSLog(@"%@", filterdArray);
/*
(
        (
        "1-A",
        "1-B",
        "1-C"
    )
)
*/
```

## array[LAST]
配列の配列で最後の要素らが条件と一致

```objectivec:array[LAST]
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF[LAST] = '1-C'"];
NSArray *filterdArray = [[self schoolClasses] filteredArrayUsingPredicate:predicate];
NSLog(@"%@", filterdArray);
/*
(
        (
        "1-A",
        "1-B",
        "1-C"
    )
)
*/
```

## array[SIZE]
配列の配列でサイズが条件と一致するか

```objectivec:array[SIZE]
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF[SIZE] = 2"];
NSArray *filterdArray = [[self schoolClasses] filteredArrayUsingPredicate:predicate];
/*
(
        (
        "2-A",
        "2-B"
    )
)
*/
```

# 識別子

## C言語の識別子
## #symbol
予約語のエスケープ

## [\\]{octaldigit}{3}
8進数のエスケープ(\\)

## [\\][xX]{hexdigit}{2}
16進数のエスケープ(\x, \X)

## [\\][uU]{hexdigit}{4}
Unicodeのエスケープ(\u, \U)

# 予約語

	AND, OR, IN, NOT, ALL, ANY, SOME, NONE, LIKE, CASEINSENSITIVE, CI, MATCHES, CONTAINS, BEGINSWITH, ENDSWITH, BETWEEN, NULL, NIL, SELF, TRUE, YES, FALSE, NO, FIRST, LAST, SIZE, ANYKEY, SUBQUERY, CAST, TRUEPREDICATE, FALSEPREDICATE

# 参考サイト
[Cocoaの日々 - [Mac][iOS] NSPredicate - １対多関連のエンティティの検索条件見本](http://cocoadays.blogspot.jp/2011/01/macios-core-data.html)