JavaScript で配列の中から最大値・最小値を求める : 異常値を省きつつ…

JavaScript で配列の中から最大値・最小値を求めるには、Math.max.apply() もしくは Math.min.apply() という構文が使える。

Math.max.apply(null, [0, 3, 10]);
Math.min.apply(null, [0, 3, 10]);

しかしこれは、言語仕様上、nullundefinedNaN が混じると正しく判定できなくなってしまう。

そこで、配列の中から数値として正しくない値を引っこ抜いて検証することにする。

今回用意した配列はこんなもの。

const array = [-1, -2.2, -Infinity, 0, Infinity, NaN, 1, null, 0xFFFF, 0b1101, 0o3162, undefined, 864e5, 9.99];

nullundefinedNaN が混じっているので、コレを除去したいと思う。なお、Infinity も入れてあるが、コレもあとで除外できる。

方法1 : Number 関数を利用して NaN を弾く

Number() 関数の引数に値を入れると、数値型に変換できるものは変換し、変換できないものは NaN にしてくれる。この結果を isNaN() 関数で判定すれば良い。

一つ注意しないといけないのは、Number(null)0 に変換されることだ。コレを回避するため、!== null な比較は必要。

Array.prototype.filter() を利用し、配列から条件に合う要素のみ抜き出してみた。

const nums = array.filter((item) => {
  return item !== null && !isNaN(Number(item));
});

方法2 : toString() を確認する

次は、Object.prototype.toStringcall() で借りて、各要素が [object Number] を返すかでチェックする。null[object Null]undefined[object Undefined] を返すのだが、NaN だけは [object Number] を返してくるので、最後に !isNaN() でチェックしている。

const nums = array.filter((item) => {
  return Object.prototype.toString.call(item) === '[object Number]' && !isNaN(item);
});

ちなみに、今回の配列の中で isNaN()true になるのは、NaNundefined のみ。nullfalse になる。

Infinity を除外するには isFinite()

方法1・2のどちらでも目的の結果は得られる。速度については気にしていない。

ココからさらに Infinity-Infinity を除外するには、isFinite() 関数を使う。

この関数は、Infinity-Infinity の他に NaNundefinedfalse を返すので、null だけ別に除外して以下のようにチェックすれば良い。

const nums = array.filter((item) => {
  return item !== null && isFinite(item));
});

あとはこうして filter() した要素を Math.max.apply()Math.min.apply() に突っ込めば OK。