:::: MENU ::::

Sublimer ses boucles en javascript

Cet article aurait pu s’appeler tout bêtement Récupérer un objet dans un tableau : les différentes méthodes mais ça sonnait moins. Derrière ce titre on va en fait voir les différents moyens de traiter une opération assez récurrente courante qu’est la sélection d’un objet dans un tableau.

For … if

La méthode la plus triviale est bien évidemment de faire une boucle et un test sur l’élément souhaité.


for(var i=0;i<monTableau.length;i++){
  if(monTableau[i].id === idDemande ) {
    objetCherche = monTableau[i].id;
    break;
  }
}

Simple, efficace, ultra standard et compatible jusqu’aux fin fond des IE.On peut facilement sortir de la boucle. Coté perf c’est un must ( si on pense à mettre en cache la taille du tableau, entre autres )

forEach

Commençons par une petite référence à la documentation  et une liste de compatibilité. A moins d’être < à IE9 ( encore et toujours ce IE… ) , ça semble plutôt bon. Le forEach est une première approche de la sublimation itérative et une chose est sure, quand on commence à l’utiliser pour faire des boucles, il est dur de s’arrêter. Mais il est aussi très difficile de stopper un forEach ! C’est impossible en fait. Pas vraiment grave quand on a que quelques éléments  mais si on veut éviter de lire un gros tableau c’est gênant.


monTableau.forEach(function(val) {
 if(val.id===5) {
   objetCherche = val ;
 }
});

En bidouillant on peut quand même s’en sortir en  levant un erreur mais il y a mieux.

Some et Every

C’est en quelque sorte deux manières un peu plus élégantes de faire un forEach et c’est autant supporté  . Ce sont les méthodes conseillées dans la documentation du forEach.

monTableau.every(function(val) {
 if (val.id === idDemande) {
   objetCherche = val ;
   return false;
 }
 return true;
});

L’exemple est le même pour Array.some sauf qu’il faut inverser return true à false et return false à true.

Filter

Ce que j’aime bien dans cette méthode, c’est qu’elle retourne l’élément demandé …


objetCherche = monTableau.filter(function (element) {
 return element.id === 5;
})[0];

.. mais la encore, en traversant l’ensemble du tableau. Retour à la case départ.

Les alternatives

A priori, le meilleur compromis en utilisant des fonctions natives semble être l’utilisation de some ou every. Mais dans un monde idéal on aimerait pouvoir récupérer directement notre résultat dans une variable .

Retour aux fondamentaux

Finalement, une petite fonction avec notre for de départ fait bien l’affaire .

function findElementById(tableau, id) {
  var valeurcherche;
  for(var i=0;i<tableau.length;i++){
    if(tableau[i].id === id ) {
      valeurcherche = tableau[i].id;
      break;
    }
  }
  return valeurcherche;
}

Les librairies

Si vous utilisez Underscore  Lo-Dash , il y a une belle fonction qui répond exactement à notre besoin .


objetCherche =_.find(monTableau, function(element) {
 return element.id === idDemande;
});

Et elle retourne le résultat sans passer tout le tableau.

Performances

Bien évidemment, les boucles for natives sont les plus performantes. La bonne surprise vient de l’implémentation find de lowDash qui en plus d’être pratique s’avère être tout à fait correcte. Le forEach fait parti des mauvais élèves mais comme il parcourt l’ensemble du tableau quoi qu’il arrive, plus la taille augmente pire c’est. Le plus mauvais résultat revient à la méthode par Array.filter.

arrayperf

 

http://jsperf.com/best-way-to-select-an-object-into-an-array

Performances des boucles natives

Afin de se faire une idée plus précise des performances et micro optimisations des boucles natives. Voici un deuxième test :

native-loop-performance-javascript

 

Exemples

See the Pen lvEFx by Maxime Warnier (@maxdow) on CodePen.5209

Conclusion

On l’a vu, il y a différents moyen d’arriver à ses fin lorsqu’on cherche un élément ( et un seul dans le cas de cet article ) dans un tableau. A vous de choisir la méthode qui vous convient le mieux, suivant l’utilisation et le besoin en performance.  Si vous en avez d’autres n’hésitez pas à rajouter dans les commentaires.


4 Comments

  • Guest |

    Petite optimisation, il me semble que lorsque l’on fait « (var i=0;i<tableau.length;i++)" la longueur du tableau est recalculée à chaque itération, ne serait il pas plus judicieux de déclarée une variable contenant la longueur du tableau ?

    • maxdow |

      merci du retour. Oui c’est pour ça que je précise « si on pense à mettre en cache la taille du tableau » . La pour l’exemple c’est le plus minimaliste possible, mais pour optimiser, le plus simple est la mise en cache de la taille de tableau ( je pense même que c’est une bonne pratique à faire quasi systématiquement ).
      Ensuite tu pourra dans le lien vers les tests de perfs, il semblerait que la décrémentation soit plus performante que i++. Il y a aussi la manière de tester. C’est plus efficace de tester une inégalité !== que une infériorité par exemple.
      Généralement la mise en cache suffit , le reste c’est vraiment des micro-optimisations à faire sur des usages bien précis car souvent inutiles.

      • KDO |

        Je pense que ce n’est pas toujours vrai, length est une property en javascript, non une fonction. Tant que l’on ne modifie pas un tableau elle ne change pas, elle n’est donc pas recalculée à chaque fois. C’est pour cela que certains navigateurs prennent un peu plus de temps quand la longueur est cachée (création d’une variable pour le cache), c’est nettement visible sur les graphes JSPERF…. et visiblement ça depend du navigateur.

        • maxdow |

          oui tu as totalement raison. En fait cette bonne pratique ne l’est pas tant que ça car finalement elle ne rend pas le code plus maintenable , au contraire. C’est comme tu le dis une propriété, donc la valeur est stockée. Ce qui change c’est l’accession à cette propriété. La le temps que met la navigateur dépend de son moteur. Avant (IE<=9), cette mise en cache avait des répercutions, aujourd’hui, je pense que tous sinon la plupart mettent en place cette micro-optimisation. A partir de 10 000 élements dans un tableau, il peut être bon de regarder les différences de performance entre navigateurs et si cette mise en cache est nécessaire ou non. Personnellement je n'utilise quasiment plus que le forEach, plus "joli". Un gouffre en terme de perf mais tellement négligeable sur peu d'éléments, même pour un puriste .