C
JavaScript/オブジェクト・配列/Lesson 12

配列メソッド — *関数型スタイルの核心*

1時間·theory
このチャプター
2/2

配列メソッド — *関数型スタイルの核心*

🎯 このレッスンを読んだ後にできること

このレッスンを読み終えると、次の3つを自信を持ってできるようになります。

  • ✅ map · filter · reduce · find · some · every のチェーン
  • ✅ sort が元の配列を変更すること — 不変にするには toSorted (ES2023) を使用
  • ✅ flat · flatMap · Array.from の活用

学習目標をチェックリストとして持ち、すべてに答えられるようになったらレッスンを閉じてください。

必須の5つのメソッド

要約

JS配列の関数型メソッドmapfilterreducefindsome/every — を覚えるだけで、ループの90%がなくなります。関数型思考の出発点。

map — 各要素を変換

javascript
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);    // 各要素を*2
console.log(doubled);    // [2, 4, 6, 8, 10]
console.log(numbers);    // [1, 2, 3, 4, 5]   ← 元の配列はそのまま! (新しい配列を返す)

const users = [{name:"ホン"}, {name:"イ"}];
const names = users.map(u => u.name);       // 各オブジェクトからnameのみ抽出
console.log(names);      // ["ホン", "イ"]

昔のコード: for ループ + push。モダン: .map() 一行。

filter — 条件を満たすものだけ残す

javascript
const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(n => n % 2 === 0);   // 偶数のみ残す
console.log(evens);     // [2, 4]

const users = [
    { name: 'A', age: 20 },
    { name: 'B', age: 15 },
    { name: 'C', age: 30 }
];
const adults = users.filter(u => u.age >= 18);
console.log(adults);    // [ { name: 'A', age: 20 }, { name: 'C', age: 30 } ]

reduce — 一つに集約する

javascript
const numbers = [1, 2, 3, 4, 5];
// acc: 累積値(開始 0), n: 現在の要素
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum);   // 15   ← 0 + 1 + 2 + 3 + 4 + 5

// 🧮 アキュムレータでオブジェクトも作成可能 — カテゴリ別の数を数える
const items = [
    { category: 'food' },  { category: 'drink' },
    { category: 'food' },  { category: 'food' }
];
const byCategory = items.reduce((acc, item) => {
    acc[item.category] = (acc[item.category] || 0) + 1;
    return acc;
}, {});
console.log(byCategory);   // { food: 3, drink: 1 }

reducemap や filter も再現できる最も強力なメソッドです。最初はわかりにくいですが、慣れると強力です。

find — 最初のマッチ

javascript
const users = [{id:1, name:"ホン"}, {id:2, name:"イ"}];

const user = users.find(u => u.id === 2);
console.log(user);       // { id: 2, name: 'イ' }   ← 条件に一致する最初の要素

const notFound = users.find(u => u.id === 99);
console.log(notFound);   // undefined   ← 見つからない場合は undefined

filterすべてのマッチfind最初の一つだけを返します。

some / every — 真偽チェック

javascript
const numbers = [1, 2, 3, 4, 5];

console.log(numbers.some(n => n > 3));    // true   ← 一つでも満たすか?(4, 5が満たす)
console.log(numbers.every(n => n > 0));   // true   ← すべて満たすか?(全部正の数)
console.log(numbers.every(n => n > 3));   // false  ← 1, 2, 3 が条件を満たさない

if (array.find(...)) より if (array.some(...)) の方が意図が明確です。

チェーン — 組み合わせる

javascript
const orders = [
    { id: 1, amount: 10000, status: 'PAID' },
    { id: 2, amount: 5000,  status: 'PENDING' },
    { id: 3, amount: 20000, status: 'PAID' }
];

const result = orders
    .filter(o => o.status === 'PAID')   // ① 決済完了のみ → [注文1, 注文3]
    .map(o => o.amount)                 // ② 金額だけ抽出 → [10000, 20000]
    .reduce((acc, a) => acc + a, 0);    // ③ 合計 → 30000

console.log(result);   // 30000

filter → map → reduce のパターンは、SQL の WHERE → SELECT → SUM と同じです。意図が一直線に読めます。

よく使う追加メソッド

javascript
// ソート (元の配列の変更に注意)
[3, 1, 2].sort((a, b) => a - b);   // [1, 2, 3]

// 反転 (元の配列を変更)
[1, 2, 3].reverse();   // [3, 2, 1]

// 含まれているか確認
[1, 2, 3].includes(2);   // true

// インデックスを探す
[1, 2, 3].indexOf(2);    // 1

// 平坦化
[[1, 2], [3, 4]].flat();   // [1, 2, 3, 4]

// 結合
const merged = [...arr1, ...arr2];   // spread

よくあるトラップ

1. 元の配列を変更する vs 新しい配列を返す:

  • sortreversepushpopsplice元の配列を変更 ⚠️
  • mapfiltersliceconcat新しい配列を返す
javascript
const arr = [3, 1, 2];
const sorted = [...arr].sort();   // 元の配列を保護

2. forEach vs map:

javascript
arr.forEach(x => console.log(x));   // void、副作用のみ
arr.map(x => console.log(x));        // [undefined, undefined, ...]

forEach は副作用専用、map は新しい配列を作るためのもの。目的に応じて使い分けましょう。

まとめ

  • map・filter・reduce = 関数型の三銃士
  • チェーンで組み合わせる
  • 元の配列の変更 vs 新しい配列の区別は必須
  • for ループを書く機会はほぼなくなる

find · some · every · flatMap — さらに5つのメソッド

find — 最初のマッチだけ

javascript
const users = [
    { id: 1, name: 'A' },
    { id: 2, name: 'B' }
];
const u = users.find(x => x.id === 2);
// { id: 2, name: 'B' }

const missing = users.find(x => x.id === 999);
// undefined

filter とは違い、最初のマッチですぐに終了します。単一レコード検索の標準。

findIndex — 位置のインデックスのみ

javascript
const idx = users.findIndex(x => x.name === 'B');   // 1
const missing = users.findIndex(x => false);         // -1

some / every — boolean を返す

javascript
const nums = [1, 2, 3, 4];
nums.some(n => n > 3);     // true (3より大きいものが*一つでも*あるか)
nums.every(n => n > 0);    // true (*すべて*が正の数か)

入力バリデーションや権限チェックによく使われます:

javascript
if (!users.every(u => u.email)) {
    throw new Error('メールアドレスが欠落しているユーザーがいます');
}

flat / flatMap — ネストを平坦化

javascript
[1, [2, 3], [4, [5]]].flat();      // [1, 2, 3, 4, [5]]
[1, [2, 3], [4, [5]]].flat(2);     // [1, 2, 3, 4, 5]

// flatMap = map + flat(1)
const sentences = ['Hello world', 'Bye now'];
sentences.flatMap(s => s.split(' '));
// ['Hello', 'world', 'Bye', 'now']

Array.from — 配列ライク・イテラブル → 配列

javascript
Array.from('abc');             // ['a','b','c']
Array.from({ length: 5 }, (_, i) => i * 2);
// [0, 2, 4, 6, 8]

const nodeList = document.querySelectorAll('div');
Array.from(nodeList).filter(...);   // NodeList を配列に変換

チェーンパターン — *バイブコーディングの定番表現*

変換・フィルタ・集計を一度に

javascript
const orders = [
    { id: 1, amount: 10000, status: 'paid' },
    { id: 2, amount: 5000,  status: 'pending' },
    { id: 3, amount: 20000, status: 'paid' },
    { id: 4, amount: 3000,  status: 'cancelled' }
];

// 決済完了した注文の合計金額
const total = orders
    .filter(o => o.status === 'paid')
    .map(o => o.amount)
    .reduce((sum, x) => sum + x, 0);
// 30000

for ループ10行 → 4行のチェーン。AIが生成するコードのほぼ標準パターン。

ソートしてから上位N件

javascript
const top3 = users
    .filter(u => u.active)
    .sort((a, b) => b.score - a.score)
    .slice(0, 3);

注意: sort()元の配列を変更します。イミュータブルにしたい場合は [...users].sort(...) または users.toSorted(...) (ES2023) を使いましょう。

グループ化 + 変換

javascript
const byStatus = orders.reduce((acc, o) => {
    (acc[o.status] ??= []).push(o);
    return acc;
}, {});
// { paid: [...], pending: [...], cancelled: [...] }

ES2024 では Object.groupBy(orders, o => o.status) の一行で完結します。

よくあるトラップ

1. reduce の初期値を忘れる

javascript
[].reduce((a, b) => a + b);   // ❌ TypeError
[].reduce((a, b) => a + b, 0); // ✅ 0

空の配列に初期値がないとエラーになります。常に初期値を明示しましょう

2. map の中で副作用を起こす

javascript
// ❌ console.log のために map を使う
users.map(u => console.log(u));

// ✅ forEach または for...of を使う
users.forEach(u => console.log(u));

map は新しい配列を作ることが目的です。結果を使わないなら forEach が正解です。

🤖 AIにこう依頼してみよう

  • 「この for ループを filter/map/reduce チェーンに変えて」
  • 「このコードで orders の中から status='paid' のものだけ amount を合算して出力して」
  • 「この sort の呼び出しが元の配列を変更しないよう toSorted に書き換えて」

⚡ やってみよう — map · filter · reduce のチェーン

コアの5つのメソッドとチェーンを一度にまとめて実行してみましょう。
✏️ JS 코드
📟 コンソール出力
▶ 実行ボタンを押してください
⚠️ ブラウザのサンドボックスで実行 — console.log()のみ対応、alert/fetchは不可
配列の高階関数 - JavaScript