JavaScriptの『map』|”変換”のメソッドを使いこなす!

JavaScript

始めに

JavaScriptを勉強中なのですが、いつも map の書き方を思い出すのに少し時間がかかるので、備忘録としてまとめました。
特に、いわゆる「連想配列(key/value)」で混乱しやすいので、配列 / オブジェクト / Map の3パターンを並べて整理します。

同じように「あれ?どう書くんだっけ?」となってる方の助けになれば嬉しいです!

この記事のゴール
  • map=変換」と説明できる
  • 元配列は不変であることを覚える
  • 連想配列に対してmapをどう使うのか?を迷わなくなる

結論

mapとはズバリ…

各要素を変換して、新しい配列を返すメソッド!

TL;DR(30秒で要点!)
  • array.map(fn)は各要素を変換して、新しい配列を返すメソッド
  • 元の配列は変更されない!
    ⇒ただしミューテート(元の配列の書き換え)してしまわないように注意!
  • オブジェクトに直接mapはできないので、配列化してから使う
    ⇒Mapオブジェクトにもmapメソッドは無い!

mapとは

mapメソッドは、配列の各要素に対して同じ関数を適用し変換できるメソッドです。
元の配列は変換されないので、元の内容を維持しながら、変換後の新しい配列を作ることができます。

基本構文

まずは基本構文を見てみましょう!

JavaScript
Array.prototype.map(callbackFn);
引数内容
callbackFn配列の各要素に対して実行する関数。
この関数で処理され戻ってきた値は、新しい配列の要素として追加される。

引数に「どんな変換処理をしたいのか?」を渡すことで、各要素が引数の処理の通り変換され、新しい配列を作ることができるんですね。

参考) MDN Web Docs

配列に対しての使い方(基本例)

では基本として、数値の配列に対してmapメソッドを使ってみます。

JavaScript
const numList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 各要素を2倍する
const newList = numList.map((n) => n * 2);
console.log("newList⇒", newList);
console.log("numList⇒", numList);

実行結果はこんな感じ↓

newListのほうはすべての要素が2倍になっていますね。
一方で元の配列であるnumListのほうは値が変わっていません。
これらのことからmapメソッドは、

  • 配列のすべての要素を変換できる
  • 元の配列は変換されない

ということが分かります。

元の配列はそのままなので、変換後の配列を使いたい場合は新しい配列に入れてあげないと想定外の動きになってしまう可能性があるので注意!

連想配列に対しての使い方

連想配列とは?

mapの話をする前に、連想配列についておさらいしておきます。

連想配列の特徴
  • {}で囲われている
  • Keyとvalueのセットになっている

JavaScriptでの連想配列と呼ばれるものの書き方はいくつかあるようです。
パターンごとに整理します。

配列の中にオブジェクト

よく使われる形で、配列の中に、{Key:value}のセットになったオブジェクトをいくつも入れている…という形式です。
これは配列なので、普通にmapメソッドを使うことができます。

JavaScript
const users = [
  { id: 1, name: "taro" },
  { id: 2, name: "hanako" },
  { id: 3, name: "jiro" },
];

console.log(users.map((user) => user.id));    // idだけの配列にする
console.log(users.map((user) => user.name));  // nameだけの配列にする

実行結果はこんな感じ↓

[]で囲われているのは配列!

オブジェクトをKey:value形式に

{Key: value}をそのまま変数に入れる形式です。
これはオブジェクトになるので、mapメソッドを使うことができません。

予測変換でもmapが出てきません💦

この場合は、オブジェクトを[Key, value]の配列にすることでmapメソッドを使えるようになります。

JavaScript
const priceTable = { apple: 150, banana: 200, orange: 300 };

// オブジェクトを配列に変換した結果を表示
console.log(Object.entries(priceTable));

// valueを10%プラスした値に変換
console.log(
  Object.entries(priceTable).map(([k, v]) => [k, Math.round(v * 1.1)])
);

実行結果はこんな感じ↓

1行目でオブジェクトを配列にした結果を表示しています。
確認してみると、元々オブジェクトだったものが配列になっていますね。
配列になったのでmapが使えるようになります。

オブジェクトを配列にする場合によく使われるのはこちら!

メソッドどんなことができる?
Object.entries(obj)オブジェクトを[Key, value]の配列にしてくれる
Object.keys(obj)オブジェクトのKeyを配列にしてくれる
Object.values(obj)オブジェクトのvalueを配列にしてくれる

Mapオブジェクト

Mapオブジェクトとは、ES2015で追加されたKeyとvalueのペアを保存できるオブジェクトです。こちらもオブジェクトなので、mapは使えません。
[…配列やオブジェクト]と記述するスプレッド構文というものを使って[Key, value]という配列の形にすることでmapが使えるようになります。

JavaScript
const priceTable = new Map([
  ["apple", 150],
  ["banana", 200],
  ["orange", 300],
]);

// スプレッド構文でpriceTableを配列に
// valueを10%マイナスした値に変換
console.log([...priceTable].map(([k, v]) => [k, Math.round(v * 0.9)]));

実行結果はこんな感じ↓

元の配列が書き変わることもある…!?

mapは元の配列を書き換えることはありません。
しかし、mapのコールバックの中でオブジェクトを直接いじると、元の配列を書き換えてしまうんです!
中身をその場で書き換えること』を『ミューテート』と言います。

JavaScript
const users = [
  { name: "A", active: false },
  { name: "B", active: false },
];

const out1 = users.map((u) => {
  // u は users の要素オブジェクトそのものを指す参照
  u.active = true;   // ← 参照先の中身を書き換えた(ミューテート)
  return u;          // out1 には “同じオブジェクトの参照” が入る
});

console.log(users[0] == out1[0]); // true(同じオブジェクト)
console.log(users[0].active);

実行結果はこんな感じ↓

元の配列も、新しい配列もどちらもtrueになってしまいました。

これは配列の要素が「参照(オブジェクトなど)」だと起こります
逆に基本例で出したような、要素が「数値」の場合は起こりません

参照と値(プリミティブ)の違いはまた別途記事にしたいと思います!

参照の値に対してmapを使うときには、スプレッド構文を使うことでミューテートを回避できます。
書き方は『Mapオブジェクト』のコード例をご参考ください。

配列内の値が「数値か?そうじゃないか?」は気を付けないといけないですね💦

まとめ

読んでいただきありがとうございました!
最後にまとめです🌟

今日のまとめ
  • mapは配列にのみ使えるメソッド
  • 各要素を変換して、新しい配列を返す
  • 要素がオブジェクトだった場合は、配列に変換してからじゃないと使えない
  • 元の配列は書き変わらない
  • ただし、要素が値(プリミティブ)ではない場合は、元の配列を書き換えてしまうことがある!

まだまだ学習途中なので、間違っている部分がありましたら
教えていただけると嬉しいです!🙏

コメント

タイトルとURLをコピーしました