Question Comment court-circuiter Array.forEach comme appeler break?


[1,2,3].forEach(function(el) {
    if(el === 1) break;
});

Comment puis-je faire cela en utilisant le nouveau forEach méthode en JavaScript? J'ai essayé "return", "return false" et "break". Break breaks et retour ne fait que continuer l'itération.


1008
2018-04-14 21:57


origine


Réponses:


Il n'y a pas de capacité intégrée à break dans forEach. Pour interrompre l'exécution, vous devez lancer une exception quelconque. par exemple.

var BreakException = {};

try {
  [1, 2, 3].forEach(function(el) {
    console.log(el);
    if (el === 2) throw BreakException;
  });
} catch (e) {
  if (e !== BreakException) throw e;
}

Les exceptions JavaScript ne sont pas très jolies. Une traditionnelle for boucle pourrait être plus approprié si vous avez vraiment besoin de break à l'intérieur.

Utilisation Array#some

Au lieu de cela, utilisez Array#some:

[1, 2, 3].some(function(el) {
  console.log(el);
  return el === 2;
});

Cela fonctionne parce que some résultats true dès que l'un des callbacks, exécutés dans l'ordre du tableau, retourner true, court-circuitant l'exécution du reste.

some, son inverse every (qui s'arrêtera sur un return false), et forEach sont toutes les méthodes ECMAScript cinquième édition qui devront être ajoutés à la Array.prototype sur les navigateurs où ils sont manquants.


1404
2018-04-14 22:02



Il y a maintenant une meilleure façon de le faire dans ECMAScript2015 (aka ES6) en utilisant le nouveau pour de la boucle. Par exemple, ce code n'imprime pas les éléments du tableau après le numéro 5:

let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for (let el of arr) {
  console.log(el);
  if (el === 5) {
    break;
  }
}

De la docs:

Tous les deux pour ... dans et pour ... de les déclarations itérent sur quelque chose. La principale différence entre eux est dans ce qu'ils parcourent. le pour ... dans instruction itère sur les propriétés énumérables d'un objet, dans l'ordre d'insertion d'origine. le pour ... de L'instruction itère sur les données que l'objet itérable définit pour être itérées.

Besoin de l'index dans l'itération? Vous pouvez utiliser Array.entries():

for (const [index, el] of arr.entries()) {
  if ( index === 5 ) break;
}

165
2017-08-19 16:43



Vous pouvez utiliser chaque méthode:

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

pour l'ancien support de navigateur, utilisez:

if (!Array.prototype.every)
{
  Array.prototype.every = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this &&
          !fun.call(thisp, this[i], i, this))
        return false;
    }

    return true;
  };
}

plus de détails ici.


154
2017-07-19 09:18



Citant de la MDN documentation de Array.prototype.forEach():

Il y a pas moyen d'arrêter ou de casser une forEach() boucle autre que   en lançant une exception. Si vous avez besoin d'un tel comportement, le .forEach() méthode est la mauvais outil, utilisez une boucle simple à la place. Si vous testez les éléments du tableau pour un prédicat et avez besoin d'une valeur de retour booléenne, vous pouvez utiliser every() ou some() au lieu.

Pour votre code (dans la question), comme suggéré par @bobince, utilisez Array.prototype.some() au lieu. Cela convient très bien à votre utilisation.

Array.prototype.some() exécute la fonction de rappel une fois pour chaque élément présent dans le tableau jusqu'à ce qu'il en trouve un où la fonction callback renvoie une valeur truey (une valeur qui devient true lorsqu'elle est convertie en Boolean). Si un tel élément est trouvé, some() renvoie immédiatement vrai. Autrement, some() renvoie false. le rappel est appelé uniquement pour les index du tableau auquel des valeurs ont été affectées; il n'est pas appelé pour les index qui ont été supprimés ou pour lesquels aucune valeur n'a été affectée.


45
2018-01-06 21:16



Malheureusement dans ce cas ce sera beaucoup mieux si vous n'utilisez pas forEach. Au lieu de cela, utilisez un for boucle et il fonctionnera maintenant exactement comme vous vous y attendez.

var array = [1, 2, 3];
for (var i = 0; i < array.length; i++) {
  if (array[i] === 1){
    break;
  }
}

38
2017-08-25 20:03



Pensez à utiliser jqueryde each méthode, car elle permet de retourner la fonction de rappel false à l'intérieur:

$.each(function(e, i) { 
   if (i % 2) return false;
   console.log(e)
})

Les bibliothèques Lodash fournissent également takeWhile méthode qui peut être chaînée avec map / reduce / fold etc:

var users = [
  { 'user': 'barney',  'active': false },
  { 'user': 'fred',    'active': false },
  { 'user': 'pebbles', 'active': true }
];

_.takeWhile(users, function(o) { return !o.active; });
// => objects for ['barney', 'fred']

// The `_.matches` iteratee shorthand.
_.takeWhile(users, { 'user': 'barney', 'active': false });
// => objects for ['barney']

// The `_.matchesProperty` iteratee shorthand.
_.takeWhile(users, ['active', false]);
// => objects for ['barney', 'fred']

// The `_.property` iteratee shorthand.
_.takeWhile(users, 'active');
// => []

24
2018-04-14 22:06



Si vous souhaitez utiliser La suggestion de Dean Edward et lancez l'erreur StopIteration pour sortir de la boucle sans avoir à attraper l'erreur, vous pouvez utiliser la fonction suivante (originaire d'ici):

// Use a closure to prevent the global namespace from be polluted.
(function() {
  // Define StopIteration as part of the global scope if it
  // isn't already defined.
  if(typeof StopIteration == "undefined") {
    StopIteration = new Error("StopIteration");
  }

  // The original version of Array.prototype.forEach.
  var oldForEach = Array.prototype.forEach;

  // If forEach actually exists, define forEach so you can
  // break out of it by throwing StopIteration.  Allow
  // other errors will be thrown as normal.
  if(oldForEach) {
    Array.prototype.forEach = function() {
      try {
        oldForEach.apply(this, [].slice.call(arguments, 0));
      }
      catch(e) {
        if(e !== StopIteration) {
          throw e;
        }
      }
    };
  }
})();

Le code ci-dessus vous donnera la possibilité d'exécuter du code comme celui-ci sans avoir à faire vos propres clauses try-catch:

// Show the contents until you get to "2".
[0,1,2,3,4].forEach(function(val) {
  if(val == 2)
    throw StopIteration;
  alert(val);
});

Une chose importante à retenir est que cela ne mettra à jour la fonction Array.prototype.forEach que si elle existe déjà. S'il n'existe pas déjà, il ne le modifiera pas.


14
2017-07-05 19:32



À partir de votre exemple de code, il semble que Array.prototype.find est ce que vous cherchez: Array.prototype.find () et Array.prototype.findIndex () 

[1, 2, 3].find(function(el) {
    return el === 2;
}); // returns 2

13
2018-01-15 20:20



Réponse courte: utiliser for...break pour cela ou changer votre code pour éviter la rupture de forEach. Ne pas utiliser .some() ou .every() émuler for...break. Réécrivez votre code pour éviter for...break boucle, ou utiliser for...break. Chaque fois que vous utilisez ces méthodes comme for...break Dieu alternatif tue chaton.

Longue réponse:

.some() et .every() les deux reviennent boolean valeur, .some() résultats true s'il y a un élément pour lequel la fonction passée renvoie true, chaque retour false s'il y a un élément pour lequel la fonction passée renvoie false. C'est ce que ces fonctions signifient. Utiliser des fonctions pour ce qu'ils ne veulent pas dire est bien pire que d'utiliser des tableaux pour la mise en page au lieu de CSS, car cela contrarie tout le monde qui lit votre code.

En outre, la seule façon possible d'utiliser ces méthodes for...break alternative est de faire des effets secondaires (changer certaines vars en dehors de .some() fonction de rappel), et ce n'est pas très différent de for...break.

Donc, en utilisant .some() ou .every() comme for...break boucle alternative n'est pas libre d'effets secondaires, ce n'est pas beaucoup plus propre alors for...break, c'est frustrant, donc ce n'est pas mieux.

Vous pouvez toujours réécrire votre code afin qu'il n'y ait pas besoin de for...break. Vous pouvez filtrer le tableau en utilisant .filter(), vous pouvez diviser le tableau en utilisant .slice() et ainsi de suite, alors utilisez .forEach() ou .map() pour cette partie du tableau.


8
2017-07-29 12:34



J'ai trouvé cette solution sur un autre site. Vous pouvez envelopper le forEach dans un scénario try / catch.

if(typeof StopIteration == "undefined") {
 StopIteration = new Error("StopIteration");
}

try {
  [1,2,3].forEach(function(el){
    alert(el);
    if(el === 1) throw StopIteration;
  });
} catch(error) { if(error != StopIteration) throw error; }

Plus de détails ici: http://dean.edwards.name/weblog/2006/07/enum/


3
2018-04-14 22:07



Si vous n'avez pas besoin d'accéder à votre tableau après l'itération, vous pouvez renflouer en définissant la longueur du tableau à 0. Si vous en avez encore besoin après votre itération, vous pouvez le cloner en utilisant la tranche.

[1,3,4,5,6,7,8,244,3,5,2].forEach(function (item, index, arr) {
  if (index === 3) arr.length = 0;
});

Ou avec un clone:

var x = [1,3,4,5,6,7,8,244,3,5,2];

x.slice().forEach(function (item, index, arr) {
  if (index === 3) arr.length = 0;
});

Ce qui est une solution bien meilleure que de lancer des erreurs aléatoires dans votre code.


3
2018-06-04 09:38