配列の値すべてが条件を満たしているか、配列の値のいずれか 1 つでも条件を満たしているか、といった処理はときどき遭遇します。しかし、頻繁に遭遇しないせいか、すぐに忘れてしまうのが Array.every()
と Array.some()
です。今回は私の忘備録として、これら 2 つの使き方をまとめてみました。
すべての値が条件に一致するかを評価する Array.every()
every()
メソッドは、配列の中の値すべてが指定した条件を満たしているなら true
を、そうでなければ false
を返します。次の例は、配列の中の値のすべてが 5 以上であるかどうかを評価します。
const nums = [5, 6, 7];
const res = nums.every(elm => elm >= 5); // true
every()
メソッドには、配列の値を評価するためのコールバック関数を引数に指定します。そのコールバック関数は配列の値に対して順次呼び出されます。そして、コールバック関数は引数に与えられた値が条件を満たしているかどうかを true
または false
で返します(true
または false
を返すよう作ります)。
前述のコールバック関数のコードはかなり記述が省略されているため、JavaScript 初心者にとって分かりずらいかもしれません。every()
の部分だけをもう少し丁寧にコードを書くと次のようになります。
const res = nums.every((elm) => {
return (elm >= 5);
})
every()
に限らず Array のメソッドでコールバック関数を引数に取るメソッドの解説では、前述のようなコールバック関数の省略表記をよく目にします。私が初めてそういう表記を目にしたときは、著者がベテランをアピールすべくマウントを取りたいのかなぁ、なんて思うこともありました。しかし、恐らくですが、決してマウント取りたいからではなく、if の条件式に 1 行で入れたいこと多いからなのかもしれませんね。
if (nums.every(elm => elm >= 5)) {
console.log('OK');
}
このように簡単な評価なら一行に無理なく入れられます。とはいえ、個人的に省略表記は読みづらいので、私は省略表記を使わずにコールバック関数を書くことがほとんどです。みなさんならどうされますか?
コールバック関数の第二引数:インデックス番号
コールバック関数の第二引数には、第一引数に与えられた値の配列における位置、つまり、インデックス番号が与えられます。次の例は、配列のすべての値がインデックス番号より大きいかどうかをチェックするコードです。
const nums = [2, 5, 6];
const res = nums.every((elm, idx) => {
return (elm > idx);
});
// true
このように配列の中の位置(インデックス番号)を使って値を評価しなければならない場合は、第二引数を使います。
コールバック関数の第三引数:元の配列の Array オブジェクト
コールバック関数の第三引数には、元となる配列の Array オブジェクトが与えられます。次の例は、配列の値が次の値より小さいかどうかをチェックするコードです。つまり配列の値が小さい順に並んでいるかをチェックします。
const nums = [1, 2, 3];
const res = nums.every((elm, idx, ary) => {
if (idx < ary.length - 1) {
return (elm < ary[idx + 1]);
} else {
return true;
}
});
// true
コールバック関数の第三引数は元の配列の Array オブジェクトのことですから、変数 nums
と ary
は同じ配列を指しています。であれば、コールバック関数内では nums
を使えば良く、第三引数は必要ないと思われるかもしれません。
少なくともこの例ではその通りです。第三引数が役に立つのは、コールバック関数が every()
の中に無名関数としてインラインに直接的に記述されるのではなく、外部で定義されている場合です。
main();
function main() {
const nums = [1, 2, 3];
const res = nums.every(compare);
console.log(res); // true
}
function compare(elm, idx, ary) {
if (idx < ary.length - 1) {
return (elm < ary[idx + 1]);
} else {
return true;
}
}
この例では、every()
の引数としてコールバック関数 compare()
が引き渡されています。コールバック関数 compare()
の中からは元となる配列 nums
は読み取れません。そこで第三引数が役に立ちます。
すべての値を評価するわけではない
every()
は、配列のすべての値を漏れなくチェックするわけではありません。条件を満たさない値が見つかった時点で配列の反復処理から抜けて結果を返します。
次の例では、配列の値のすべてが 5 以上かどうかを評価します。そして、コールバック関数では評価する値をコンソール出力しています。
const nums = [6, 8, 4, 7, 10];
const res = nums.every((elm) => {
console.log(elm);
return (elm >= 5);
});
// false
コンソールには 6, 8, 4 が出力され、そこで反復処理から抜けます。つまり、コールバック関数の呼び出しは 4 に遭遇した時点で終了したということです。なぜなら、この時点で every()
が返す値(ここでは false
)が確定するからです。
いずれか 1 つでも条件を満たしているかを評価する Array.some()
some()
は、配列の値のうち一つでも条件を満たせば true
を、すべての値が条件を満たさなければ false
を返します。使い方は前述の every()
とまったく同じです。
次の例は、配列の値のうち一つでも 5 より小さい値が存在するかを評価しています。また、コールバック関数では評価する値をコンソール出力しています。
const nums = [6, 8, 4, 7, 10];
const res = nums.some((elm) => {
console.log(elm);
return (elm < 5);
});
// true
コンソールには 6, 8, 4 が出力され、そこで反復処理から抜けます。つまり、コールバック関数の呼び出しは 4 に遭遇した時点で終了したということです。なぜなら、この時点で some()
が返す値(ここでは true
)が確定するからです。
まとめ
Array.every()
と Array.some()
は今回のサンプル程度のことであれば簡単に自作できます。私も every()
と some()
の存在を忘れて、ついつい自作してしまうことがあります。いくら簡単なコードで自作できるとはいえ、バグの可能性は排除できませんので、every()
と some()
を積極的に使っていきたいですね。
今回は以上で終わりです。最後まで読んでくださりありがとうございました。それでは次回の記事までごきげんよう。