web-dev-qa-db-ja.com

NSArrayによるオブジェクトの検索-ベストプラクティス

Solution: @BlackRiderの回答は、特に複雑な比較に対して最も用途が広いため、正しいとマークしましたが、他にも非常に良い回答とコメントがあります。同じ質問または類似の質問をお持ちの方には、それらを確認して、特定の状況に最適な行動方針を評価することをお勧めします。

私の状況では、実際に実装にBlackRiderのソリューションを使用していません。 @JoshCaswellのコメントと@voromaxのindexesOfObjectsWithOptions:passingTest:の提案の助けを借りて、この状況での比較が非常に簡単であるという事実により、私は独自のソリューションを使用することを選択しました(以下の編集#2を参照)。

回答し、洞察を提供してくれたすべての人に感謝します。


そのオブジェクトのプロパティ(この場合は一意の識別子)に基づいてNSArrayからオブジェクトを取得する効率的な方法を探しています。 Linqを使用したC#.NETでは、次のようなことをします

MyObject obj = myList.Single(o => o.uuid == myUUID);

また、一意でないプロパティに一致するオブジェクトの配列を取得する効率的な方法があるかどうかも疑問に思っています。繰り返しになりますが、Linqでは次のようになります。

List<MyObject> objs = myList.Where(o => o.flag == true).ToList();

もちろん、これを行うためのループを作成することはできますが、それらは再利用できず、そのパフォーマンスには疑問があります。

一意のIDを持つオブジェクトを見つける:

-(MyObject*)findObjectWithUUID:(NSString*)searchUUID{
    for (MyObject* obj in _myArray){
        if([obj.uuid isEqualToString: searchUUID])
            return obj;
    }
}

オブジェクトの配列を見つける:

-(NSArray*)findObjectsWithFlag:(BOOL)f{
    NSMutableArray* arr = [NSMutableArray array];
    for (MyObject* obj in _myArray){
        if(obj.flag == f)
            [arr addObject:obj];
    }
    return arr;
}

-編集-

幸いなことに、最初の状況では、探しているオブジェクトには一意の識別子があり、唯一の識別子があることを知っています。 indexOfObject:によって呼び出されるオブジェクトにisEqualを実装するソリューションを思い付きました

- (BOOL)isEqual:(id)object{
    return [self.uuid isEqualToString: ((MyObject*)object).uuid];
}

そして、「偽の」検索オブジェクトを作成し、それを使用して実際の検索オブジェクトを見つけます

MyObject *lookupObject = [[MyObject alloc] init];
lookupObject.uuid = searchUUID;
MyObject *actualObject = 
    [_myArray objectAtIndex:[_myArray indexOfObject:lookupObject]];

これは基本的に、上で投稿したfor-inループと同じですが、読みやすく再利用しやすいかもしれません。もちろん、これは1つの一意のオブジェクトを見つけるためだけに機能し、私の質問の後半には対応していません。

-EDIT 2-

コメントで推奨されているように、Classを確認し、hashを実装します。

- (BOOL)isEqual:(id)object{
    return [object isKindOfClass:[MyObject class]] && 
           [self.uuid isEqualToString: ((MyObject*)object).uuid];
}

- (NSUInteger)hash{
    return [self.uuid hash];
}
27
Uptown Apps

[NSPredicate]を使用できます。これにより、検索用のクエリのような構文が提供されます。述語構文の説明については、 このページ をご覧ください。以下に簡単な例を示します。

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"propertyName == %@", @"value"];
NSArray *filteredArray = [myArray filteredArrayUsingPredicate:predicate];

パフォーマンスについては、配列内の検索ではすべての要素を繰り返し処理する必要があるため、ソリューションは問題ないと思います。次に、オブジェクトごとに、フィールドの値と検索する値を比較します。同じデータ内での繰り返し検索を最適化できます。いくつかのフィールドの値を一致するオブジェクト(またはマッピングが1対多の場合はオブジェクトのコレクション)にマップするディクショナリを作成してデータを追加します。

37
Macondo2Seattle

最新のブロック構文もご覧ください: indexOfObjectWithOptions:passingTest: または indexesOfObjectsWithOptions:passingTest: 並行性と検索順序をサポートします。

14
voromax

Rmaddysのコメントに興味を持ったので、ループと述語の違いを確認しました。

NSStringプロパティを持つ単純なオブジェクトを想定しましょう。毎回異なるプロパティ値で配列に10 000回挿入しました。

目的のオブジェクトが配列の最後の位置にある最悪のシナリオでは、ループアプローチはNSPredicateよりも3.5倍高速でした(0.39秒対0.11秒、arraySize = 10000、10回の反復、iPad Mini)

参考に使用したコード: Pastebin

10
Szymon Kuczur

nSArrayを検索する最速の方法は、興味のある人のために、バックグラウンドスレッドでforループを使用することです。 [self performSelectorInBackground ...]メソッドを使用します。 10000個のカスタムオブジェクトのNSArrayでは、約1秒で全体を徹底的に検索しました。メインスレッドでは、約10秒以上かかりました。

3
RASS

NSArrayと関連していることは知っていますが、Swiftを使用して、Swift配列であるstructを使用してそれを行うと、ずっと簡単に。

Swift 2.2/Swift 3.0/Swift 4.xすべてのバージョンで正常に動作しています

カスタムモデルクラスがあると仮定しましょう

class User {
    var userId = 0
    var userName = ""
} 

そして、usersArrayクラスのカスタムオブジェクトを持つUserという名前の配列があると仮定しましょう。

そして、userId = 100を使用して、この配列からオブジェクトを取得します。例:-

let filteredArray = usersArray.filter({$0.userId == 100}) 

このフィルターされた配列には、userIdが100であるすべてのカスタムオブジェクトが含まれます。

print(filteredArray[0].userName) //will print the name of the user with userId = 100
3