404 Not Found

404 Not Found


nginx

JavaScriptの配列応用

前のレッスンでは基本的な配列操作を学びました。ここでは配列の「パワームーブ」— 高階メソッドでレベルアップしましょう。より少ないコードでより多くのことができ、モダンJavaScript開発では不可欠なスキルです。

map — 各要素を変換する

mapはすべての要素に関数を実行し、新しい配列を返します。元の配列は変更されません。組み立てラインのように考えましょう:各要素が入り、変換され、新しい製品の一部として出てきます。

HTML
<script>
let prices = [10, 20, 30];
let doubled = prices.map(p => p * 2);   // [20, 40, 60]
</script>

コールバックは3つの引数を受け取ります:(要素, インデックス, 元の配列)。ほとんどの場合、最初の1つだけが必要です。

filter — パスしたものを残す

filterはテストに合格した要素を残し、新しい配列を返します。元の配列は変更されません。セキュリティチェックポイントのように、条件を満たすアイテムは通過し、残りはフィルターされます。

HTML
<script>
let scores = [45, 80, 92, 55, 78, 98];
let passed = scores.filter(s => s >= 60);  // [80, 92, 78, 98]
</script>

コールバックがtrueを返す要素は残され、falseを返す要素は破棄されます。

reduce — 1つの値に蓄積する

reduceは配列を1つの値に「圧縮」します。最も一般的な用途は合計です:

HTML
<script>
let nums = [1, 2, 3, 4, 5];
let sum = nums.reduce((acc, cur) => acc + cur, 0);  // 15
</script>

パラメータの意味:

💪 reduceは最初は複雑に見えますが、本質はシンプルです。ステップバイステップで配列を1つの値に圧縮します。合計、最大値の検索、出現回数のカウント — reduceはすべて対応できます。

実例:map、filter、reduceの組み合わせ

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>map / filter / reduce</h2>
  <div id="output"></div>
  <script>
    let products = [
      { name: 'ノートPC', price: 999, category: '電子機器' },
      { name: 'Tシャツ', price: 19, category: '衣類' },
      { name: 'マウス', price: 39, category: '電子機器' },
      { name: 'ジーンズ', price: 49, category: '衣類' },
      { name: 'キーボード', price: 79, category: '電子機器' }
    ];

    let html = '<strong>全商品:</strong><br>';
    products.forEach(p => html += `${p.name} - $${p.price}(${p.category})<br>`);

    let electronics = products.filter(p => p.category === '電子機器');
    html += '<br><strong>電子機器(filter):</strong><br>';
    electronics.forEach(p => html += `${p.name} - $${p.price}<br>`);

    let names = products.map(p => p.name);
    html += '<br><strong>商品名(map):</strong>' + names.join(', ') + '<br>';

    let total = products.reduce((sum, p) => sum + p.price, 0);
    html += '<br><strong>合計金額(reduce):</strong>$' + total;

    document.getElementById('output').innerHTML = html;
  </script>
</body>
</html>

findとfindIndex

何も見つからない場合、findundefinedを返し、findIndex-1を返します。

HTML
<script>
let users = [
  { name: '花子', age: 20 },
  { name: '太郎', age: 17 },
  { name: '一郎', age: 25 }
];

users.find(u => u.age > 22);       // { name: '一郎', age: 25 }
users.findIndex(u => u.age < 18);  // 1(太郎のインデックス)
</script>

sort — 要素を並べ替える

sortは配列をインプレースで整列します(元の配列を変更します!)。デフォルトでは文字列としてソートされるため、多くの初心者が引っかかります:

HTML
<script>
let nums = [10, 1, 21, 2];
nums.sort();           // [1, 10, 2, 21]  ← 文字列ソート! "10" < "2"

// 正しい:数値ソートには比較関数が必要
nums.sort((a, b) => a - b);   // [1, 2, 10, 21]  ← 昇順
nums.sort((a, b) => b - a);   // [21, 10, 2, 1]  ← 降順
</script>
⚠️ sortは元の配列を変更します!元の配列を保持するには、先にコピーしましょう:[...arr].sort(...)

実例:学生の点数ランキング

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>学生の点数ランキング</h2>
  <div id="output"></div>
  <script>
    let students = [
      { name: '花子', score: 72 },
      { name: '太郎', score: 95 },
      { name: '一郎', score: 88 },
      { name: '恵子', score: 61 },
      { name: '美咲', score: 95 }
    ];

    let html = '<strong>元の順序:</strong><br>';
    students.forEach(s => html += `${s.name} - ${s.score}点<br>`);

    let byScore = [...students].sort((a, b) => b.score - a.score);
    html += '<br><strong>点数順(降順):</strong><br>';
    byScore.forEach((s, i) => {
      let medal = i === 0 ? '🥇' : i === 1 ? '🥈' : i === 2 ? '🥉' : '';
      html += `${medal} ${s.name} - ${s.score}点<br>`;
    });

    let topStudents = students.filter(s => s.score >= 90);
    html += '<br><strong>90点以上(filter):</strong><br>';
    topStudents.forEach(s => html += `${s.name} - ${s.score}点<br>`);

    let found = students.find(s => s.name === '一郎');
    html += `<br><strong>「一郎」を検索(find):</strong>${found ? found.score + '点' : '見つかりません'}`;

    document.getElementById('output').innerHTML = html;
  </script>
</body>
</html>

someとevery

HTML
<script>
let scores = [60, 75, 82, 90];

scores.some(s => s >= 90);    // true  — 誰かが90点以上
scores.some(s => s === 100);  // false — 満点の人はいない
scores.every(s => s >= 60);   // true  — 全員合格
</script>

💪 someは「誰かが...か?」と問い、everyは「全員が...か?」と問いかけます。一方は存在をチェックし、もう一方は完全性をチェックします。

スプレッド演算子(...)

スプレッド演算子...は配列を「展開」します。コピーと結合によく使われます:

HTML
<script>
let a = [1, 2, 3];
let b = [...a];          // コピー: [1, 2, 3](新しい配列、参照ではない)
let c = [...a, 4, 5];    // 結合: [1, 2, 3, 4, 5]
let d = [0, ...a, 4];    // 挿入: [0, 1, 2, 3, 4]

let x = [1, 2], y = [3, 4];
let z = [...x, ...y];    // [1, 2, 3, 4]
</script>

分割代入

配列から値を抽出し、位置で変数に代入します:

HTML
<script>
let [a, b, c] = [1, 2, 3];
// a = 1, b = 2, c = 3

let [first, ...rest] = [1, 2, 3, 4, 5];
// first = 1, rest = [2, 3, 4, 5]

let [name, score] = ['花子', 95];
// name = '花子', score = 95
</script>

💪 分割代入はコードを簡潔にします。関数が複数の値を返す場合、配列にまとめて分割代入するのはよくあるパターンです。

メソッドチェーン

高階メソッドは新しい配列を返すため、次々にチェーンできます:

HTML
<script>
let result = students
  .filter(s => s.score >= 60)     // まず、合格した生徒をフィルター
  .map(s => s.name)               // 次に、名前を抽出
  .sort();                         // 最後に、ソート
</script>

実例:チェーンでデータを処理

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>メソッドチェーン</h2>
  <div id="output"></div>
  <script>
    let employees = [
      { name: '花子', dept: 'エンジニアリング', salary: 8000 },
      { name: '太郎', dept: '営業', salary: 5000 },
      { name: '一郎', dept: 'エンジニアリング', salary: 12000 },
      { name: '恵子', dept: '営業', salary: 6500 },
      { name: '美咲', dept: 'エンジニアリング', salary: 10000 },
      { name: '健太', dept: 'マーケティング', salary: 5500 }
    ];

    let html = '<strong>全社員:</strong><br>';
    employees.forEach(e => html += `${e.name} | ${e.dept} | $${e.salary}<br>`);

    let techHighSalary = employees
      .filter(e => e.dept === 'エンジニアリング')
      .filter(e => e.salary >= 9000)
      .map(e => `${e.name}($${e.salary})`);
    html += '<br><strong>エンジニアリング高収入(チェーンfilter+map):</strong><br>' + techHighSalary.join(', ');

    let avgSalary = employees
      .filter(e => e.dept === 'エンジニアリング')
      .map(e => e.salary)
      .reduce((sum, s) => sum + s, 0);
    let count = employees.filter(e => e.dept === 'エンジニアリング').length;
    html += `<br><br><strong>エンジニアリング平均給与:</strong>$${(avgSalary / count).toFixed(0)}`;

    let allAbove3k = employees.every(e => e.salary > 3000);
    let has12k = employees.some(e => e.salary >= 12000);
    html += `<br><br><strong>全員の給与 > $3000?</strong>${allAbove3k ? 'はい' : 'いいえ'}`;
    html += `<br><strong>誰か$12000以上?</strong>${has12k ? 'はい' : 'いいえ'}`;

    document.getElementById('output').innerHTML = html;
  </script>
</body>
</html>

実例:スプレッド演算子と分割代入

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>スプレッド演算子と分割代入</h2>
  <div id="output"></div>
  <script>
    let a = [1, 2, 3];
    let b = [...a];
    b.push(4);
    let html = `<strong>スプレッドでコピー:</strong><br>`;
    html += `元の配列a: [${a}]<br>`;
    html += `コピーb: [${b}]<br>`;
    html += `bを変更してもaには影響なし<br><br>`;

    let x = [1, 2], y = [3, 4];
    let merged = [...x, ...y, 5];
    html += `<strong>結合された配列:</strong>[${merged}]<br><br>`;

    let [first, second, ...rest] = [10, 20, 30, 40, 50];
    html += `<strong>分割代入:</strong><br>`;
    html += `first = ${first}<br>`;
    html += `second = ${second}<br>`;
    html += `rest = [${rest}]<br><br>`;

    let scores = [85, 92, 78, 95, 60];
    let [highest, ...others] = [...scores].sort((a, b) => b - a);
    html += `<strong>分割代入 + ソート:</strong><br>`;
    html += `最高点 = ${highest}<br>`;
    html += `その他 = [${others}]`;

    document.getElementById('output').innerHTML = html;
  </script>
</body>
</html>

📖 まとめ

  1. mapは各要素を新しい配列に変換し、filterは一致する要素を残し、reduceは1つの値に蓄積する
  2. findは最初の一致する要素を返し、findIndexは最初の一致するインデックスを返す
  3. sortはデフォルトでは文字列順 — 数値には常に比較関数を渡す:(a, b) => a - b
  4. someはいずれかの要素が一致するかをチェックし、everyはすべての要素が一致するかをチェックする
  5. スプレッド演算子...は配列をコピー・結合し、分割代入は要素を変数に抽出する
  6. 高階メソッドは新しい配列を返し、チェーンして簡潔で読みやすいコードが書ける

❓ よくある質問

Q mapforEachの違いは何ですか?
A mapは新しい配列を返し、forEachundefinedを返します。結果が必要な場合はmapを使い、アクションを実行するだけの場合はforEachを使います。forEachの中で手動でpushしないでください — それはmapの仕事です。
Q なぜsortは元の配列を変更するのですか?
A 歴史的な設計選択です。sortはインプレース操作で、新しい配列を返すのではなく要素を再配置します。元の配列を保持するには、先にコピーしましょう:[...arr].sort(...)
Q reduceで初期値を省略できますか?
A はい。省略すると、最初の要素が初期値となり、2番目から反復が始まります。ただし、初期値なしの空の配列はエラーをスローするため、安全のため常に指定しましょう。

📝 演習

  1. 配列[3, 8, 12, 5, 9, 17, 4]が与えられたら、filterで7より大きい数値を残し、mapで2倍にして出力してください。
  2. reduceを使って、配列['りんご', 'バナナ', 'りんご', 'さくらんぼ', 'バナナ', 'りんご']の各単語の出現回数をカウントしてください → { りんご: 3, バナナ: 2, さくらんぼ: 1 }
  3. 学生の配列(namescoreを持つ)が与えられたら、チェーンを使って:合格した生徒をフィルター → 点数で降順ソート → 名前を新しい配列に抽出してください。結果をページに表示してください。
100%