Short circuit Array.forEach like calling break

Hoe kan ik dit doen met de nieuwe forEach-methode in JavaScript? Ik heb return;, return false;en breakgeprobeerd. breakcrasht en returndoet niets anders dan doorgaan met itereren.


Antwoord 1, autoriteit 100%

Er is geen ingebouwde mogelijkheid om breakte gebruiken in forEach. Om de uitvoering te onderbreken, zou je een of andere uitzondering moeten maken. bijv.

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

Antwoord 2, autoriteit 26%

Er is nu een nog betere manier om dit te doen in ECMAScript2015 (ook bekend als ES6) met behulp van de nieuwe voor van lus. Deze code drukt bijvoorbeeld de array-elementen niet af na het cijfer 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;
  }
}

Antwoord 3, autoriteit 9%

U kunt elkegebruiken methode:

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

ES6

[1,2,3].every( el => el !== 1 )

gebruik voor ondersteuning van oude browsers:

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;
  };
}

meer details hier.


Antwoord 4, autoriteit 4%

Helaas is het in dit geval veel beter als je forEachniet gebruikt.
Gebruik in plaats daarvan een gewone for-lus en het zal nu precies werken zoals je zou verwachten.

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

Antwoord 5

Vanuit uw codevoorbeeld lijkt het erop dat Array.prototype.findis wat u zoekt: Array.prototype.find()en Array.prototype.findIndex()

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

Antwoord 6

Overweeg om jquery‘s eachmethode te gebruiken, aangezien het de mogelijkheid biedt om een ​​valse inside callback-functie te retourneren:

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

Lodash-bibliotheken bieden ook een takeWhile-methode die kan worden gekoppeld aan map/ verkleinen/vouwen 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');
// => []

Antwoord 7

Als je Dean Edwards suggestiewilt gebruiken en de StopIteration-fout om uit de lus te breken zonder de fout te hoeven opvangen, kunt u de volgende functie gebruiken (oorspronkelijk van hier):

// 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;
        }
      }
    };
  }
})();

De bovenstaande code geeft je de mogelijkheid om code zoals de volgende uit te voeren zonder dat je je eigen try-catch-clausules hoeft te doen:

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

Een belangrijk ding om te onthouden is dat hiermee de functie Array.prototype.forEach alleen wordt bijgewerkt als deze al bestaat. Als het nog niet bestaat, zal het het niet wijzigen.


Antwoord 8

Kort antwoord: gebruik hiervoor for...breakof verander je code om te voorkomen dat forEachwordt verbroken. Gebruik .some()of .every()niet om for...breakte emuleren. Herschrijf je code om de for...break-lus te vermijden, of gebruik for...break. Elke keer dat je deze methodes gebruikt als for...breakalternatief, vermoordt God kitten.

Lang antwoord:

.some()en .every()geven beide een booleanwaarde, .some()retourneren trueals er een element is waarvoor de doorgegeven functie trueretourneert, elke retourneert falseals er een element is waarvoor de doorgegeven functie false. Dit is wat die functies betekenen. Functies gebruiken voor wat ze niet betekenen is veel erger dan tabellen gebruiken voor lay-out in plaats van CSS, omdat het iedereen die je code leest frustreert.

De enige manier om deze methoden te gebruiken als alternatief voor for...breakis om neveneffecten te maken (verander enkele vars buiten .some()callback-functie), en dit verschilt niet veel van for...break.

Dus, het gebruik van .some()of .every()als for...breakloop-alternatief is niet vrij van side effecten, dit is niet veel schoner dan for...break, dit is frustrerend, dus dit is niet beter.

Je kunt je code altijd herschrijven, zodat er geen noodzaak is in for...break. U kunt de array filteren met .filter(), u kunt de array splitsen met .slice()enzovoort, en vervolgens .forEach()gebruiken of .map()voor dat deel van de array.


Antwoord 9

Dit is iets wat ik bedacht heb om het probleem op te lossen… Ik ben er vrij zeker van dat het het probleem oplost dat de oorspronkelijke vraagsteller had:

Array.prototype.each = function(callback){
    if(!callback) return false;
    for(var i=0; i<this.length; i++){
        if(callback(this[i], i) == false) break;
    }
};

En dan noem je het met:

var myarray = [1,2,3];
myarray.each(function(item, index){
    // do something with the item
    // if(item != somecondition) return false; 
});

Het retourneren van false binnen de callback-functie veroorzaakt een onderbreking. Laat het me weten als dat niet echt werkt.


Antwoord 10

Zoals eerder vermeld, kunt u .forEach()niet breken.

Hier is een iets modernere manier om een ​​foreach te doen met ES6 Iterators. Hiermee krijgt u tijdens iteratie directe toegang tot index/value.

const array = ['one', 'two', 'three'];
for (const [index, val] of array.entries()) {
  console.log('item:', { index, val });
  if (index === 1) {
    console.log('break!');
    break;
  }
}

Uitvoer:

item: { index: 0, val: 'one' }
item: { index: 1, val: 'two' }
break!

Links


Antwoord 11

Een ander concept dat ik bedacht heb:

function forEach(array, cb) {
  var shouldBreak;
  function _break() { shouldBreak = true; }
  for (var i = 0, bound = array.length; i < bound; ++i) {
    if (shouldBreak) { break; }
    cb(array[i], i, array, _break);
  }
}
// Usage
forEach(['a','b','c','d','e','f'], function (char, i, array, _break) {
  console.log(i, char);
  if (i === 2) { _break(); }
});

Antwoord 12

Als je geen toegang nodig hebt tot je array na iteratie, kun je het redden door de lengte van de array in te stellen op 0. Als je het na je iteratie nog steeds nodig hebt, kun je het klonen met slice..

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

Of met een kloon:

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;
});

Wat een veel betere oplossing is dan willekeurige fouten in je code te gooien.


Antwoord 13

Deze oplossing gevonden op een andere site. Je kunt de forEach in een try/catch-scenario verpakken.

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; }

Meer details hier: http://dean.edwards.name/weblog/ 2006/07/enum/


Antwoord 14

Dit is een for-lus, maar handhaaft de objectreferentie in de lus, net als een forEach(), maar u kunt uitbreken.

var arr = [1,2,3];
for (var i = 0, el; el = arr[i]; i++) {
    if(el === 1) break;
}

Antwoord 15

Nog een andere benadering

       var wageType = types.filter(function(element){
            if(e.params.data.text == element.name){ 
                return element;
            }
        });
        console.dir(wageType);

Antwoord 16

Ik gebruik nullhackvoor dat doel, het probeert toegang te krijgen tot de eigenschap null, wat een fout is:

try {
  [1,2,3,4,5]
  .forEach(
    function ( val, idx, arr ) {
      if ( val == 3 ) null.NULLBREAK;
    }
  );
} catch (e) {
  // e <=> TypeError: null has no properties
}
//

Antwoord 17

Als u uw forEach-syntaxis wilt behouden, is dit een manier om deze efficiënt te houden (hoewel niet zo goed als een normale for-lus). Controleer onmiddellijk op een variabele die weet of je uit de lus wilt breken.

Dit voorbeeld gebruikt een anonieme functie voor het maken van een functiebereikrond de forEachdie u nodig hebt om de klaar-informatie op te slaan.

(function(){
    var element = document.getElementById('printed-result');
    var done = false;
    [1,2,3,4].forEach(function(item){
        if(done){ return; }
        var text = document.createTextNode(item);
        element.appendChild(text);
        if (item === 2){
          done = true;
          return;
        }
    });
})();
<div id="printed-result"></div>

Antwoord 18

Gebruik de functie array.prototype.every, die u het hulpprogramma biedt om de looping te doorbreken. Zie hier een voorbeeld Javascript-documentatie over het Mozilla-ontwikkelaarsnetwerk


Antwoord 19

Eens met @bobince, upvoted.

Ook ter info:

Prototype.js heeft iets voor dit doel:

<script type="text/javascript">
  $$('a').each(function(el, idx) {
    if ( /* break condition */ ) throw $break;
    // do something
  });
</script>

$breakwordt intern opgevangen en afgehandeld door Prototype.js, waarbij de “elke” cyclus wordt doorbroken, maar geen externe fouten worden gegenereerd.

Zie Prototype.JS APIvoor details.

jQuery heeft ook een manier, retourneer gewoon false in de handler om de lus vroegtijdig te onderbreken:

<script type="text/javascript">
  jQuery('a').each( function(idx) {
    if ( /* break condition */ ) return false;
    // do something
  });
</script>

Zie jQuery APIvoor details.


Antwoord 20

Dit is niet de meest efficiënte, aangezien je nog steeds alle elementen doorloopt, maar ik dacht dat het misschien de moeite waard was om het heel eenvoudige te overwegen:

let keepGoing = true;
things.forEach( (thing) => {
  if (noMore) keepGoing = false;
  if (keepGoing) {
     // do things with thing
  }
});

Antwoord 21

je kunt de onderstaande code volgen die voor mij werkt:

var     loopStop = false;
YOUR_ARRAY.forEach(function loop(){
    if(loopStop){ return; }
    if(condition){ loopStop = true; }
});

Antwoord 22

Ik weet dat het niet de juiste manier is. Het is niet de lus doorbreken.
Het is een Jugad

let result = true;
[1, 2, 3].forEach(function(el) {
    if(result){
      console.log(el);
      if (el === 2){
        result = false;
      }
    }
});

Antwoord 23

Als u moet breken op basis van de waarde van elementen die zich al in uw array bevinden, zoals in uw geval (dwz als de break-voorwaarde niet afhankelijk is van de runtime-variabele die kan veranderen nadat de array zijn elementwaarden heeft toegewezen gekregen), kunt u gebruik ook een combinatie van slice()en indexOf()als volgt.

Als je moet pauzeren wanneer forEach ‘Apple’ bereikt, kun je

var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
var fruitsToLoop = fruits.slice(0, fruits.indexOf("Apple"));
// fruitsToLoop = Banana,Orange,Lemon
fruitsToLoop.forEach(function(el) {
    // no need to break
});

Zoals vermeld in W3Schools.comretourneert de slice()-methode de geselecteerde elementen in een array, als een nieuw array-object. De originele array wordt niet gewijzigd.

Zie het in JSFiddle

Ik hoop dat het iemand helpt.


Antwoord 24

probeer met “find” :

var myCategories = [
 {category: "start", name: "Start", color: "#AC193D"},
 {category: "action", name: "Action", color: "#8C0095"},
 {category: "exit", name: "Exit", color: "#008A00"}
];
function findCategory(category) {
  return myCategories.find(function(element) {
    return element.category === category;
  });
}
console.log(findCategory("start"));
// output: { category: "start", name: "Start", color: "#AC193D" }

Antwoord 25

Waarom probeer je de functie niet in een belofte te stoppen?

De enige reden waarom ik het ter sprake breng, is dat ik een functie in een API gebruik die op dezelfde manier werkt als forEach. Ik wil niet dat het blijft herhalen zodra het een waarde heeft gevonden, en ik moet iets teruggeven, dus ik ga gewoon een belofte oplossen en het op die manier doen.

traverseTree(doc): Promise<any> {
  return new Promise<any>((resolve, reject) => {
    this.gridOptions.api.forEachNode((node, index) => {
    //the above function is the one I want to short circuit.
      if(node.data.id === doc.id) {
        return resolve(node);
      }
    });
  });
}

Dan hoef je alleen maar iets met het resultaat te doen, zoals

this.traverseTree(doc).then((result) => {
   this.doSomething(result);
});

Mijn bovenstaande voorbeeld is in typoscript, negeer gewoon de typen. De logica zou je hopelijk moeten helpen om uit je lus te “breken”.


Antwoord 26

wat dacht je van een eenvoudige array-splitsing door de arraylengte te verkleinen ???

Het zal een knoeiboel makenmet je huidige array, dus je zult een kopie moeten maken met behulp van de destructurerende functies …

bijvoorbeeld:

const arr = [1, 2, 3, 4, 5];
// Don't forget to work with a copy of the original array otherwise you will loose your data contained inside
[...arr].forEach(function(value, index, copiedArrInstance) {
  if (index === 2) copiedArrInstance.length = 0;
  console.log(value);
});
// you will still have your array intact
console.log(arr);

Antwoord 27

Je kunt een variant van forEachmaken die het mogelijk maakt om break, continue, returnen zelfs async/await: (voorbeeld geschreven in TypeScript)

export type LoopControlOp = "break" | "continue" | ["return", any];
export type LoopFunc<T> = (value: T, index: number, array: T[])=>LoopControlOp;
Array.prototype.ForEach = function ForEach<T>(this: T[], func: LoopFunc<T>) {
    for (let i = 0; i < this.length; i++) {
        const controlOp = func(this[i], i, this);
        if (controlOp == "break") break;
        if (controlOp == "continue") continue;
        if (controlOp instanceof Array) return controlOp[1];
    }
};
// this variant lets you use async/await in the loop-func, with the loop "awaiting" for each entry
Array.prototype.ForEachAsync = async function ForEachAsync<T>(this: T[], func: LoopFunc<T>) {
    for (let i = 0; i < this.length; i++) {
        const controlOp = await func(this[i], i, this);
        if (controlOp == "break") break;
        if (controlOp == "continue") continue;
        if (controlOp instanceof Array) return controlOp[1];
    }
};

Gebruik:

function GetCoffee() {
    const cancelReason = peopleOnStreet.ForEach((person, index)=> {
        if (index == 0) return "continue";
        if (person.type == "friend") return "break";
        if (person.type == "boss") return ["return", "nevermind"];
    });
    if (cancelReason) console.log("Coffee canceled because: " + cancelReason);
}

Antwoord 28

const Book = {"Titles":[                          
    {"Book3" : "BULLETIN 3"},
    {"Book1" : "BULLETIN 1"},
    {"Book2" : "BULLETIN 2"}    
]}
const findbystr = function(str) { 
    Book.Titles.forEach(function(data) { 
        if (typeof data[str] != 'undefined') {
            return data[str];
        } 
    }, str) 
}
book = findbystr('Book1');
console.log(book);

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Other episodes