Objective-CでNSArrayをフィルタリングして新しいNSArrayにする


121

私が持っているNSArrayと私は新しいを作成したいのですがNSArray一定の基準を満たして元の配列からオブジェクトを。基準は、を返す関数によって決定されますBOOL

を作成NSMutableArrayし、ソース配列を反復処理して、フィルター関数が受け入れるオブジェクトをコピーしてから、その不変バージョンを作成できます。

もっと良い方法はありますか?

回答:


140

NSArray及びNSMutableArrayフィルタアレイの内容に方法を提供します。指定された述語と一致するレシーバー内のオブジェクトを含む新しい配列を返すfilteredArrayUsingPredicateをNSArray提供します。filterUsingPredicate:を追加します。これは、指定された述語に対してレシーバーのコンテンツを評価し、一致するオブジェクトのみを残します。これらのメソッドを次の例に示します。NSMutableArray

NSMutableArray *array =
    [NSMutableArray arrayWithObjects:@"Bill", @"Ben", @"Chris", @"Melissa", nil];

NSPredicate *bPredicate =
    [NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"];
NSArray *beginWithB =
    [array filteredArrayUsingPredicate:bPredicate];
// beginWithB contains { @"Bill", @"Ben" }.

NSPredicate *sPredicate =
    [NSPredicate predicateWithFormat:@"SELF contains[c] 's'"];
[array filteredArrayUsingPredicate:sPredicate];
// array now contains { @"Chris", @"Melissa" }

84
Papa Smurfのポッドキャストを聞いたところ、コミュニティが評価を改善できるように回答をStackOverflowに含める必要があるとPapa Smurfは言いました。
willc2 2009

10
@mmalc-より適切かもしれませんが、ここで表示する方が確かに便利です。
ブライアン

5
NSPredicateは死んで、長いライブブロックです!cf. 以下の私の答え。
クレイブリッジ、

contains [c]の意味?[c]は常に表示されますが、何をしているのかわかりません。
user1007522 2014年

3
@ user1007522、[c]は大文字と小文字を区別せずに一致します
jonbauer 2014年

104

これを行う方法はたくさんありますが、最も近いものは確かに次のものを使用してい[NSPredicate predicateWithBlock:]ます:

NSArray *filteredArray = [array filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
    return [object shouldIKeepYou];  // Return YES for each object you want in filteredArray.
}]];

それは、できるだけ簡潔なものだと思います。


迅速:

NSArraySwift でsを使用する人にとっては、これをさらに簡潔なバージョンにすることをお勧めします。

nsArray = nsArray.filter { $0.shouldIKeepYou() }

filterは単なるメソッドですArrayNSArray暗黙的にSwiftにブリッジされArrayます)。引数は1つBoolです。配列内の1つのオブジェクトを取り、を返すクロージャです。クロージャでtrueは、フィルタリングされた配列で必要なオブジェクトを返すだけです。


1
NSDictionary *バインディングがここで果たす役割は何ですか?
2015

@KaitainバインディングはNSPredicate predicateWithBlock:APIで必要です。
ThomasW 2015

@Kaitain bindings辞書には、テンプレートの変数バインディングを含めることができます。
Amin Negm-Awad

複雑なオブジェクトを処理するときに、受け入れられた回答よりもはるかに有用です:)
Ben Leggiero

47

Clay Bridgesの回答に基づいて、ブロックを使用したフィルタリングの例を次に示します(yourArray配列変数名とtestFuncテスト関数の名前を)。

yourArray = [yourArray objectsAtIndexes:[yourArray indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
    return [self testFunc:obj];
}]];

最後に、答えは、ブロックを使用したフィルタリングに言及するだけでなく、それを行う方法についての良い例も示しています。ありがとう。
クリスティアン2016年

私はこの答えが好きです。たとえfilteredArrayUsingPredicateあなたが不明瞭一種の目的を任意の述語を使用していないという事実は、スリムです。
James Perih

これは、これを行うための最も近代的な方法です。個人的には、ある時点でAPIから消えた古い省略形の「objectsPassingTest」を待ち望んでいます。それでも、これは高速で良好に実行されます。私はNSPredicateが好きですが、もっと重いハンマーが必要な他のことについては
Motti Shneor

ここでエラーが発生しますImplicit conversion of 'NSUInteger' (aka 'unsigned long') to 'NSIndexSet * _Nonnull' is disallowed with ARC... NSIndexSetsを期待しています
anoop4real

@ anoop4real、あなたが言及した警告の原因は、のindexOfObjectPassingTest代わりに誤って使用したことだと思いますindexesOfObjectsPassingTest。見逃しやすいが、大きな違い:)
pckill

46

OS X 10.6 / iOS 4.0以降の場合は、NSPredicateよりもブロックの方が良いでしょう。参照してください-[NSArray indexesOfObjectsPassingTest:]または便利な追加する独自のカテゴリを作成し-select:たり-filter:する方法()。

他の誰かにそのカテゴリの作成、テストなどをしてもらいたいですか?BlocksKit配列ドキュメント)を確認してください。そして、そこにある多くによって発見されるより多くの例と言うには、例えばを探して、「選択はNSArrayのブロックカテゴリ」


5
例を挙げて答えを広げていただけませんか?ブログのウェブサイトは、最も必要なときに死ぬ傾向があります。
Dan Abramov 2012年

8
リンクの腐敗に対する保護は、関連するコードを抜粋してリンクされた記事からではなく、リンクを追加しないことです。リンクは保持しますが、いくつかのサンプルコードを追加します。
toolbear 12/07/12

3
@ClayBridges私はこの質問を見つけて、ココアをフィルタリングする方法を探しました。回答にはコードサンプルが含まれていないため、ブロックが必要なものを達成できないことを確認するためにリンクを掘り下げるのに約5分かかりました。コードが答えに含まれていた場合、おそらく30秒かかります。この答えは、実際のコードがないと、FARの有用性が低くなります。
N_A

1
@mydogisbox et alia:ここには4つの回答しかないので、コードサンプリングされた独自の光沢のある優れた回答を収める十分な余地があります。私は満足しているので、一人にしておいてください。
クレイブリッジ

2
mydogisboxはこの点で正しいです。あなたがしているすべてがリンクを提供することであるなら、たとえあなたがより多くのリンクを追加したとしても、それは実際には答えではありません。meta.stackexchange.com/questions/8231を参照してください。リトマス試験:あなたの答えはそれ自体で成り立つことができますか、それとも価値のあるリンクをクリックする必要がありますか? 特に、「NSPredicateよりもブロックの方が良いと思う」と述べていますが、その理由についてはあまり説明していません。
ロバートハーヴェイ

16

オブジェクトがすべて同様のタイプであると仮定すると、基準に使用している関数を呼び出すメソッドを、その基本クラスのカテゴリとして追加できます。次に、そのメソッドを参照するNSPredicateオブジェクトを作成します。

一部のカテゴリでは、関数を使用するメソッドを定義します

@implementation BaseClass (SomeCategory)
- (BOOL)myMethod {
    return someComparisonFunction(self, whatever);
}
@end

次に、フィルタリングする場所はどこでも:

- (NSArray *)myFilteredObjects {
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"myMethod = TRUE"];
    return [myArray filteredArrayUsingPredicate:pred];
}

もちろん、関数がクラス内から到達可能なプロパティとのみ比較する場合は、関数の条件を述語文字列に変換する方が簡単な場合があります。


優雅で短く、ショートカットなので、最も基本的なものであるNSPredicateを形式化する方法、つまりプロパティとブール型メソッドにアクセスする方法をもう一度学ぶ必要があるため、私はこの回答が気に入りました。ラッパーは必要なかったと思います。単純な[myArray FilteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@ "myMethod = TRUE"]]; 十分でしょう。ありがとう!(私は代替案も大好きですが、これは素晴らしいものです)。
Motti Shneor

3

NSPredicateフィルターに収集した状態を構築するNEXTSTEPの方法があります(NSArrayNSSetNSDictionary)。

たとえば、2つの配列arrfilteredarr

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

filteredarr = [NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];

Filteredarrには、文字cのみを含むアイテムが必ず含まれます。

SQLの背景が少しだけある人を覚えやすくするため

*--select * from tbl where column1 like '%a%'--*

1)select * from tbl- >コレクション

2)column1 like '%a%' ->NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"c"];

3)select * from tbl where column1 like '%a%' ->

[NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];

これが役に立てば幸い


2

NSArray + Xh

@interface NSArray (X)
/**
 *  @return new NSArray with objects, that passing test block
 */
- (NSArray *)filteredArrayPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate;
@end

NSArray + Xm

@implementation NSArray (X)

- (NSArray *)filteredArrayPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
{
    return [self objectsAtIndexes:[self indexesOfObjectsPassingTest:predicate]];
}

@end

このカテゴリはこちらからダウンロードできます


0

このライブラリをチェックアウト

https://github.com/BadChoice/Collection

ループを二度と書くことのない、簡単な配列関数がたくさんあります

だからあなたはただ行うことができます:

NSArray* youngHeroes = [self.heroes filter:^BOOL(Hero *object) {
    return object.age.intValue < 20;
}];

または

NSArray* oldHeroes = [self.heroes reject:^BOOL(Hero *object) {
    return object.age.intValue < 20;
}];

0

最良かつ簡単な方法は、このメソッドを作成して配列と値を渡すことです。

- (NSArray *) filter:(NSArray *)array where:(NSString *)key is:(id)value{
    NSMutableArray *temArr=[[NSMutableArray alloc] init];
    for(NSDictionary *dic in self)
        if([dic[key] isEqual:value])
            [temArr addObject:dic];
    return temArr;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.