配列の中の最大値を取得する(NaN, null対応)

# 普通に扱う場合、もはやイディオムのコレ

```js:
Math.max.apply(null, [-100, 0, 3.14, 0xFF, 1000]); // => 1000
// ※ Mathの関数のコンテキスト(applyやcallの第一引数)はなんでもいいらしい
```

## 注意点は`NaN`や`null` `nudefined`があるとき

### NaN

`NaN`は何と比較しても`NaN`しか返らない

```js:
Math.max(100, NaN, -100); // => NaN
Math.max(NaN, Infinity); // => NaN (Infinityと比較しても同じ)
Math.min(100, NaN, -100); // => NaN (Math.minの比較でも同じ)
```

### undefinedとnull

`null`と`undefined`は暗黙の型変換が行われる

```js:
// それぞれの数値型への暗黙の型変換
+null; // => 0
+undefined; // => NaN
```

従って

```js:
// undefined
Math.max(1, undefined, null); // => NaN (undefinedのNaNが返る)

// null
Math.max(1, 3, 5, 7, null, 11, 13); // => 13 (おや?大丈夫じゃね?ところが↓)
Math.max(-2, -4, -8, -16, null, -64); // => 0 (nullが0に変換されるので最大は0)
```

# nullなどを含む配列から最大値を得るにはどうするか

## 1. 余計な要素を取り除く方法

### 1.1. 地道にループして余計な要素のない配列をつくってから比較

```js:
var array = [/* これにデータが詰まってるとする */];

var newArray = [];
for (var i = 0, l = array.length; i < l; i++) {
	var n = array[i];
	if (n != null && !isNaN(n)) {
		newArray.push(n);
	}
}

var max = Math.max.apply(null, newArray);
```
何かすごく余計なことをしている気がする…。

### 1.2. 余計な要素は無視して比較しながら求める

```js:
var array = [/* これにデータが詰まってるとする */];
var max;

for (var i = 0, l = array.length; i < l; i++) {
	var n = array[i];
	if (n != null && !isNaN(n)) {
		if (max) {
			max = Math.max(max, n);
		} else {
			max = n;
		}
	}
}
```
一番素直なやり方…。

### 1.3. 余計な要素を配列から取り除いて比較 ※糞コード注意

```js:
var array = [/* これにデータが詰まってるとする */];

var clone = array.concat();
var i;
while ((i = clone.indexOf(NaN)) !== -1) clone.splice(i, 1); // 糞糞糞糞糞糞
while ((i = clone.indexOf(undefined)) !== -1) clone.splice(i, 1);
while ((i = clone.indexOf(null)) !== -1) clone.splice(i, 1);

var max = Math.max.apply(null, clone);
```
トリッキーなことをしてみようと思ったけど、これは激遅の上に **取得** できない糞コード。

どこが臭うかというと

- `Array.prototype.indexOf`がIE9以上でしか使えない
- `NaN` `undefined` `null`があればあるほどループの回数が増える。線形検索を各要素で行うので、最悪[要素数x3]のループが起こる。
- `indexOf`の検索は通常の比較演算と同じ振る舞いなので、`NaN`は **絶対にヒットしない** 。つまり`NaN`は取り除けないので、配列に`NaN`が含まれていれば絶対に`NaN`が **返ってくる** 。 ←糞糞糞糞糞糞糞糞糞糞糞糞

## 2. 並び替えてから取得する

### 2.1. sortを使う。

```js:
var array = [/* これにデータが詰まってるとする */];
var clone = array.concat();
clone.sort(function (a, b) {
    var aIsNotNumeric = a == null || isNaN(a);
    var bIsNotNumeric = b == null || isNaN(b);
    if (aIsNotNumeric && bIsNotNumeric) {
        return 0;
    } else if (aIsNotNumeric) {
        return 1;
    } else if (bIsNotNumeric) {
        return -1;
    } else {
        return b - a; // min評価の場合は a - b
    }
});
var max = clone[0];
```

何をしているかというと、基本的には降順ソートをして最初の要素を取得するというもの。

この場合も単純にやってしまうと`NaN`や`null`のでおかしくなる。

配列のソートは `a` と `b` で

- `a`を後ろにまわしたい場合`1`
- `b`を後ろにまわしたい場合`-1`
- 何もしない場合`0`

という戻り値のコントロールを行えばよいので、`NaN` `null` `undefined`が現れたらとにかく配列の後ろに送るようにソートすればよい。

* * *

# 速度評価

`0xFFFF`個要素のある配列を作って、それから上記の処理を100回繰り返したときのパフォーマンス速度。

## Chome
```
[1.1.] --- 521.0349999979371ms
[1.2.] --- 560.3829999890877ms
[1.3.] --- 2905.022999999346ms
[2.1.] --- 44.17900000407826ms
```

## Firefox

```
[1.1.] -> 123 --- 49.050766000000294ms
[1.2.] -> 123 --- 45.754089999999906ms
[1.3.] -> NaN --- 3067.4649800000007ms
[2.1.] -> 123 --- 661.0221620000002ms
```

ChromeとFirefoxでこんなにも結果が異なるとは…。

※1.3.は評価する価値もないですホントは。

# 結論

最初Chromeだけで試してソート方式がいいなと早とちりしてましたが、@gocho さんからご指摘をいただいてFirefoxでの速度が変わると教えていただきました。

**ベスト** ってないのかぁ。。。