Dans Cocoa, si je souhaite parcourir un NSMutableArray et supprimer plusieurs objets qui répondent à certains critères, quelle est la meilleure façon de procéder sans redémarrer la boucle chaque fois que je supprime un objet?
Merci,
Edit: Juste pour clarifier - je cherchais la meilleure façon, par exemple quelque chose de plus élégant que de mettre à jour manuellement l'index où je suis. Par exemple en C ++ je peux faire;
iterator it = someList.begin();
while (it != someList.end())
{
if (shouldRemove(it))
it = someList.erase(it);
}
objective-c
cocoa
Andrew Grant
la source
la source
Réponses:
Pour plus de clarté, j'aime faire une boucle initiale où je collecte les éléments à supprimer. Ensuite, je les supprime. Voici un exemple utilisant la syntaxe Objective-C 2.0:
Ensuite, il n'est pas question de savoir si les indices sont mis à jour correctement ou d'autres petits détails de comptabilité.
Modifié pour ajouter:
Il a été noté dans d'autres réponses que la formulation inverse devrait être plus rapide. c'est-à-dire si vous parcourez le tableau et composez un nouveau tableau d'objets à conserver, au lieu d'objets à jeter. Cela peut être vrai (mais qu'en est-il du coût de mémoire et de traitement pour allouer un nouveau tableau et éliminer l'ancien?), Mais même s'il est plus rapide, ce n'est peut-être pas aussi grave que pour une implémentation naïve, car NSArrays ne se comportent pas comme des tableaux "normaux". Ils parlent mais ils marchent différemment. Voir une bonne analyse ici:
La formulation inverse peut être plus rapide, mais je n'ai jamais eu besoin de m'en soucier, car la formulation ci-dessus a toujours été assez rapide pour mes besoins.
Pour moi, le message à retenir est d'utiliser la formulation la plus claire pour vous. Optimisez uniquement si nécessaire. Personnellement, je trouve la formulation ci-dessus la plus claire, c'est pourquoi je l'utilise. Mais si la formulation inverse est plus claire pour vous, allez-y.
la source
Encore une variante. Ainsi, vous obtenez une lisibilité et de bonnes performances:
la source
removeObjectsAtIndexes
est la pire méthode pour retirer les objets, êtes-vous d'accord avec cela? Je pose cette question parce que votre réponse est trop ancienne maintenant. Encore faut-il choisir le meilleur?enumerateObjectsUsingBlock:
vous obtiendrait l'incrément d'index gratuitement.Il s'agit d'un problème très simple. Vous venez d'itérer en arrière:
Il s'agit d'un schéma très courant.
la source
Certaines des autres réponses auraient de mauvaises performances sur de très grands tableaux, car les méthodes comme
removeObject:
etremoveObjectsInArray:
impliquent de faire une recherche linéaire du récepteur, ce qui est un gaspillage parce que vous savez déjà où se trouve l'objet. Aussi, tout appel àremoveObjectAtIndex:
devra copier les valeurs de l'index à la fin du tableau vers le haut d'un emplacement à la fois.Plus efficace serait le suivant:
Parce que nous définissons la capacité de
itemsToKeep
, nous ne perdons pas de temps à copier des valeurs pendant un redimensionnement. Nous ne modifions pas le tableau en place, nous sommes donc libres d'utiliser l'énumération rapide. UtilisersetArray:
pour remplacer le contenu dearray
avecitemsToKeep
sera efficace. Selon votre code, vous pouvez même remplacer la dernière ligne par:Il n'est donc même pas nécessaire de copier les valeurs, il suffit d'échanger un pointeur.
la source
Vous pouvez utiliser NSpredicate pour supprimer des éléments de votre tableau mutable. Cela ne nécessite aucune boucle.
Par exemple, si vous avez un NSMutableArray de noms, vous pouvez créer un prédicat comme celui-ci:
La ligne suivante vous laissera un tableau qui ne contient que des noms commençant par b.
Si vous ne parvenez pas à créer les prédicats dont vous avez besoin, utilisez ce lien développeur Apple .
la source
J'ai fait un test de performance en utilisant 4 méthodes différentes. Chaque test a parcouru tous les éléments d'un tableau de 100 000 éléments et a supprimé tous les 5 éléments. Les résultats n'ont pas beaucoup varié avec / sans optimisation. Cela a été fait sur un iPad 4:
(1)
removeObjectAtIndex:
- 271 ms(2)
removeObjectsAtIndexes:
- 1010 ms (car la construction de l'index prend environ 700 ms; sinon, cela revient essentiellement à appeler removeObjectAtIndex: pour chaque élément)(3)
removeObjects:
- 326 ms(4) créer un nouveau tableau avec des objets réussissant le test - 17 ms
La création d'un nouveau tableau est donc de loin la plus rapide. Les autres méthodes sont toutes comparables, sauf que l'utilisation de removeObjectsAtIndexes: sera pire avec plus d'éléments à supprimer, en raison du temps nécessaire pour construire l'ensemble d'index.
la source
Soit utiliser le compte à rebours en boucle sur les indices:
ou faites une copie avec les objets que vous souhaitez conserver.
En particulier, n'utilisez pas de
for (id object in array)
boucle ouNSEnumerator
.la source
Pour iOS 4+ ou OS X 10.6+, Apple a ajouté une
passingTest
série d'API dansNSMutableArray
, comme– indexesOfObjectsPassingTest:
. Une solution avec une telle API serait:la source
De nos jours, vous pouvez utiliser une énumération inversée basée sur des blocs. Un exemple de code simple:
Résultat:
une autre option avec une seule ligne de code:
la source
De manière plus déclarative, selon les critères correspondant aux éléments à supprimer, vous pouvez utiliser:
@Nathan devrait être très efficace
la source
Voici la manière simple et propre. J'aime dupliquer mon tableau directement dans l'appel d'énumération rapide:
De cette façon, vous effectuez l'énumération à travers une copie du tableau à supprimer, les deux contenant les mêmes objets. Un NSArray ne contient que des pointeurs d'objet, ce qui est donc très bon en termes de mémoire / performances.
la source
for (LineItem *item in self.lineItems.copy)
Ajoutez les objets que vous souhaitez supprimer à un deuxième tableau et, après la boucle, utilisez -removeObjectsInArray :.
la source
cela devrait le faire:
J'espère que cela t'aides...
la source
Pourquoi n'ajoutez-vous pas les objets à supprimer à un autre NSMutableArray. Une fois l'itération terminée, vous pouvez supprimer les objets que vous avez collectés.
la source
Que diriez-vous de permuter les éléments que vous souhaitez supprimer avec le 'n'th élément,' n-1'th élément et ainsi de suite?
Lorsque vous avez terminé, vous redimensionnez le tableau à la `` taille précédente - nombre de swaps ''
la source
Si tous les objets de votre tableau sont uniques ou si vous souhaitez supprimer toutes les occurrences d'un objet lorsqu'ils sont trouvés, vous pouvez énumérer rapidement une copie du tableau et utiliser [NSMutableArray removeObject:] pour supprimer l'objet de l'original.
la source
+arrayWithArray
son exécution?La réponse de benzado ci-dessus est ce que vous devez faire pour la préforme. Dans l'une de mes applications, removeObjectsInArray a pris 1 minute, l'ajout à un nouveau tableau a pris 0,023 seconde.
la source
Je définis une catégorie qui me permet de filtrer à l'aide d'un bloc, comme ceci:
qui peut ensuite être utilisé comme ceci:
la source
Une meilleure implémentation pourrait être d'utiliser la méthode de catégorie ci-dessous sur NSMutableArray.
Le bloc de prédicat peut être implémenté pour effectuer un traitement sur chaque objet du tableau. Si le prédicat renvoie true, l'objet est supprimé.
Un exemple de tableau de dates pour supprimer toutes les dates du passé:
la source
Itérer en arrière était mon préféré pendant des années, mais pendant longtemps je n'ai jamais rencontré le cas où l'objet «le plus profond» (nombre le plus élevé) avait été retiré en premier. Momentanément avant que le pointeur ne passe à l'index suivant, il n'y a rien et il se bloque.
La méthode de Benzado est la plus proche de ce que je fais maintenant, mais je n'ai jamais réalisé qu'il y aurait un remaniement de la pile après chaque suppression.
sous Xcode 6 cela fonctionne
la source