0からわかる!JavaScript mapの使い方や注意点を実践形式で解説

「JavaScript mapってどうやって使うの?」
「どのようにmapが使われているのか、コードが見てみたい!」
「mapでたくさんエラーが出る…、気を付けるポイントを知りたい!」

この記事に訪れたあなたはこのような疑問を持っていませんか?

JavaScriptの配列を学習していると必ず学ぶことになる関数にmapがあります。

mapは様々な配列の操作が簡単にできる便利なものですが、使い方がたくさんあり全部覚えきるのが大変ですよね。

また慣れるまでが難しく、よくわからないエラーが出て困ってしまうこともあるでしょう。

そこで今回は、mapの使い方や注意点をサンプルコードを載せながら解説していきます。

この記事を読めばどんな処理がされているのか視覚的にわかりすいので、まったくmapを知らない人でも理解できますよ。

JavaScript mapとは?

JavaScript mapは配列の各要素に対して処理をして、新しい配列を作れる便利な機能です。

for文やforeachと似ていますが、mapはより簡単にコードが書けてより多様なことができちゃうんですね。

mapの書き方を以下に載せます。

listname.map( function ( value ){ … });

配列listnameにmapで、各要素をvalueに格納して処理をしていく感じですね。これだけ見てもよくわからないと思いますので、サンプルコードを見ていきましょう。

var number = [1,2,3,4,5];

number.map( function ( num ){
console.log(num);
});
// 実行結果
1
2
3
4
5

上記のコードはnumber配列を使ってmapを使っていますね。

そして各要素をnumに入れ、それぞれの要素に対してconsole.log(num);で出力しています。配列の中身が[1,2,3,4,5]なので上記の実行結果が得られるわけなんですね。

今度は、取り出した値を新しい配列に入れてみましょう。

var number = [1,2,3,4,5];

var Number = number.map( function ( num ){
return num;
});
console.log(Number);
// 実行結果
[ 1, 2, 3, 4, 5 ]

先ほどのmapをNumber変数に入れていますね。実はmapを変数に入れることで新しい関数が作れるようになるんですね。

mapの中身に注目してください。

return num;

returnでnumを返すことで、新しく作成した配列Numberにnumを入れているんです。よって最後にNumberを出力すると上記のような実行結果が得られるわけなんですね。

JavaScript mapの特徴

mapは新しい配列を作るのに向いていると解説しましたが、「これ、for文でもいいような…」と感じる人もいるでしょう。

確かに、取り出した値を順番に配列へ入れる操作はfor文でもできますよね。

しかしmapには簡単に配列が作れるだけでなく、以下のような処理をするのにも向いているんです。

  • 配列を更新して複製できる
  • ラムダ式で記述できる
  • 新しいオブジェクトを作成できる
  • forEachがとても使いやすい

それぞれの項目について、コードを載せながら解説していきます。

配列を更新して複製できる

これは先ほど変数にmap関数を入れることで新しい配列が作れると解説しましたね。

ですが配列を更新するのも簡単に作れるんです。

var number = [1,2,3,4,5];

number = number.map( function ( num ){
return num + 5;
});
console.log(number);
// 実行結果
[ 1, 2, 3, 4, 5 ]

上記のコードは配列numberの各要素に5を足している様子のプログラムです。今回もmapの中身に注目してみましょう。

return num + 5;

numに5を足してreturnしているのがわかったでしょうか?

上記の部分が何を行っているのかというと、取り出した値をnumberに入れる際に5足されているんです。

つまり、すべての要素に5足されて更新しているように見えるわけなんですね。

値を更新する処理はfor文でもできます。それが以下のコードです。

var number = [1,2,3,4,5];

for(var i=0; i<5; i++){
number[i] = number[i] + 5;
}
console.log(number);
// 実行結果
[ 1, 2, 3, 4, 5 ]

for文でも全く同じ実行結果にはなりましたが、「なんだか見づらいな…」と思いませんか?

number[i]が連続して出ているので、若干見づらくなってしまっています。

このようにmapではどんな処理をしているのかわかりやすいくらい、簡単にコードが書けちゃうんですね。

ラムダ式で記述できる

「for文よりは見やすいけど、functionの部分が少しわかりづらい…」
「正直、for文とあまり変わらないような…」

と思ったそこのあなた。ラムダ式を使えばmapがもっと簡単に記述できますよ。

ラムダ式とは、関数を変数のように扱えるもので、アロー演算子を使って記述できるんですね。

先ほどの配列numberを使ってラムダ式で記述していきます。

var number = [1,2,3,4,5];
number = number.map( num => {return num + 5 });
console.log(number);
// 実行結果
[ 1, 2, 3, 4, 5 ]

map関数で新しく作成した変数numに=>を付けてそのままやってほしい処理を書くだけですね。

実行結果も全く同じになっているため、1行に収められる点もあってラムダ式はとてもわかりやすくコードが書けるんです。

最近ではラムダ式を用いることが主流になっていますので、書けるようになっておくといいでしょう。

新しいオブジェクトを作成できる

オブジェクトとは、値とその値のキーが一緒になったデータのまとまりのことを言います。値とその値のキーのことをまとめてプロパティと呼ぶこともありますね。

var number = {one:1,two:2,three:3,four:4,five:5};

上記がオブジェクトの例なのですが、オブジェクトはmap関数で作れちゃうのは知っていましたか?

var number = new Map();
number.set('one',1);
number.set('two',2);
number.set('three',3);
number.set('four',4);
number.set('five',5);
console.log(number);
// 実行結果
Map(5) {
  'one' => 1,
  'two' => 2,
  'three' => 3,
  'four' => 4,
  'five' => 5
}

実行結果を見ると、確かにオブジェクトが作れていますね。ではどのようにしてオブジェクトが作られているのかを1行ずつ見ていきましょう。

まずは1行目を見ると、新しくMap()を作成しています。そして2行目からnumber.setで値とその値のキーを入れているんですね。

第一引数にキーを、第二引数にキーの値を入れることで上記の実行結果のようになりますよ。

また指定したキーの値を取り出したり、削除したりすることもできちゃうんですね。コードを載せて解説していきます。

var number = new Map();
number.set('one',1);
number.set('two',2);
number.set('three',3);
number.set('four',4);
number.set('five',5);
number.delete('two');
console.log("one : "+number.get('one'));
console.log(number);
// 実行結果
one : 1
Map(4) { 'one' => 1, 'three' => 3, 'four' => 4, 'five' => 5 }

指定したキーの値を出力するにはgetを、指定したキーの値を削除するにはdeleteを使います。これらをうまく使えば、オブジェクトの管理ができてしまうわけなんですね。

オブジェクトを追加したり、削除したりと簡単に操作ができるのはmapならではのメリットといえますよ。

forEachがとても使いやすい

作成したオブジェクトの各要素の値にある処理をしたいことってよくありませんか?

配列なら各要素に同じ処理をするのはforeachが最適です。

しかしオブジェクトを使ってforeachで処理を回すときは、キーだけの配列を取得してからでないといけなく、とても複雑になってしまいますよね。

map関数なら新しい配列を作るなどといったことはせず、そのままforeachを使えます。

var number = new Map();
number.set('one',1);
number.set('two',2);
number.set('three',3);
number.set('four',4);
number.set('five',5);

number.forEach( function (num) {
    console.log(num);
});
// 実行結果
1
2
3
4
5

上記のコードがやっていることは、Mapで作成したオブジェクトnumberにforeachで処理を回しているだけ。

いつもの書き方と全く同じでオブジェクトの値を取り出せてしまうんですね。

ちなみに、Mapで作成していない普通のオブジェクトにforeachで値を取り出すコードはこちら。

var number = {one:1,two:2,three:3,four:4,five:5};

Object.keys(number).forEach( function (num){
console.log(number[num]);
});
// 実行結果
1
2
3
4
5

実行結果は同じになりましたが、foreachの部分がやや見づらくなってしまっていますね。

上記のコードと比べてもmapがどれだけ簡単にコードが書けるかわかったかと思います。

mapの使い方

mapの書き方について一通り解説してきました。とても便利な機能であることがわかりましたね。

次は実際にプログラミングしてmapの便利さを実感していきましょう。

「mapを使ってさらに見やすくて簡単なコードを書きたい!」という方は自分の手でプログラムを組んでみてくださいね。

使用例1: 配列を引数とする関数を使って、連想配列の中身を表示する

連想配列food = [{name:’barger’},{name:’potato’}];があった時、配列を引数とする関数を作成しfoodのキーの値を出力させます。

つまり以下のような実行結果を出したいわけなんですね。

barger
potato

それではキーの値を出力させる関数を作っていきましょう。

関数の名前をfoodnameとして、配列を引数とするので以下のようになります。

function foodname(lists){ … }

作成した関数の引数は配列なので、foodnameにはfoodを引数として入れましょう。
次に関数の中で中身を表示する処理を書けばいいだけなんですね。

lists.map(list => console.log(list.name));

関数として引き渡されたlistsに、mapでキーがnameの値を出力しています。

listsには配列foodが渡されているので、結果として連想配列の中身を表示できるわけです。

完成したコードは以下のようになります。

var food = [{name:'barger'},{name:'potato'}];
function foodname(lists){
    lists.map(list => console.log(list.name));
}
foodname(food);
// 実行結果
barger
potato

使用例2: オブジェクトを引数とする関数を使って、連想配列の中身を表示する

使用例1で使った関数を少し変えて、オブジェクトを引数とする関数にしてみましょう。

function foodname({lists}){
    lists.map(list => console.log(list.name));
}

引数のlistsを{lists}に書き換えることで、引数がオブジェクトの関数になりましたね。

そのあとはどうすればいいと思いますか?「これで、このまま実行すればいいのかな?」と感じる方もいるかもしれませんので、このまま実行してみましょう。

var food = [{name:'barger'},{name:'potato'}];
function foodname({lists}){
    lists.map(list => console.log(list.name));
}
foodname(food);
// 実行結果
lists.map(list => console.log(list.name));
^
TypeError: Cannot read property 'map' of undefined

実行した結果、エラーが出てしまいましたね。これは関数の引数{lists}がオブジェクトなのに対し、受け渡したfoodは配列のため上記のエラーが出てしまうんです。

ということで、新しいオブジェクトfood2を作成して受け渡してみましょう。

var food = [{name:'barger'},{name:'potato'}];
var food2 = {lists:food};
function foodname({lists}){
    lists.map(list => console.log(list.name));
}
foodname(food2);
// 実行結果
barger
potato

今度はしっかりと中身が表示できました。少し複雑なので、変わった部分について解説していきますね。

まずは2行目を見てみると、新しく作成したオブジェクトfood2があります。

そして3行目の{lists}ですが、ここでfood2のキーはlists、値はfoodになっていることに注目してください。

キーのlistsはfoodnameの引数と同じ文字になっているのがわかりますね?

そして引数はオブジェクトなので、ここでは渡されたオブジェクトのキーがlistsの値であるfoodが呼び込まれるわけなんです。

あとは使用例1の時と同様にして、mapで配列foodの値を出力していきます。

使用例3: 文字列を数列にして、配列に入れる

次は文字列”1,2,3,4,5”をカンマ区切りで数列にして、配列に入れてみましょう。

理想としては以下のような配列になって欲しいわけですね。

[ 1, 2, 3, 4, 5 ]

ますはカンマ区切りで配列にしてから、文字列を数列に直していきましょう。

カンマ区切りで配列にするにはsplit()を使います。

split(“x”)のように指定することで、指定した文字列で区切ってくれるので、カンマ区切りをしたい場合はsplit(“,”)と書けますね。

配列に変換出来たら次は、数列への変換です。数列への変換はNumber()によって可能ですが、せっかくmapを覚えたので使ってみましょう。

number.split(",").map(Number);

たったこれだけで数列にした配列が作れちゃうんです。

内容もmapで新しく配列を作っているので非常にわかりやすいですよね。

あとは数列になっているか確認してみましょう。完成したコードは以下のようになります。

var number = "1,2,3,4,5";
var array = number.split(",").map(Number);
console.log(array);
// 実行結果
[ 1, 2, 3, 4, 5 ]

ちゃんと数列に変換されていましたね。新しく配列を作るには、それを格納する変数が必要になるので気を付けてください。

使用例4: 配列の各要素に1.2を掛けた配列を出力する

最後に配列number = [1,2,3,4,5]の各要素に1.2を掛けた排列を出力してみましょう。

この記事を読んできたあなたなら、「配列numberにmap関数で、各要素に1.2を掛けた数をreturnすれば良さそうだ、簡単だな」と思ったはず。

完成したコードはこちらになります。

var number = [1,2,3,4,5];

number = number.map( function ( num ){
    return num * 1.2;
});
console.log(number);
// 実行結果
[ 1.2, 2.4, 3.5999999999999996, 4.8, 6 ]

コード自体はとても簡単ですね。ですが実行結果を見て「ん!?」となったのではないでしょうか。

実行後の配列の2番目が3.6になるところ、3.5999999999999996になってしまっています。

実はJavaScriptは少数の計算がとても苦手で、3.5999999999999996のような現象が起きてしまうのはJavaScriptが正しい値だと認識してしまうからなんですね。

「じゃあ、どうしたらいいの?」と思われた方もいるでしょう。やや面倒になってしまいますが、正しい値で出力されるようにする方法はあります。

それは整数値のみで計算を行う方法。例えば上記のコードなら

return num * 1.2;

return num * 12/10;

のように書き換えれば正しい値で出力されるようになるんですね。

本来なら、整数と整数で掛け算できるよう関数を作っておくべきですが、かなり複雑になってしまうので手打ちで12/10と書いてしまっても問題ないでしょう。

mapを使うときの3つの注意点

mapの使い方がわかったところで早速プログラミングすると、なんだかよくわからないエラーが起きてしまい困ってしまうことってありませんか?

「サンプルコードを写したのにエラーが起きてしまう…」
「xxx is not a functionってエラーが出たけどこれなんだろう?」

このような悩みを持つ人は、もしかすると次のエラーを起こす要因を作っているかもしれません。

  • 中括弧で括っていない
  • 使用できない型にmapしている
  • breakやcontinueを使っている

それぞれの要因について、詳しく見ていきましょう。

注意点1: アロー関数の中身は中括弧で括る

次のコードには間違いがあります。いったいどこに間違いがあると思いますか?

var number = [1,2,3,4,5];
number = number.map( num => return num; );
console.log(number);

正解は、「=>の先を中括弧で括っていない」です。中括弧で括らずエラーを起こしてしまったこと、あなたも経験しているのではないでしょうか。

パッと見て間違っているところはないように見えるため、間違いに気づかず時間だけが経ってしまったこともあります。

そんな無駄な時間を消費しないためにも、「アロー関数の中身は中括弧で括っているか?」をしっかりと確認しておきましょう。

正しいコードは以下の通りです。

var number = [1,2,3,4,5];
number = number.map( num => { return num; });
console.log(number);
// 実行結果
[ 1, 2, 3, 4, 5 ]

ちなみになぜエラーを起こすのかについては、=>がreturnを指しているのか、numを指しているのかわからないためにエラーが出るんですね。

つまり以下のコードはエラーが起きずに通ってくれます。

number = number.map( num => console.log(num) );

中括弧には、return num;が一つのまとまりであることを教えてくれると覚えておくといいでしょう。

注意点2: 通常のオブジェクトには使用できない

次のコードもあなたがやってしまっているかもしれない、間違ったコードです。どこが間違っているのか考えてみてください。

var number = {one:1,two:2,three:3,four:4,five:5};
number = number.map(  num => { num + 5; } );
console.log(number);
// 実行結果
number = number.map(  num => { num + 5; } );
                ^
TypeError: number.map is not a function

正解は「通常のオブジェクトにmap関数を使っている」です。

mapが使えるのは配列かMapで作成したオブジェクトなので、通常のオブジェクトにmapを使おうとするとタイプエラーが起きてしまいます。

エラーメッセージの「xxx is not a funtion」が出た場合は、mapが使えないものに無理やりmapを使おうとしていないかチェックしてみてください。

注意点3: breakやcontinueは使えない

またmap関数のデメリットとして、breakやcontinueが使えない点があります。

例えば条件分岐でbreakをしたいと思い、以下のコードを書いたとしましょう。

var number = new Map([
['one',1],
['two',2],
['three',3],
['four',4],
['five',5]
]);

number = number.map( num => {
if(num+5 > 8) break;
console.log(nun);
});

そして上記のコードを実行するとエラーが出てしまいました。

ついつい書きたくなってしまいたくなるコードですが、map関数ではbreakが使えないんですね。continueも同様に使えません。

「どうしてもbreakを使った処理がしたい!」と困っているあなたは、for-of文を使用すればbreakが使えますよ。

var number = new Map([
['one',1],
['two',2],
['three',3],
['four',4],
['five',5]
]);

var array = number.values();

for(var num of array){
    if(num+5 > 8) break;
    console.log(num+5);
}
// 実行結果
6
7
8

number.values();でオブジェクトnumberの値だけを取り出して配列にして、for-of文で配列arrayを回しています。

これなら条件分岐でbreakやcontinueができそうですね。

JavaScript mapを学習する方法

JavaScriptのmapの使い方に関してある程度解説してきましたが、ここまで読まれた方の中には「難しくてよくわからない…」と感じる方もいるはず。

mapはとても便利な機能ですが、やや複雑な記述をすることもあるので、この記事を読んだだけではマスターするのは難しいですよね。

mapを理解する一番の近道は、やはり自分の手でコードを書いていくのがいいでしょう。

コードを書きながら「この部分はどう動いているのか?」を考えればmapの仕組みも理解しやすくなります。

また参考書を購入して学習するのもあり。参考書ならより詳しいmapの説明がなされています。

for-of文などの様々な構文も合わせて学習できるので、JavaScriptの学習にはもってこいの方法といえますよ。

「でも、どんな参考書を買ったらいいんだろう?」と参考書の購入に迷ったら、以下の記事でおすすめの参考書を紹介していますので、ぜひ目を通していってくださいね。

【2021年最新】Javascriptの学習本・参考書おすすめ13選!初心者から経験者まで

まとめ

今回はJavaScriptのmapを紹介しました。連想配列の操作が簡単に行えたり、簡潔な文で書けたりととても便利な機能であることが分かりましたね。

mapを上手く使いこなすことで、様々な配列の操作をすらすらと書けるようになりますし、どんな処理がされているのか一目でわかるくらい見やすいコードが作れちゃうんです。

もちろんmapだけをマスターするのでは配列の操作は完璧にできません。少数の掛け算やbreakなど、mapではできないような操作もありましたね。

配列を扱えるようになるには、mapが苦手とする操作ができる構文も覚える必要があります。

「この操作には、この構文が一番向いている!」とすぐに考えられるよう他の構文も学習できるといいですね。

こちらの記事もおすすめ

お婆ちゃんでもわかる!JavaScript for文の使い方・注意点を実例解説