.every()および.some()を使用してJavaScript配列を操作する方法
序章
ES5とES6は、配列として表現されたデータのコレクションを操作するためのより良い方法を含む、JavaScriptに多くの変更をもたらしました。 この言語は配列での宣言型データ操作のサポートを大幅に改善し、多くの最新のブラウザーがそれらをサポートしていますが、一般的に使用されるのは配列機能の限られたサブセットのみです。 特に、.every()
および.some()
関数は、開発者が配列を操作する方法を改善し、パフォーマンスを向上させる可能性があります。
この記事では、主要なJavaScript配列関数が.map()
、.filter()
、および.reduce()
であることを示し、.every()
と.some()
は、より優れたソリューションよりもコンピューティング能力を節約します。
.map()
、.filter()
、および.reduce()
:著名なJavaScript配列関数
.map()
、.filter()
、および.reduce()
の使用法は非常に一般的です。 この記事では、これら3つの関数をMFRと呼びましょう。
これらの関数は、JavaScriptで配列データを操作するための他の関数よりも目立つようになっています。 たとえば、 Googleトレンドは、他の機能のサンプルよりもMFRのアクティブな検索を示しています。
Google Search は、MFRの検索クエリがはるかに多くの結果を生成することも示しています。
.filter()
の検索結果は7,400万を超えています。 これは、.every()
の結果よりも99.97% hi大きく、。some()
の結果よりも99.98% hi大きくなっています。 これは、人々がより多くの MFR を話し、書き、教えていることを意味します。これは、時間の経過とともにより多くの使用法を示唆しています。
.every()
および.some()
の利点
JavaScriptでは、一般に、配列の計算を反復(.forEach)および変換(.map、.filter、.reduce --aka MFR )操作に制限するように教えられています。 これにより、.some()
または.every()
をMFR に強制的に入力する計算、特に.filter()
の後に。[ X162X]。
MFR のほとんどすべての使用法で、コレクション全体を調べずに早期に終了する必要がある場合は、.some()
または.every()
の両方が短絡し、最後まで実行してコンピューティングリソースを浪費する可能性があるのではなく、できるだけ早くアレイの反復を終了します。
より複雑なアプリを構築し、より多くのバイトをクライアントに送信しているため、JavaScriptの解析にかなりのオーバーヘッドが発生します( TTI を参照、3回の反復を行うためにできることは何でも(短絡のため)) 30の代わりに大歓迎です。
Array.prototype.every()
every()
メソッドを使用すると、配列内のすべての要素が特定の要件を満たしているかどうかを確認できます。 指定された要件を満たさない要素を最初に検出すると、配列の評価(短絡)を停止します。
Array.prototype.some()
some()
メソッドを使用すると、配列内のいくつかの(少なくとも1つの)要素が特定の要件を満たしているかどうかを確認できます。 与えられた要件を満たしている要素を最初に見つけたときに、配列の評価(短絡)を停止します。
1つの例、2つのシナリオ
整数の束を追加するための単純なadd()
関数を作成するように求められたとします。 関数は、加算される整数がいくつでも与えられることを期待し、加算を計算した後にそれらの合計を返す必要があります。
このタスクを完了するには、次の2つの方法があります。
シナリオ1
add()関数に混合入力(整数、浮動小数点数、未定義、文字列など)が与えられた場合、整数のみを処理し、それらの加算から合計を返す必要があります。 add()関数は次のように実装できます。
const add = (...entries) => { return entries .filter(Number.isInteger) .reduce((sum, int) => { return sum + int; }, 0); };
このシナリオでは、add()
関数が指定された入力内に存在する整数から合計を計算することを想定しているため、前のコードブロックにあるような関数を実装できます。 最初に.filter()
を使用して整数のみを抽出し、次に.reduce()
を使用して合計を計算します。 ただし、.filter()
操作は、配列内に整数がない場合でも配列全体を反復処理し、計算リソースを浪費します。
次のようなより良い解決策を得ることができると主張することもできます。
const add = (...entries) => { return entries.reduce((sum, entry) => { if(Number.isInteger(entry)) { return sum + entry; } return sum; }, 0); };
add()
のこのテイクは、有効なデータを識別するために反復し、さらに有効なデータに基づいて結果を計算するために反復する最初のテイクとは異なり、反復のブロックが1つしかないため少し優れています。 ただし、配列内に整数がない場合でも、配列の最後まで実行しています。
収集された入力に少なくとも1つの整数があるかどうかを最初に判断する方法が必要です。 入力から合計を見つけようとしているので、実際には少なくとも2つの整数が必要です。 2つ以上の整数が見つかった場合は、入力を整数の合計に減らします。
.some()
を使用して、この条件が満たされていることを確認します。
const add = (...entries) => { let theSum = 0; if(hasTwoOrMoreInts(entries)){ // there are >= 2 integers, lets sum them theSum = entries.reduce((sum, entry) => { if(Number.isInteger(entry)) { return sum + entry; } return sum; }, 0); } return theSum; };
これで、整数が2つ以上あることが確実でない限り、合計の計算を妨げる条件ができました。 これは、次のように作成するhasTwoOrMoreInts()
関数を使用して実行しています。
const hasTwoOrMoreInts = (entries) => { let lastIndex = -1; let hasMinimumIntsCount = false; const hasAnInt = entries.some((entry, index) => { lastIndex = index; return Number.isInteger(entry); }); if(hasAnInt === true) { // we've got one int, is there another? const hasMoreInts = entries.slice(lastIndex + 1).some(Number.isInteger); hasMinimumIntsCount = (hasMoreInts === true) && hasAnInt; } return hasMinimumIntsCount; };
シナリオ2
add()
関数が混合入力(整数、浮動小数点数、未定義、文字列など)を受け取ることができるが、すべての入力が整数である場合にのみ、指定された入力の合計を計算する必要がある場合、一般的なアプローチMFRの卓越性の影響を受けて、次のようになります。
const add = (...entries) => { let theSum = 0; const nonInts = entries.filter(entry => !Number.isInteger(entry)); if(nonInts.length === 0) { // are there non-ints? theSum = entries.reduce((sum, int) => { return sum + int; }, 0); } return theSum; }
ここでも、entries.filter()
は、entries
配列全体を反復処理して、整数ではないすべての入力を収集することにより、無効な入力があるかどうかを確認しようとします。 無効な入力(nonInts.length === 0
)がない場合は、.reduce()
を使用して合計を計算します。 これは、すべての無効な入力の総数またはすべての入力自体が必要な場合に意味があります。 それ以外の場合は、最初の無効な入力を探し、そこから次に何をするかを決定する必要があります。
.some()
と同様に、.every()
は必要最小限の情報に基づいて動作します。 この場合、整数ではない入力が見つかると、それ以上検索せず(false
を返すことで終了します)、次の重要なことに進むことができます。
.every()
がこれをどのように行うかを見てみましょう。
const add = (...entries) => { let theSum = 0; const areAllInts = entries.every(Number.isInteger); if(areAllInts === true) { // are these indeed all ints? theSum = entries.reduce((sum, int) => { return sum + int; }, 0); } return theSum; };
entries.every()
は整数ではないものを見つけるとすぐにfalse
を返すため、entries
内の無効な要素のテストを終了して、リソースを解放することができます。モバイルユーザーにスムーズなスクロール体験を提供するために必要です。
より実用的な例
プログラマーは、add()
関数を頻繁に作成することはあまりないので、実際の例を見てみましょう。仮想のHTMLフォーム検証に.every()
と.some()
を適用します。 。
フォームから必要なすべてのデータを取得して検証した後でのみ、フォームを送信します。 同様に、オプションのデータの少なくとも1つを入力する必要があります。
const requiredFields = Array.of( isFirstNameValid(), isLastNameValid(), isEmailValid(), isAboutMeValid() ); const optionalFields = Array.of( isTwitterValueValid(), isFacebookValue(), isGoogleplusValueValue() ); const isValid = (inputStatus) => inputStatus === true; if(requiredFields.every(isValid) && optionalFields.some(isValid)) { // all required fields are valid // and at least one social media field is valid // lets proceed to submit the form now } else { // lets tell the user we are serious here // this will happen really fast since we are short-circuiting // with .some and .every above }
上記のように、Array.of()
内のすべての関数は、特定のフォームフィールドの検証を実行してから、true
またはfalse
のいずれかを返します。 Array.of()
は、指定されたパラメーター(この場合は検証関数)から配列を作成するためのファクトリであるため、optionalFields
は最終的に[true, false, false]
のようになります。 このように、requiredFields
のいずれかの値がfalseの場合、またはoptionalFields
のいずれもtrueでない場合、フォームは送信されません。 フォームの送信は、実行の最短パスで決定されます。
結論
この調査は、MFRがどれほど目立つようになったかを明らかにするのに役立ちました。 また、.every()
および.some()
がアレイを操作するためのより効率的な戦略を提供できる理由についても説明します。
JavascriptコードベースでMFRよりも。some()
または.every()
を宣伝または採用するためのいくつかの可能なシナリオを次に示します。
.filter()
の直後に.forEach()
、.map()
、.reduce()
、またはそれらを含むチェーンの組み合わせ。.filter()
の直後に、呼び出しの結果を調べる条件が続き、条件の本体に.forEach()
、.map()
、.reduce()
、または.filter()
の結果に基づいて、それらを含む連鎖的な組み合わせ。
これらおよびその他の関連するシナリオでは、できるだけ早くチェックを終了すると、リソースが最適化されます。