web-dev-qa-db-fra.com

NSArray trouver un ou des objets - meilleures pratiques

Solution: J'ai marqué la réponse de @ BlackRider comme correcte car elle est la plus polyvalente, en particulier pour les comparaisons complexes, mais il existe d'autres très bonnes réponses et commentaires. J'encourage toute personne ayant la même question ou une question similaire à les examiner et à évaluer la meilleure ligne de conduite pour votre situation spécifique.

Dans ma situation, je n'utilise en fait pas la solution de BlackRider dans mon implémentation. J'ai choisi d'utiliser ma propre solution (voir l'édition n ° 2 ci-dessous) avec l'aide des commentaires de @ JoshCaswell ainsi que de la suggestion de @ voromax de indexesOfObjectsWithOptions:passingTest: du fait que mes comparaisons sont très simples dans cette situation.

Merci à tous ceux qui ont répondu et fourni des informations.


Je cherche un moyen efficace de récupérer un objet à partir d'un NSArray basé sur une propriété de cet objet (un identifiant unique, dans ce cas). En C # .NET en utilisant Linq, je ferais quelque chose comme

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

Je me demande également s'il existe un moyen efficace d'obtenir un tableau d'objets correspondant à une propriété non unique. Encore une fois, avec Linq, cela ressemblerait à

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

Bien sûr, je peux écrire des boucles pour cela, mais elles ne seraient pas réutilisables et je me méfie de leurs performances.

Recherche d'un objet avec un ID unique:

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

Recherche d'un tableau d'objets:

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

- MODIFIER -

Heureusement dans la première situation, l'objet que je recherche a un identifiant unique et je sais qu'il n'y en aura qu'un. J'ai trouvé une solution pour implémenter isEqual sur mon objet qui sera invoquée par indexOfObject:

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

Et puis créez un "faux" objet de recherche et utilisez-le pour trouver le vrai

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

C'est essentiellement la même que la boucle for-in que j'ai postée ci-dessus, mais pourrait être plus lisible et plus réutilisable. Bien sûr, cela ne fonctionne que pour trouver un objet unique et ne répond pas à la seconde moitié de ma question.

- EDIT 2 -

Vérifier Class et implémenter hash comme recommandé dans les commentaires.

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

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

Vous pouvez utiliser [NSPredicate], qui vous donne une syntaxe de recherche pour la recherche. Consultez cette page pour la description de la syntaxe du prédicat. Voici un exemple simple:

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

En ce qui concerne les performances, je pense que votre solution est correcte car toute recherche dans un tableau doit de toute façon parcourir tous les éléments, puis, pour chaque objet, comparer la valeur d'un champ à la valeur que vous recherchez. Vous pouvez optimiser les recherches répétées dans les mêmes données, par exemple en créant et en remplissant un dictionnaire qui mappe les valeurs de certains champs aux objets correspondants (ou collections d'objets, si le mappage est un à plusieurs).

37
Macondo2Seattle

Vous pouvez également consulter la syntaxe de bloc moderne: indexOfObjectWithOptions:passingTest: ou indexesOfObjectsWithOptions:passingTest: qui prennent en charge la concurrence et l'ordre de recherche.

14
voromax

J'ai été intrigué par les commentaires de rmaddys, j'ai donc vérifié la différence entre le bouclage et le prédicat.

Supposons un objet simple avec la propriété NSString. Je l'ai inséré dans le tableau 10 000 fois, à chaque fois avec une valeur de propriété différente.

Dans le pire des cas, lorsque l'objet souhaité se trouvait sur la dernière position du tableau, l'approche en boucle était 3,5 fois plus rapide que NSPredicate (0,39 s vs 0,11 s, arraySize = 10000, 10 itérations, iPad Mini)

Code que j'ai utilisé comme référence: Pastebin

10
Szymon Kuczur

juste pour ceux qui sont intéressés, j'ai trouvé le moyen le plus rapide de rechercher dans NSArray en utilisant une boucle for sur un thread d'arrière-plan. en utilisant la méthode [self performSelectorInBackground ...]. Dans un NSArray de 10000 objets personnalisés, j'ai parcouru le tout à fond en environ 1 seconde. Sur le fil principal, cela a pris environ 10 secondes ou plus.

3
RASS

Je sais que c'est lié à NSArray mais si nous le faisons en utilisant Swift et en utilisant le tableau Swift qui est un struct, alors ce sera être beaucoup plus facile.

Swift 2.2/Swift 3.0/Swift 4.x Fonctionne bien sur toutes les versions)

Supposons que nous ayons une classe de modèle personnalisée

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

Et supposons que nous avons un tableau nommé usersArray qui a des objets personnalisés de la classe User.

Et nous voulons récupérer un objet de ce tableau avec userId = 100 par exemple:-

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

Ce tableau filtré contiendra tous les objets personnalisés qui ont userId comme 100

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