comment casser la fonction _.each dans underscore.js

200

Je cherche un moyen d'arrêter les itérations de la _.each()méthode underscore.js , mais je ne trouve pas la solution. jQuery .each()peut se casser si vous le faites return false.

Existe-t-il un moyen d'arrêter le trait de soulignement each ()?

_([1,2,3]).each(function(v){
    if (v==2) return /*what?*/;
})
dy_
la source
4
Je ne pense pas que ce soit possible, car la forEachfonction native n'offre pas non plus cette fonctionnalité.
Felix Kling
8
Normalement, lors de l'utilisation eachavec une fermeture (dans la plupart des langues), vous souhaitez d'abord filtrer votre liste. De cette façon, vous n'avez pas à vous soucier d'en rompre. De manière générale, si vous devez interrompre tôt une itération, il existe probablement une manière différente de procéder.
Rob Hruska
Voici quelques questions liées à Groovy, où le comportement (incapacité à rompre facilement une eachfermeture) est similaire à JavaScript.
Rob Hruska
@Dmitry_F, comme d'autres l'ont noté, vous ne pouvez pas faire exactement ce que vous demandez. Mais comme je l'ai démontré, vous pouvez utiliser Array.everypour émuler le comportement que vous souhaitez.
aeskr
@Rob. À votre santé. Premier commentaire vraiment utile. En fait, il y avait une façon différente de procéder.
net.uk.sweet

Réponses:

267

Vous ne pouvez pas rompre avec la eachméthode: elle émule le forEachcomportement de la méthode native et le natif forEachne fournit pas d'échapper à la boucle (à l'exception de lever une exception).

Cependant, tout espoir n'est pas perdu! Vous pouvez utiliser la Array.everyméthode. :)

À partir de ce lien:

everyexécute la callbackfonction fournie une fois pour chaque élément présent dans le tableau jusqu'à ce qu'il en trouve un où callbackrenvoie une valeur fausse. Si un tel élément est trouvé, la everyméthode renvoie immédiatement false.

En d'autres termes, vous pourriez faire quelque chose de compliqué comme ça ( lien vers JSFiddle ):

[1, 2, 3, 4].every(function(n) {
    alert(n);
    return n !== 3;
});

Cela alertera 1par 3, puis « casser » hors de la boucle.

Vous utilisez underscore.js, vous serez heureux d'apprendre qu'il ne fournit un everyce méthode qu'ils appellent every, mais comme ce lien mentionne, ils fournissent également un alias all.

aeskr
la source
2
Est-ce que underscore.js fournit également une implémentation pour cela?
Felix Kling
1
@FelixKling, oui. J'ai ajouté cela à ma réponse.
aeskr
2
en ce moment (05/2013), il n'y a _.every()ni _.all()méthode ni méthode pour les tableaux en trait de soulignement - alors respectez le Array.every().
pkyeck
3
Cela fonctionnera, mais c'est une raison habituelle d'utilisation every. Faites donc attention à la lisibilité.
evanrmurphy
3
La documentation de soulignement pour _.each()contient une note spécifique sur le fait que vous ne pouvez pas sortir de la boucle et vous recommande d'utiliser à la _.find()place. http://underscorejs.org/#each
blatt
70

Mettre à jour:

_.find serait mieux car il sort de la boucle lorsque l'élément est trouvé:

var searchArr = [{id:1,text:"foo"},{id:2,text:"bar"}];
var count = 0;
var filteredEl = _.find(searchArr,function(arrEl){ 
              count = count +1;
              if(arrEl.id === 1 ){
                  return arrEl;
              }
            });

console.log(filteredEl);
//since we are searching the first element in the array, the count will be one
console.log(count);
//output: filteredEl : {id:1,text:"foo"} , count: 1

** Vieux **

Si vous souhaitez rompre conditionnellement une boucle, utilisez _.filter api au lieu de _.each. Voici un extrait de code

var searchArr = [{id:1,text:"foo"},{id:2,text:"bar"}];
var filteredEl = _.filter(searchArr,function(arrEl){ 
                  if(arrEl.id === 1 ){
                      return arrEl;
                  }
                });
console.log(filteredEl);
//output: {id:1,text:"foo"}
Nikhil
la source
1
cela ne rompt pas la boucle - il filtre simplement le tableau. imaginez que vous n'avez pas 2 mais 20 000 articles dans le tableau. votre journal ne produirait que l'exemple que vous avez publié, mais la boucle s'exécuterait 20 000 fois :(
pkyeck
@pkyeck vous avez raison, peut-être que _.find est meilleur que _.filter car il se casse après la découverte de l'elemnt, voici le violon: jsfiddle.net/niki4810/9K3EV
Nikhil
2
Je pense que cette réponse devrait être marquée comme la bonne. _.findfait exactement ce qui est demandé: parcourir la liste jusqu'au retour du rappel true.
Fabien Quatravaux
A voté pour cette réponse car la réponse acceptée (Array.every) ne fonctionnera pas sur les objets, mais _.find () le fera.
mat
Et c'est ce qui est recommandé dans la documentation: il est également bon de noter qu'une chaque boucle ne peut pas être rompue - pour rompre, utilisez plutôt _.find.
shaharsol
15

Vous pouvez jeter un œil à la _.someplace de _.each. _.somearrête de parcourir la liste une fois qu'un prédicat est vrai. Les résultats peuvent être stockés dans une variable externe.

_.some([1, 2, 3], function(v) {
    if (v == 2) return true;
})

Voir http://underscorejs.org/#some

Joan
la source
6
_([1,2,3]).find(function(v){
    return v if (v==2);
})
Rockyboy_ruby
la source
3

Peut-être que vous voulez utiliser Underscore any () ou find (), qui arrêtera le traitement lorsqu'une condition est remplie.

Grantwparks
la source
3

Comme les autres réponses, c'est impossible. Voici le commentaire sur le disjoncteur dans le numéro de soulignement de soulignement n ° 21

czizzy
la source
3

Vous ne pouvez pas casser un forEachtrait de soulignement, car il émule le comportement natif EcmaScript 5.

JaredMcAteer
la source
2

Je crois que si votre tableau était en fait un objet, vous pourriez retourner en utilisant un objet vide.

_.({1,2,3,4,5}).each(function(v){  
  if(v===3) return {}; 
});
bm_i
la source
Cela ne se produit qu'avec EcmaScript v <5 car la comparaison que le soulignement fait pour vérifier si vous retournez l'objet vide dans l'alternative fournie à forEach n'est effectuée que lorsque l'objet natif n'est pas disponible.
Alfonso de la Osa
1

Mettre à jour:

Vous pouvez réellement "casser" en lançant une erreur à l'intérieur et en l'attrapant à l'extérieur: quelque chose comme ceci:

try{
  _([1,2,3]).each(function(v){
    if (v==2) throw new Error('break');
  });
}catch(e){
  if(e.message === 'break'){
    //break successful
  }
}

Cela a évidemment des implications concernant toutes les autres exceptions que votre code déclenche dans la boucle, alors utilisez-les avec prudence!

bm_i
la source
J'adore la façon dont j'obtiens autant de votes
négatifs
1
Je suis en retard à la fête, mais notez que ce gars a non seulement dit ce que vous avez suggéré, mais a proposé deux autres alternatives (et plus adaptées). Le seul offert le "hack" au cas où l'utilisateur le voudrait par tous les moyens. Au lieu de cela, vous n'avez offert que le vilain hack.
Areks
0

travaillé dans mon cas

var arr2 = _.filter(arr, function(item){
    if ( item == 3 ) return item;
});
aleXela
la source