🙄

磯野家で学ぶ!配列のミュータブルとイミュータブル

2025/01/15に公開

概要

この記事は、表題の通り磯野家でjsのArray.propertyのミュータブル、イミュータブルを学ぶことを目的としています。

今回使うデータ

// 磯野家
const isonoFamily = [
  { name: "カツオ", age: 11 },
  { name: "ワカメ", age: 9 },
  { name: "波平", age: 54 },
  { name: "フネ", age: 50 }
];

// フグ田家
const fugutaFamily = [
  { name: "サザエ", age: 27 },
  { name: "マスオ", age: 28 },
  { name: "タラオ", age: 3 }
];

ミュータブルな場合

ミュータブルな操作では、「カツオを家族リストから削除する」など、元のデータ(家族のリスト)を直接変更します。

PopやSpliceを使うことで磯野家から存在が消える

jsの配列メソッドにはpopやspliceなど直接割り当てた変数の配列に影響するものが存在します。

// カツオを削除
isonoFamily.splice(1, 1); // 配列のインデックス1(カツオ)を削除
console.log(isonoFamily);
// [
//   { name: "ワカメ", age: 9 },
//   { name: "波平", age: 54 },
//   { name: "フネ", age: 50 }
// ]

この場合、家族リストが破壊的に変更されるため、「カツオが家族から存在しない」状態になり、他のコードがこのデータを使用する際に問題が発生する可能性があります。

これにより次に磯野家でなにか分岐や処理を行いたい場合にカツオは存在しないままとなります。

イミュータブルな場合

イミュータブルな操作では、「カツオが外出中」や「条件に基づいてフィルタリングする」など、新しいリストを作成することで元のデータを維持します。

Array.fillterやArray.mapを使うことで磯野家を基に新しい状態を作ることができる

ex: カツオだけ外出しているパターンを作る

// 新しいリストを作成(カツオを除外)
const isonoFamilyWithoutKatsuo = isonoFamily.filter(member => member.name !== "カツオ");

console.log(isonoFamily);
// 元のリスト:
// [
//   { name: "カツオ", age: 11 },
//   { name: "ワカメ", age: 9 },
//   { name: "波平", age: 54 },
//   { name: "フネ", age: 50 }
// ]

console.log(isonoFamilyWithoutKatsuo);
// 新しいリスト:
// [
//   { name: "ワカメ", age: 9 },
//   { name: "波平", age: 54 },
//   { name: "フネ", age: 50 }
// ]

ex: 年齢で家族を分ける(大人だけでお酒が飲める家族のリストを作成)

// 20歳以上のメンバーを新しいリストとして抽出
const adults = isonoFamily.filter(member => member.age >= 20);

console.log(adults);
// [
//   { name: "波平", age: 54 },
//   { name: "フネ", age: 50 }
// ]

こんな感じで磯野家の状態はそのままで、お酒が飲めるメンバーadultsというグループを作成することができました。

磯野家とフグ田家を合わせるケース

ミュータブルの場合

// ミュータブルに追加
isonoFamily.push(...fugutaFamily); // 直接変更
console.log(isonoFamily);
// [
//   { name: "サザエ", age: 27 },
//   { name: "カツオ", age: 11 },
//   { name: "ワカメ", age: 9 },
//   { name: "波平", age: 54 },
//   { name: "フネ", age: 50 },
//   { name: "マスオ", age: 28 },
//   { name: "タラオ", age: 3 }
// ]

スプレッド構文でフグ田家を追加します。
このとき、fugutaFamilyは影響を受けないですが、磯野家側の定数isonoFamilyは今後フグ田家と同じ値を参照し続けます。

イミュータブルの場合

// イミュータブルに追加
const combinedFamily = [...isonoFamily, ...fugutaFamily];
console.log(combinedFamily);
// [
//   { name: "サザエ", age: 27 },
//   { name: "カツオ", age: 11 },
//   { name: "ワカメ", age: 9 },
//   { name: "波平", age: 54 },
//   { name: "フネ", age: 50 },
//   { name: "マスオ", age: 28 },
//   { name: "タラオ", age: 3 }
// ]

console.log(isonoFamily);
// 元のデータはそのまま保持:
// [
//   { name: "カツオ", age: 11 },
//   { name: "ワカメ", age: 9 },
//   { name: "波平", age: 54 },
//   { name: "フネ", age: 50 }
// ]

こちらは2つの家族の変数を新しい配列combinedFamilyという定数で定義します。
こうすることによって元のisonoFamilyfugutaFamilyはそのままの状態を保持し続けることができます。
こうすることによって保守性を高めることができます。(カツオの戸籍がフグ田家に意図せず移ってしまうことを防ぎます)

ミュータブル vs イミュータブルの違い

ミュータブル

  • 元のデータが直接変更される。
  • 予期しない副作用が発生する可能性がある。
  • 例: カツオがリストから削除され、元のデータからも完全に消える。

イミュータブル

  • 元のデータは変更されない。
  • 新しいデータを生成するので安全。
  • カツオが外出中、新しいリストに「カツオなし」の状態を作るだけで、元のデータには影響を与えない。

まとめ

少しネタっぽくなりましたが、ミュータブルとイミュータブルの概念を理解する一助になれば幸いです。

Discussion