35
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JavaScriptのmap/parseInt問題の解説

Last updated at Posted at 2017-03-31

TL;DR

  • ['10', '10', '10'].map(parseInt) は直感に反する結果 [10, NaN, 2] を返す。
  • これはparseIntが省略可能な第2引数を取り、mapがコールバックに3個の引数を渡すため。
  • この問題を回避するためには、 ['10', '10', '10'].map(e => parseInt(e)) のように匿名関数でラップしてmapからparseIntに第2引数が渡されるのを抑止すれば良い。

問題

JavaScriptで数字文字列があり、これを数値の配列にしたいとします。

'10' // これを
 10  // こうしたい

こんなときは標準関数parseIntが使えます。

parseInt('10'); 
// => 10

さて、今度はJavaScriptで数字文字列の配列があり、これを数値の配列にしたいとします。

['10', '10', '10'] // これを
[ 10 ,  10 ,  10 ] // こうしたい

配列の全要素に関数を適用するには、Arrayのメソッドmapを使えばいいですね。

['10', '10', '10'].map(parseInt);
// => [10, NaN, 2] // あれ!?

解説

なぜこのような結果が返ってくるのでしょうか?
まずArray.prototyp.mapのコールバックに渡される引数の仕様を見てみましょう。

callbackfn is called with three arguments: the value of the element, the index of the element,
and the object being traversed.

(意訳) コールバック関数には3個の引数が渡される。 要素値、インデクス、そして走査する配列オブジェクトだ。
http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.map

次にparseIntの仕様を見てみましょう。
parseIntは第1引数に変換する文字列、第2引数に基数を受け取ります。
ただし、第2引数を省略した場合は、下記の動作をします。

If radix is undefined or 0, it is assumed to be 10 except when the number begins with the code unit pairs 0x or 0X,
in which case a radix of 16 is assumed.

(意訳) 基数が未指定か0の場合、10進数とみなして評価される。ただし、文字列が0xまたは0Xから始まる場合は16進数とみなされる。
http://www.ecma-international.org/ecma-262/6.0/#sec-parseint-string-radix

以上を踏まえて、先のコードがどう動くか考えてみます。

['10', '10', '10'].map(parseInt);
  • 1番目の要素: parseInt('10', 0, ['10', '10', '10']) => '10'を10進数とみなし10
  • 2番目の要素: parseInt('10', 1, ['10', '10', '10']) => 不正な基数1なのでNaN
  • 3番目の要素: parseInt('10', 2, ['10', '10', '10']) => '10'を2進数のイチゼロとみなし2

実に「正しく」変換されているのが分かりますね。
さすがJavaScriptさんやでー。

解決

mapのコールバックとしてparseIntを直接渡すのではなく、匿名関数でラップします。

['10', '10', '10'].map(e => parseInt(e, 10)); // (1) 明示的に10進数とみなして変換
['10', '10', '10'].map(e => parseInt(e, 0));  // (2) 自動判定で変換(この配列の場合はすべて10進数)
['10', '10', '10'].map(e => parseInt(e));     // (3) 同上

上記のいずれも [10, 10, 10] を返します。

35
16
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?