Optimisation: Eviter 2 boucles imbriquées?

Salut à  tous,


Aujourd'hui je ne vais pas poser de problèmes quoi que...  ::)


C'est plus un cas d'école qu'autre chose.


Je voudrais optimiser mon code suivant:



for (NSString* memberId in members){
for (Member* member in self.arrayOfMembers) {
if ([member.memberId isEqualToString:memberId]) {
if ([member isEqual:self.currentConnected])
[self action];
[self.arrayOfMembers removeObject:member];
}
}
}

Ma méthode reçoit un array de string members, et j'ai 2 variable d'instance:


- arrayOfMembers


- currentConnected (string)


 


Le but étant d'éviter les boucles imbriquées (entre autres).


Voilà , Merci d'avance pour vos conseils et astuce :)


 


 


Réponses

  • AliGatorAliGator Membre, Modérateur
    novembre 2013 modifié #2
    NSArray* memberIDs = [members valueForKey:@memberId];
    for (Member* member in self.arrayOfMembers) {
    if ([memberIDs containsObject:member.memberId]) {
    if ([member isEqual:self.currentConnected])
    [self action];
    [self.arrayOfMembers removeObject:member];
    }
    }
    [EDIT] Attention cependant modifier un objet mutable pendant son itération est déconseillé...

    Plutôt que [self.arrayOfMembers removeObject:member], qui modifie l'objet self.arrayOfMembers sur laquelle ta boucle for est en train d'itérer, moi je prévoirais plutôt un NSMutableArray objectsToRemove avant la boucle for, j'ajouterais member à  objectsToRemove dans le if à  la place du removeObject, et je ferais un "removeObjects:" à  la fin de la boucle.
  • SmySmy Membre
    novembre 2013 modifié #3

    Pour la lisibilité dans ta boucle (oui, ce n'est pas la demande principale des deux boucles imbriquées :) )



    if( ![member.memberId isEqualToString:memberId] )
    continue;

    if( [member isEqual:self.currentConnected] )
    [self action];
    [self.arrayOfMembers removeObject:member];




  • for (Member* member in self.arrayOfMembers) {
    if ([members containsObject:member.memberId]) {
    if ([member isEqual:self.currentConnected])
    [self action];
    [self.arrayOfMembers removeObject:member];
    }
    }

    [EDIT] Attention cependant modifier un objet mutable pendant son itération est déconseillé...


    Plutôt que [self.arrayOfMembers removeObject:member], qui modifie l'objet self.arrayOfMembers sur laquelle ta boucle for est en train d'itérer, moi je prévoirais plutôt un NSMutableArray objectsToRemove avant la boucle for, j'ajouterais member à  objectsToRemove dans le if à  la place du removeObject, et je ferais un "removeObjects:" à  la fin de la boucle.

     




     


    Tu sais que je fais l'erreur à  chaque fois  <_<


    Et après je m'étonne d'avoir des crash  -_-


     


    Merci Ali de me l'avoir fait remarquer avant mes test ^^'



  • [EDIT] Attention cependant modifier un objet mutable pendant son itération est déconseillé...




     


    ... je découvre... même si ça paraà®t logique. 


     


    Jusqu'à  maintenant, je m'y suis prise en utilisant une boucle for.... à  l'envers... 


    Ce n'est peut-être pas très clean ?

  • SmySmy Membre
    novembre 2013 modifié #6

    Une autre solution est de ne pas utiliser la fast enumeration, non ?



    for( int index = 0; index < [self.arrayOfMembers count]; ) {
    Member *member = [self.arrayOfMembers objectAtIndex:index];

    if( ... )
    [self.arrayOfMembers removeObjectAtIndex:index];
    else
    index++;
    }

  • AliGatorAliGator Membre, Modérateur

    Un autre solution est de ne pas utiliser la fast enumeration, non ?


    for( int index = 0; index < [self.arrayOfMembers count]; ) {
    Member *member = [self.arrayOfMembers objectAtIndex:index];

    if( ... )
    [self.arrayOfMembers removeObjectAtIndex:index];
    else
    index++;
    }

    Perso je trouve que c'est encore pire à  lire. Car tu as un décalage d'indice dû au removeObject, et en effet tu ne fais pas le "++" si tu supprimes, mais sans mettre de commentaires ce n'est pas idéal à  comprendre. (Et si on veut être tatillon il faut plutôt écrire "++index" que "index++" dans ce cas).
    Et en plus cela oblige à  recalculer [self.arrayOfMembers count] à  chaque itération ;)

    Je préfère la méthode de Booleanne de faire une itération à  l'envers, c'est plus lisible et plus optimal.

    PS : J'ai mis dans mon post plus haut que c'était "déconseillé" de modifier un objet mutable pendant sa propre itération... je dirais même que c'est interdit. Normalement quand on utilise la syntaxe de NSFastEnumeration (enumerateObjects...), on a une exception explicite disant "can't modify a mutable object while enumerating on it" un truc comme ça. Qu'on a pas quand on fait une boucle for indicée.


  • Perso je trouve que c'est encore pire à  lire. Car tu as un décalage d'indice dû au removeObject, et en effet tu ne fais pas le "++" si tu supprimes, mais sans mettre de commentaires ce n'est pas idéal à  comprendre. (Et si on veut être tatillon il faut plutôt écrire "++index" que "index++" dans ce cas).

    Et en plus cela oblige à  recalculer [self.arrayOfMembers count] à  chaque itération ;)

     




     


    Ce n'est pas parfait non plus, je te l'accorde, mais ça a l'avantage de la compacité.


     


    Je trouve ta version avec NSMutableArray objectsToRemove un peu longue pour le coup, comme quoi :)

  • AliGatorAliGator Membre, Modérateur

    Ce n'est pas parfait non plus, je te l'accorde, mais ça a l'avantage de la compacité.
     
    Je trouve ta version avec NSMutableArray objectsToRemove un peu longue pour le coup, comme quoi :)

    Yep, c'est pour ça que j'ai dit que la méthode de booleanne était le meilleur compromis ;)
  • BooleanneBooleanne Membre
    novembre 2013 modifié #10

    Pour le meilleur rapport : qualité/masse de travail, je me débrouille plutôt pas trop mal... Je suis très économe dans ce domaine.... plus je pense, et moins j'en fais... ou l'éloge de la paresse   ;)


  • AliGatorAliGator Membre, Modérateur
    Sinon une autre solution c'est encore d'utiliser les méthodes dédiées de NSArray/NSMutableArray.

    Donc il y en a une première pour avoir directement les indices des objets qui passent un certain test, ça peut déjà  être une première solution :
    NSArray* memberIDs = [members valueForKey:@memberId];
    // Récupérer les indices des membres dont l'ID est dans memberIDs, donc des membres à  supprimer du tableau
    NSIndexSet* indexes = [self.arrayOfMembers indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
    return [memberIDs containsObject:member.memberId];
    }];
    if ([memberIDs containsObject:self.currentConnected.memberId]) [self action];
    [self.arrayOfMembers removeObjectsAtIndexes:indexes];
    Et sinon demander directement de filtrer peut en être une autre, avec la méthode filterUsingPredicate :
    NSArray* memberIDs = [members valueForKey:@memberId];
    if ([memberIDs containsObject:self.currentConnected.memberId]) [self action];
    // Ne garder QUE les membres dont l'ID n'est pas dans memberIDs (et supprimer les autres du tableau)
    NSPredicate* predicate = [NSPredicate predicateWithFormat:@NOT SELF.memberId IN %@", memberIDs];
    [self.arrayOfMembers filterUsingPredicate:predicate];
    Bref, c'est pas les solutions qui manquent ;)
Connectez-vous ou Inscrivez-vous pour répondre.