Breakを呼び出すようなArray.forEachの短絡


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

forEachJavaScriptの新しいメソッドを使用してこれを行うにはどうすればよいですか?私は試しましたreturn;return false;そしてbreakbreakクラッシュしreturnて何もせず、繰り返しを続ける。


6
それはしばらく注目する価値があるreturn確かに反復を続けるん、それはブロックでそれの後に来る任意のコードをスキップします。次のコードを例にとり[1,2,3].forEach(function(el) { if(el === 2) { console.log(`Match on 2!`); return; } console.log(el); });ます。.2 console.log(el);が一致するとスキップされます。
シェーン

5
TL; DR:ほとんどの時間を節約できます。最近、JSをたくさん使っています。おそらくあなたが探している答え(28問のうち...)は次の答えです:stackoverflow.com/a/32101207/1599699
Andrew

回答:


2142

何のビルトインする機能はありませんbreakの中でforEach。実行を中断するには、何らかの例外をスローする必要があります。例えば。

var BreakException = {};

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

JavaScriptの例外はひどくきれいではありません。forあなたが本当にbreakそれを中に入れる必要があるなら、伝統的なループはより適切かもしれません。

使用する Array#some

代わりに、次を使用しますArray#some

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

これが機能するのは、配列順に実行されるコールバックのいずれかがsome戻るtrueとすぐに戻りtrue、残りの実行が短絡されるためです。

some、その逆every(これはで停止しますreturn false)でありforEach、ECMAScript Fifth EditionのすべてのメソッドArray.prototypeです。これらのメソッドは、存在しないブラウザーでに追加する必要があります。


111
これは、通常のforループを使用するよりも読みやすく、パフォーマンスも高くありません。答えは「この場合はforEachを使用しないでください」-1
BT

37
ここでは「一部」で問題ないと思いますが、早期終了の最適化を使用してみませんか
chrismarx

28
心から感謝します、someそしてevery、これは答えのTOPにあるはずです。なぜそれが読みにくいと思うのか理解できません。それはただ素晴らしいです!
Karl Adler

9
の使用Array#someは本当にいいです。まず、ie9やfirefox 1.5を含むほとんどのブラウザーとの互換性も非常によく機能します。私の使用例は、数値が下限と上限のペアの間にあるrange [a、b]の配列でインデックスを見つけることです。見つかった場合、テストしてtrueを返します。for..of次の最良のソリューションですが、新しいブラウザのみです。
Sojimaxi

96
例外処理を制御フローとして使用しないでください。限目。
率直

479

ECMAScript2015(別名ES6)でこれを行うには、新しいforループを使用してさらに優れた方法があります。たとえば、次のコードは、数値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;
  }
}

ドキュメントから:

for ... infor ... ofの両方ステートメントで何かが繰り返されます。それらの主な違いは、何を繰り返すかです。以下のために...内のステートメントは、原稿挿入のためには、オブジェクトの列挙可能なプロパティを反復処理します。以下のために...のステートメントは、反復可能なオブジェクトの定義を繰り返し処理するというデータを反復処理します。

反復でインデックスが必要ですか?使用できますArray.entries()

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

4
@superhero for ... ofループで要素のインデックスを取得できますentries。使用する必要があります。for(const [index、element] of someArray.entries()){// ...}
blackxored

配列で...で使用しないことをお勧めしませんか?
schehata

4
@emostafaあなたは約正しいのループの配列には推奨されていないが、これは実際に使用するアプローチであるループ。
canac

これは「for for」であり、これは本当にクリーンなソリューションですが、これはES6の機能でもあるため、環境がES6用にセットアップされている場合にのみ機能することに注意してください。
チャド

私はこのソリューションを頻繁に使用しており、オブジェクトにも使用しています。オブジェクトObject.entries(myObject)を使用するfor..inと、配列に使用するのとまったく同じように実行できます。JS配列は基本的には内部の
Andrew

204

すべての方法を使用できます。

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

ES6

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

古いブラウザサポートを使用する場合:

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

詳細はこちら


10
ES6でのすてきなクリーン[1,2,3].every( el => el !== 1 )
メターム

1
@Valdemar、しかし呼び出しが順番に行わevery れることを保証しますか?
パセリエ2017

4
@Pacerierは、あなたは、インデックスがあることES6仕様でアルゴリズムを見ることができますk:0から始まり、1ずつインクリメントされるhttp://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.every
XP1 2017年

@ XP1、すべての実装者はそのようにする必要がありますか?
Pacerier 2017

1
@Pacerier、はい、最も人気のある実装は適切に動作します。組み込み実装が心配な場合、通常はOperaまたはWebkitです。メソッドeveryは、callbackfnがfalseを返す要素が見つかるまで、配列に存在する各要素について、callbackfn を昇順で 1回呼び出します。また、レッツkは1で0と8.e増加Kなるステップ7を見て
Valdemar_Rudolfovich

78

MDNドキュメントArray.prototype.forEach()からの引用:

例外をスローする以外にループを停止または解除する方法はありませんforEach()。このような動作が必要な場合、.forEach()メソッドは間違ったツールです。代わりに単純なループを使用してください。述語の配列要素をテストしていて、ブール戻り値が必要な場合は、every()またはを使用できますsome()

@bobinceによって提案されているように、(質問の)コードにはArray.prototype.some()代わりに使用してください。ユースケースに非常に適しています。

Array.prototype.some()コールバックが真の値(に変換されたときに真になる値)を返す場所が見つかるまで、配列内の各要素に対してコールバック関数を1回実行しますBoolean。そのような要素が見つかると、some()すぐにtrueを返します。それ以外の場合は、some()falseを返します。コールバックは、値が割り当てられている配列のインデックスに対してのみ呼び出されます。削除されたインデックス、または値が割り当てられていないインデックスに対しては呼び出されません。


1
これが正解です。'some'は、foreach / breakが行うことを正確に実行します。反復n = trueになるまでループします。
アントニーブース

74

残念ながら、この場合、を使用しない方がはるかに優れていますforEach。代わりに、通常のforループを使用すると、期待どおりに動作します。

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

27
この正解のパフォーマンスが高く、コードが少なく、可読性が優れているのに比べて、最高投票数が可能な最悪の実装であることに感銘を受けます。例外を投げます...本当に?従来のforループは十分に十分ではありませんか?
gdbj 2017年

2
@gdbj私はあなたの発言に同意し、この方法を使用しましたが、本当にショックなのは、これらのハックなしではforEachを終了する方法がないということです。
ScottN

28

jqueryeachメソッドを使用することを検討してください。コールバック関数内でfalseを返すことができるためです。

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

LodashライブラリはtakeWhile、map / reduce / foldなどと連鎖できるメソッドも提供します:

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');
// => []

1
jQueryを使用する正当な理由。ネイティブJavaScriptのforEachはまだ不足しています。
Alex Grande

3
@AlexGrande jQueryのforEachとJavaScriptのforEachには互換性がありません。
ビョルン

10
JavaScriptは、jQueryがオプションではない多くの場所で使用されています。
JBRウィルキンソン2016年


18

ディーン・エドワードの提案を使用し、StopIterationエラーをスローして、エラーをキャッチせずにループから抜け出す場合は、次の関数を使用できます(最初はここから)。

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

上記のコードを使用すると、独自の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);
});

覚えておくべき重要なことの1つは、Array.prototype.forEach関数が既に存在する場合にのみこれを更新することです。まだ存在しない場合は、変更されません。


11

短い答え:for...breakこれを使用するか、コードを変更して、の破損を回避しますforEach.some()または.every()エミュレートするために使用しないでくださいfor...breakfor...breakループを回避するようにコードを書き直すか、を使用してくださいfor...breakfor...break代わりにこれらの方法を使用するたびに、神は子猫を殺します。

長い答え:

.some()そして.every()両方の戻りboolean値は、.some()リターンtrueのための任意の要素は、関数が戻るが渡された場合true、すべてのリターンfalseのための任意の要素は、関数が戻るが渡された場合をfalse。これがその機能の意味です。意味のないものに関数を使用することは、CSSの代わりにレイアウトにテーブルを使用するよりもはるかに悪いことです。なぜなら、コードを読むすべての人を苛立たせるからです。

また、これらのメソッドをfor...break代替として使用する唯一の可能な方法は、副作用(.some()コールバック関数の外部で一部の変数を変更する)を行うことであり、これはと大差ありませんfor...break

したがって、ループの代替として、.some()または.every()as を使用してもfor...break副作用がありません。これはそれほどクリーンでfor...breakはありません。イライラするので、これは良くありません。

コードはいつでも書き換えることができるので、での必要はありませんfor...break。を使用して配列をフィルタリングしたり.filter()、配列を使用.slice()して配列を分割し.forEach()たり.map()、配列のその部分にor を使用したりできます。


.filterを使用することは、実際には、多くの場合の破壊の適切な解決策です。
TKoL

パフォーマンスはどうですか?頻繁に使用した場合、フィルターはパフォーマンスに影響しませんか?
tfrascaroli 2016年

はい、フィルター配列のプロトタイプは重くなる可能性があります。私はそれを愛していますが、使い過ぎるとパフォーマンスに影響する可能性があります。
チャド

@tfrascaroliは、for...breakパフォーマンスが必要な場合はループを使用します。forループがより最もパフォーマンスの反復ツールである.forEach().any().map().filter()など
マックス

6

これは私が問題を解決するために思いついたものです...私はそれが元の質問者が持っていた問題を修正すると確信しています:

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

そして、あなたはそれを使ってそれを呼び出すでしょう:

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

コールバック関数内でfalseを返すと、ブレークが発生します。それが実際に機能しない場合はお知らせください。


1
=== false== falseループを続行するために明示的にtrue(または真の値)を返す必要がないので、いくつかの制御パスが値を返さず、ループが予期せず中断しないようにするよりも良いかもしれません。
ジェイク

6

私が思いついた別のコンセプト:

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


構文は[NSArray enumerateObjectsUsingBlock]に似ています。ありがとうございます。
Chrstph SLN、

@Drenai署名はネイティブに類似していArray.prototype.forEach()ます。 forそして、breakこの質問が尋ねられるずっと前に存在しました。OPは、より機能的なを使用してその動作を探していましたforEach
c24w

@Drenaiはコメントを削除しました(ただし、反対票は残しました)。このソリューションの署名は覚えるのが難しく、for...inおよびで問題を解決できる場合は不要であると述べていますbreak
c24w


5

このソリューションは別のサイトで見つかりました。forEachを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; }

詳細はこちら:http : //dean.edwards.name/weblog/2006/07/enum/


2
制御フローステートメントとして例外を使用しないでください。予期しない結果を処理するために使用します。
最大

4

反復後に配列にアクセスする必要がない場合は、配列の長さを0に設定することで救済できます。反復後にも必要な場合は、スライスを使用して複製できます。

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

またはクローンで:

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

これは、コードでランダムなエラーをスローするよりもはるかに優れたソリューションです。


よくやった:)しかし、割り当て後にいくつかのアクションがある場合array.length0それらは現在の反復で適用されるので、おそらくそのreturnような割り当ての後に使用する方が良い場合があります
zhibirc

4

これはforループですが、forEach()と同じようにループ内のオブジェクト参照を維持しますが、ブレークアウトできます。

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

4

前に述べたように、あなたは壊れることはできません.forEach()

ES6イテレーターでforeachを行うもう少しモダンな方法を次に示します。反復時に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;
  }
}

出力:

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

リンク集


3

さらに別のアプローチ

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

2

私はその目的のためにnullhackを使用しますnull、それはのプロパティにアクセスしようとしますが、これはエラーです:

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

1
なぜthrow BREAKですか?
Bergi、2015

1

維持したい場合 forEach構文、これは効率を維持する方法です(ただし、通常のforループほど良くはありません)。ループを抜けたいかどうかがわかる変数をすぐに確認してください。

この例では、無名関数を使用して、完了した情報を格納する必要のある関数スコープを作成しますforEach

(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>

私の2セント。


1

私はそれが正しい方法ではないことを知っています。ループを壊すものではありません。ジュガドです

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



0

@bobinceに同意する(賛成)。

また、参考までに:

Prototype.jsには、この目的のための何かがあります。

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

$break Prototype.jsが内部的にキャッチして処理するため、「各」サイクルは中断されますが、外部エラーは発生しません。

詳細については、Prototype.JS APIを参照してください。

jQueryにも方法があります。ループを早期に中断するには、ハンドラーでfalseを返すだけです。

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

  });
</script>

詳細については、jQuery APIを参照してください。


0

すべての要素を循環させるので、これは最も効率的ではありませんが、非常に単純なものを検討する価値があると思いました。

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

continueはキーワードです。コードは構文エラーです。
Bergi

3
とにかくES6を使用している場合は、for ofループに切り替えて、break;通常どおりループから切り替える必要があります。
Bergi

修正済み、true-ただし、簡潔にするために主にes6を使用
martyman

0

あなたは私のために働く以下のコードに従うことができます:

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

なぜ-1?それは、例外をキャッチすることよりも醜くない、それは私見より大きなハックです。
バイロンウィットロック2017年

0

使いたい for in

var words = ['a', 'b', 'c'];
var text = '';
for (x in words) {
    if (words[x] == 'b') continue;
    text += words[x];
}
console.log(text);

for inのようforEachに動作し、内部でexit関数にreturnを追加できます。パフォーマンスも向上しました。


0

あなたの場合のようにすでに配列にある要素の値に基づいて分割する必要がある場合(つまり、分割条件が、配列に要素値が割り当てられた後に変更される可能性があるランタイム変数に依存しない場合)、組み合わせを使用することもできます。以下のように、slice()およびindexOf()を使用します。

forEachが「Apple」に達したときに中断する必要がある場合は、

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

W3Schools.comで述べられているように、slice()メソッドは、配列内の選択された要素を新しい配列オブジェクトとして返します。元の配列は変更されません。

JSFiddle見る

それが誰かを助けることを願っています。


0

あなたはのバリアント作成することができますforEachすることができますことをbreakcontinuereturn、さらにはasync/ await(活字体で書かれた例):

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

使用法:

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

-1

「見つける」で試してください:

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

-1

はい、forEachループを続行して終了できます。

続行するには、リターンを使用できます。ループは続行しますが、現在の関数は終了します。

ループを終了するには、3番目のパラメーターを長さ0に設定し、空の配列に設定します。ループは継続されず、現在の関数は継続するため、通常のforループの終了のように、「return」を使用して終了できます...

この:

[1,2,3,4,5,6,7,8,9,10].forEach((a,b,c) => {
    console.log(a);
    if(b == 2){return;}
    if(b == 4){c.length = 0;return;}
    console.log("next...",b);
});

これを印刷します:

1
next... 0
2
next... 1
3
4
next... 3
5

-2

以前は、私のコードは以下です

 this.state.itemsDataSource.forEach((item: any) => {
                if (!item.isByPass && (item.invoiceDate == null || item.invoiceNumber == 0)) {

                    return false;
                }
            });

以下に変更しましたので修正しました。

 for (var i = 0; i < this.state.itemsDataSource.length; i++) {
                var item = this.state.itemsDataSource[i];
                if (!item.isByPass && (item.invoiceDate == null || item.invoiceNumber == 0)) {

                    return false;
                }
            }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.