Array.prototype.map()

map() メソッドは、与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列を生成します

構文

var new_array = arr.map(function callback(currentValue[, index[, array]]) {
    // 新しい配列の要素を返す
}[, thisArg])

引数

callback
新しい配列の要素を生成するための関数です。次の 3 つ引数を取ります。
currentValue
現在処理中の要素の値です。
indexOptional
現在処理中の要素の配列内におけるインデックスです。
arrayOptional
map が実行されている配列です。
thisArgOptional
callback を実行するときに this として使う値です。

返値

与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列です。

解説

map は、与えられた callback 関数を配列の順番通りに、各要素に対して一度ずつ呼び出し、その結果から新しい配列を生成します。callback は、値が代入されている配列のインデックスに対してのみ呼び出されます(undefined が代入されているものも含みます)。すでに削除されたインデックスや、まだ値が代入されていないインデックスに対しては呼び出されません。

map は新しい配列を作成するので、返された配列を使わない場合、map を使うのはアンチパターンです。代わりに forEach または for-of を使用してください。返される配列を使わない場合、または callback から値を返さない場合、それは map を使うべきではないというサインです。

callback は、要素の値、要素のインデックス、走査されている Array オブジェクトという 3 つの引数をともなって呼び出されます。

mapthisArg 引数が与えられた場合は、 callback の呼び出しのたびにそのオブジェクトが this として使用されます。引数が省略された場合は、 this の値として undefined が渡されます。 callback によって最終的に識別できる this の値は、関数における通常の this を決定するルールに従って決まります。

map は呼び出された配列を変化させません (ただし、呼び出された callback が配列を変更する可能性はあります)。

map によって処理される要素の範囲は、 callback が最初に呼び出される前に設定されます。 map の呼び出しが開始された後に追加された要素に対しては、 callback は実行されません。既存の配列要素が変更されたり、削除された場合、 callback に渡される値は map がそれらを訪れた時点での値になり、 map が削除された要素を訪問することはありません。

仕様書で定義されているアルゴリズムによって、 map が呼び出された配列がまばらである場合、結果の配列も同じインデックスを空白に保ちます。

数値の配列を平方根の配列にマッピングする

次のコードは、数値からなる配列を取り、それらの数値の平方根からなる新しい配列を生成します。

var numbers = [1, 4, 9];
var roots = numbers.map(function(num) {
return Math.sqrt(num)
});
// roots の内容は [1, 2, 3] となる
// numbers の内容は [1, 4, 9] のまま

map を使用して配列内のオブジェクトを再フォーマット

次のコードは、オブジェクトの配列を受け取り、新たにフォーマットされた新しい配列を生成しています。

var kvArray = [{key: 1, value: 10}, 
               {key: 2, value: 20}, 
               {key: 3, value: 30}];

var reformattedArray = kvArray.map(obj =>{ 
   var rObj = {};
   rObj[obj.key] = obj.value;
   return rObj;
});
// フォーマットされた配列の内容は [{1: 10}, {2: 20}, {3: 30}]となる 

// kvArray は
// [{key: 1, value: 10}, 
//  {key: 2, value: 20}, 
//  {key: 3, value: 30}]
// のまま

引数を含む関数を使用して数値配列をマッピングする

次のコードは、1 つの引数を必要とする関数を使用するときに map がどのように動作するかを示しています。引数は元の配列を通した map ループとして、配列の各要素に自動的に割り当てられます。

var numbers = [1, 4, 9];
var roots = numbers.map(function(num) {
return Math.sqrt(num)
});
// roots is now [1, 2, 3]
// numbers is still [1, 4, 9]

汎用的な map の使用

以下の例は、各文字を表す ASCII エンコードのバイトの配列を得るために String に map を使う方法を示しています。:

var map = Array.prototype.map;
var a = map.call('Hello World', function(x) { 
  return x.charCodeAt(0); 
});
// a の内容は [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100] となる

汎用的な map の使用: querySelectorAll

この例では、querySelectorAll によって収集されたオブジェクトのコレクションを反復処理する方法を示します。この場合、選択したすべてのオプションが画面に表示され、コンソールに表示されます。
この場合、画面に選択されているすべてのオプションの値を返します。

var elems = document.querySelectorAll('select option:checked');
var values = Array.prototype.map.call(elems, function(obj) {
  return obj.value;
});

もっと簡単な方法は Array.from() メソッドを使用することです。

トリッキーな使用例

(このブログの記事に創発されました)

コールバック関数は第一引数 (変換するべき要素) だけを意識して指定するケースがほとんどだと思います。しかし一般的に第一引数しか使わないような関数でも、実は追加のオプション引数を受け取っている場合があります。これは混乱につながる可能性があります。

まずこの例をご覧ください。

["1", "2", "3"].map(parseInt);

返値は [1, 2, 3] となりそうですが、実際には [1, NaN, NaN] となります。

parseInt は大抵一つの引数のみで使われますが、実際には二つの引数を取っています。一つ目は数値文字列、二つ目は基数です。 Array.prototype.map はコールバックに次の 3 つの引数を与えています。

  • その要素
  • そのインデックス
  • その配列

parseInt は三つ目の引数を無視しますが、二つ目の引数は処理の対象としますので、混乱を起こす可能性があります。繰り返し手順の正確な例は以下の通りです。

// parseInt(string, radix) -> map(parseInt(value, index))
/*  first iteration (index is 0): */ parseInt("1", 0); // 1
/* second iteration (index is 1): */ parseInt("2", 1); // NaN
/*  third iteration (index is 2): */ parseInt("3", 2); // NaN

解決策を考えてみましょう。

function returnInt(element) {
  return parseInt(element, 10);
}

['1', '2', '3'].map(returnInt); // [1, 2, 3]
// 期待した通り、数値の配列が返る。

// アロー関数構文を使って、より簡潔に上記と同じ結果を得ることが出来ます。
['1', '2', '3'].map( str => parseInt(str) );

// ちなみにこの命題ではもっと簡単に同じ結果を得る方法があります。
['1', '2', '3'].map(Number); // [1, 2, 3]

// parseInt() とは違って、 Number() は float または (解決した) 指数表現を返します。
['1.1', '2.2e2', '3e300'].map(Number); // [1.1, 220, 3e+300]
// 比較のために、上記の配列に parseInt() を用いると次のようになります。
['1.1', '2.2e2', '3e300'].map( str => parseInt(str) ); // [1, 2, 3]

parseInt を引数として呼び出された map メソッドの代替出力の 1 つは、次のように実行されます。

var xs = ['10', '10', '10'];

xs = xs.map(parseInt);

console.log(xs);
// 実際の結果 10,NaN,2 は上記の説明からすると意外なものかもしれません。

undefined を持つ配列のマッピング

undefined または nothing を返すと、以下のものを返します。

var numbers = [1, 2, 3, 4];
var filteredNumbers = numbers.map(function(num, index) {
  if(index < 2) {
     return num;
  }
});
// filteredNumbers は [1, 2, undefined, undefined]
// numbers は [1, 2, 3, 4] のまま

ポリフィル

map は ECMA-262 標準に最近追加されたものである為、標準準拠を謳う実装中に存在しない場合があります。次のコードをスクリプトの先頭に挿入すると、 map がネイティブでサポートされていない ECMA-262 実装でも map を使用できるようになります。このアルゴリズムは ECMA-262 第 5 版で指示されたアルゴリズムと全く同じものです。 ObjectTypeErrorArray はそれぞれオリジナルの値を持ち、またそれらの Function.prototype.call のオリジナルの値として評価されます。

// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.io/#x15.4.4.19
if (!Array.prototype.map) {

  Array.prototype.map = function(callback/*, thisArg*/) {

    var T, A, k;

    if (this == null) {
      throw new TypeError('this is null or not defined');
    }

    // 1. Let O be the result of calling ToObject passing the |this| 
    //    value as the argument.
    var O = Object(this);

    // 2. Let lenValue be the result of calling the Get internal 
    //    method of O with the argument "length".
    // 3. Let len be ToUint32(lenValue).
    var len = O.length >>> 0;

    // 4. If IsCallable(callback) is false, throw a TypeError exception.
    // See: http://es5.github.com/#x9.11
    if (typeof callback !== 'function') {
      throw new TypeError(callback + ' is not a function');
    }

    // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
    if (arguments.length > 1) {
      T = arguments[1];
    }

    // 6. Let A be a new array created as if by the expression new Array(len) 
    //    where Array is the standard built-in constructor with that name and 
    //    len is the value of len.
    A = new Array(len);

    // 7. Let k be 0
    k = 0;

    // 8. Repeat, while k < len
    while (k < len) {

      var kValue, mappedValue;

      // a. Let Pk be ToString(k).
      //   This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the HasProperty internal 
      //    method of O with argument Pk.
      //   This step can be combined with c
      // c. If kPresent is true, then
      if (k in O) {

        // i. Let kValue be the result of calling the Get internal 
        //    method of O with argument Pk.
        kValue = O[k];

        // ii. Let mappedValue be the result of calling the Call internal 
        //     method of callback with T as the this value and argument 
        //     list containing kValue, k, and O.
        mappedValue = callback.call(T, kValue, k, O);

        // iii. Call the DefineOwnProperty internal method of A with arguments
        // Pk, Property Descriptor
        // { Value: mappedValue,
        //   Writable: true,
        //   Enumerable: true,
        //   Configurable: true },
        // and false.

        // In browsers that support Object.defineProperty, use the following:
        // Object.defineProperty(A, k, {
        //   value: mappedValue,
        //   writable: true,
        //   enumerable: true,
        //   configurable: true
        // });

        // For best browser support, use the following:
        A[k] = mappedValue;
      }
      // d. Increase k by 1.
      k++;
    }

    // 9. return A
    return A;
  };
}

仕様書

仕様書 状態 備考
ECMAScript Latest Draft (ECMA-262)
Array.prototype.map の定義
ドラフト
ECMAScript 2015 (6th Edition, ECMA-262)
Array.prototype.map の定義
標準
ECMAScript 5.1 (ECMA-262)
Array.prototype.map の定義
標準 初回定義。 JavaScript 1.6 で実装。

ブラウザーの互換性

Update compatibility data on GitHub
デスクトップモバイルサーバー
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewAndroid 版 ChromeAndroid 版 FirefoxAndroid 版 OperaiOSのSafariSamsung InternetNode.js
mapChrome 完全対応 ありEdge 完全対応 12Firefox 完全対応 1.5IE 完全対応 9Opera 完全対応 ありSafari 完全対応 ありWebView Android 完全対応 ありChrome Android 完全対応 ありFirefox Android 完全対応 4Opera Android 完全対応 ありSafari iOS 完全対応 ありSamsung Internet Android 完全対応 ありnodejs 完全対応 あり

凡例

完全対応  
完全対応

関連情報