JavaScriptの配列操作(map, filter, forEachなど)

2025-07-28

はじめに

JavaScriptの配列は、データの集合を効率的に操作するための強力なメソッドを多数提供しています。

ES5(ECMAScript 5)で導入された配列メソッドは、関数型プログラミングスタイルを可能にし、コードをより宣言的に書くことができます。

配列の基本特性

JavaScriptの配列は、順序付けられた値のコレクションです。
言い換えると配列は、「整理棚の引き出し」 のようなものです。

各引き出しには番号が付いていて(0番から始まります)、中には何でも収納できます。本、文房具、お菓子など、異なる種類のものでもOK。新しい物を追加すれば引き出しが自動的に増え、物を減らせば引き出しも減ります。

例えば「クラスの名簿」も配列の一種。出席番号順に生徒が並び、先生は「3番の生徒は?」とすぐに特定できます。でも、途中で転校生が来れば順番が変わり、欠席者がいれば空き席ができます。

このように配列は、順番に整理され、自由に増減できる「スマートな収納システム」 なのです。
他のプログラミング言語と異なり、JavaScriptの配列には以下の特徴があります。

1. 動的サイズと型の柔軟性

伝統的なプログラミング言語の配列が固定サイズ・同一データ型であるのに対し、JavaScriptの配列は動的にサイズが変化し、異なるデータ型を混在させることができます。

const mixedArray = [1, '文字列', true, {name: 'オブジェクト'}, [1, 2, 3]];
console.log(mixedArray.length); // 5

2. オブジェクトベースの実装

技術的には、JavaScriptの配列は特別な種類のオブジェクトです。
数値インデックスをプロパティ名として使用し、lengthプロパティを持ちます。

const arr = ['a', 'b', 'c'];
console.log(typeof arr); // "object"
console.log(arr['0']); // "a" - 配列はオブジェクトとしてアクセス可能

配列の内部構造

メモリ管理

JavaScriptエンジン(V8など)は、配列の内容に基づいて最適なメモリ表現を選択します。

  • パックド配列: 連続したメモリ領域に密集して格納
  • ホールリー配列: スパース(疎)な配列でメモリ効率が異なる

インデックスとlengthプロパティ

配列のインデックスは0から始まり、lengthプロパティは常に最も大きな数値インデックスより1大きい値になります。

const arr = [];
arr[100] = 'value';
console.log(arr.length); // 101
console.log(arr[50]); // undefined

配列の種類と特性

密配列(Dense Arrays)

ほとんどの場合、私たちが扱うのは密配列です。

const dense = [1, 2, 3, 4, 5]; // すべての要素が定義済み

疎配列(Sparse Arrays)

未定義の要素を含む配列。

const sparse = [1, , 3]; // インデックス1が空
console.log(sparse.length); // 3
console.log(1 in sparse); // false

主要な配列メソッド

1. forEach – 各要素に対する処理

forEachは配列の全要素に順番にアクセスし、指定した関数を実行します。戻り値はなく、配列を変更しません。主に繰り返し処理や画面表示、ログ出力などに使います。breakreturnで途中終了はできません。

const numbers = [1, 2, 3];

// 従来のforループ
for (let i = 0; i < numbers.length; i++) {
  console.log(numbers[i]);
}

// forEachを使用
numbers.forEach(
  function(number) {
    console.log(number);
  }
);

特徴

  • 戻り値はundefined
  • ループを途中で止められない(breakが使えない)
  • 配列の要素を変更可能(元の配列を変更する)

2. map - 配列の変換

mapは配列の各要素に関数を適用し、その結果を新しい配列として返します。元の配列は変更されません。例えば数値配列の各要素を2倍にしたり、オブジェクト配列から特定のプロパティだけを取り出すときに使います。

const doubled = numbers.map(number => number * 2);
console.log(doubled); // [2, 4, 6]

特徴

  • 新しい配列を返す
  • 元の配列は変更されない
  • 各要素を1対1で変換

3. filter - 条件に合う要素の抽出

filterは配列から条件を満たす要素だけを抜き出し、新しい配列として返します。元の配列は変更されません。例えば偶数だけを抽出する、特定の文字列を含む要素だけを取得するなどに便利です。

const evens = numbers.filter(number => number % 2 === 0);
console.log(evens); // [2]

特徴

  • 新しい配列を返す
  • コールバック関数がtrueを返した要素のみ含まれる
  • 元の配列は変更されない

4. reduce - 配列の畳み込み

reduceは配列の全要素を順に処理し、1つの値にまとめます。合計値、平均値、オブジェクトの集計などに使われます。初期値を設定でき、要素ごとに計算結果を次の要素処理に引き継ぐことができます。

const sum = numbers.reduce((accumulator, current) => accumulator + current, 0);
console.log(sum); // 6

特徴

  • 配列を単一の値に集約
  • 初期値を指定可能(省略すると最初の要素が初期値になる)
  • 多彩な用途(集計、変換、フラット化など)

5. find / findIndex - 要素の検索

findは条件に合う最初の要素を返し、findIndexはそのインデックス(位置)を返します。条件に合うものがない場合、findundefinedfindIndex-1を返します。

const users = [
  { id: 1, name: '太郎' },
  { id: 2, name: '花子' }
];

const user = users.find(user => user.id === 2);
console.log(user); // { id: 2, name: '花子' }

const index = users.findIndex(user => user.name === '太郎');
console.log(index); // 0

6. some / every - 条件チェック

someは配列の中で1つでも条件を満たせばtrueを返します。everyは全ての要素が条件を満たすときのみtrueを返します。部分一致か全一致かで使い分けます。

const hasEven = numbers.some(n => n % 2 === 0); // true
const allEven = numbers.every(n => n % 2 === 0); // false

特徴

  • some(): 条件を満たす要素が見つかった時点で処理を終了
  • every(): 条件を満たさない要素が見つかった時点で処理を終了
  • 大きな配列でも効率的に処理できる

7. sort - 配列のソート

sortは配列を並び替えます。デフォルトでは文字列として比較するため、数値ソートでは比較関数を指定します。元の配列が変更される点に注意が必要です。

const unsorted = [3, 1, 4, 2];
const sorted = unsorted.sort((a, b) => a - b); // [1, 2, 3, 4]

注意点

  • 元の配列を変更する
  • デフォルトは文字列としてのソート(数値ソートには比較関数が必要)

メソッドチェーン

配列メソッドはチェーンして使用できます。メソッドチェーン(method chaining)とは、複数のメソッドを「ドット(.)」でつなげて連続して呼び出す書き方です。処理を一行でスッキリ書けるのが特徴です。

const result = numbers
  .filter(n => n % 2 === 0) // 偶数だけ抽出
  .map(n => n * 3) // 3倍する
  .reduce((sum, n) => sum + n, 0); // 合計を計算

console.log(result); // 6 (2 * 3 = 6)

ES6+の新しい配列機能

スプレッド演算子

配列やオブジェクトの中身を展開する記号。

const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]

Array.from()

配列のようなもの(Array-like)やイテラブル(例:文字列、Set)を、本物の配列に変換するメソッド。

const arrayLike = { 0: 'a', 1: 'b', length: 2 };
const realArray = Array.from(arrayLike); // ['a', 'b']

分割代入

配列やオブジェクトの中の値を、変数にまとめて取り出す書き方。

const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(rest); // [3, 4, 5]

パフォーマンスの考慮

  • 大きな配列forループの方が高速な場合あり
  • 不変性map/filter/sliceは新しい配列を作成(メモリ使用量に注意)
  • 早期終了some/everyは条件を満たせば途中で終了

JavaScriptの配列メソッド一覧表

基本メソッド

メソッド名説明戻り値元の配列変更使用例
push()末尾に要素を追加新しい配列の長さ✅ 変更するarr.push(1, 2)
pop()末尾の要素を削除削除した要素✅ 変更するarr.pop()
unshift()先頭に要素を追加新しい配列の長さ✅ 変更するarr.unshift(0)
shift()先頭の要素を削除削除した要素✅ 変更するarr.shift()
length配列の長さを取得数値❌ 変更しないarr.length

検索・判定メソッド

メソッド名説明戻り値元の配列変更使用例
indexOf()要素の最初のインデックスインデックス(見つからない場合は-1)❌ 変更しないarr.indexOf('a')
lastIndexOf()要素の最後のインデックスインデックス(見つからない場合は-1)❌ 変更しないarr.lastIndexOf('a')
includes()要素の存在確認boolean❌ 変更しないarr.includes('a')
find()条件を満たす最初の要素要素(見つからない場合はundefined)❌ 変更しないarr.find(x => x > 5)
findIndex()条件を満たす最初のインデックスインデックス(見つからない場合は-1)❌ 変更しないarr.findIndex(x => x > 5)
some()条件を満たす要素が1つでもあるかboolean❌ 変更しないarr.some(x => x > 5)
every()すべての要素が条件を満たすかboolean❌ 変更しないarr.every(x => x > 0)

変換メソッド

メソッド名説明戻り値元の配列変更使用例
map()各要素を変換した新しい配列新しい配列❌ 変更しないarr.map(x => x * 2)
filter()条件を満たす要素の新しい配列新しい配列❌ 変更しないarr.filter(x => x > 5)
reduce()配列を単一値に集約集約値❌ 変更しないarr.reduce((a, b) => a + b)
reduceRight()右から左に集約集約値❌ 変更しないarr.reduceRight((a, b) => a + b)
flat()多次元配列を平坦化新しい配列❌ 変更しないarr.flat(2)
flatMap()map後にflatを実行新しい配列❌ 変更しないarr.flatMap(x => [x, x*2])

反復メソッド

メソッド名説明戻り値元の配列変更使用例
forEach()各要素に対して処理実行undefined❌ 変更しないarr.forEach(x => console.log(x))
entries()キー/値のイテレーターIterator❌ 変更しないarr.entries()
keys()キーのイテレーターIterator❌ 変更しないarr.keys()
values()値のイテレーターIterator❌ 変更しないarr.values()

配列操作メソッド

メソッド名説明戻り値元の配列変更使用例
concat()配列を結合新しい配列❌ 変更しないarr1.concat(arr2)
slice()部分配列を抽出新しい配列❌ 変更しないarr.slice(1, 3)
splice()要素の追加/削除削除された要素の配列✅ 変更するarr.splice(1, 2, 'a')
join()配列を文字列に結合文字列❌ 変更しないarr.join(', ')
reverse()配列の順序を反転反転した配列✅ 変更するarr.reverse()
sort()配列をソートソートした配列✅ 変更するarr.sort((a, b) => a - b)

まとめ

JavaScriptの代表的な配列メソッドを学ぶことで、コードを短く効率的に書けるようになります。forEachやmapなどを使えば、従来のfor文より読みやすく、意図が明確な処理が可能です。filterやfindで条件抽出、reduceで集計や加工、sortで並び替えなど、実用的な操作が簡単に行えます。これらを活用することで、保守性や再利用性が高まり、開発スピードも向上します。

演習問題

初級問題(3問)

  1. 次の配列の各要素を2倍した新しい配列を作成しなさい(mapを使用)。
   const numbers = [1, 2, 3, 4, 5];
  1. 次の配列から文字列の要素だけを抽出しなさい(filterを使用)。
   const mixed = [1, 'apple', 2, 'banana', true, 'orange'];
  1. 次のコードの出力結果は?
   const arr = [10, 20, 30];
   arr.forEach(function(num) {
      num = num * 2;
   });
   console.log(arr);

中級問題(6問)

  1. 次の配列から偶数のみを抽出し、それぞれを3倍して、結果の合計を求めなさい。
   const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  1. 次のオブジェクトの配列から、ageが30以上の人のnameだけを含む配列を作成しなさい。
   const people = [
     { name: '太郎', age: 25 },
     { name: '花子', age: 30 },
     { name: '次郎', age: 35 }
   ];
  1. 次の2つの配列を結合し、重複を除去した新しい配列を作成しなさい。
   const arr1 = [1, 2, 3];
   const arr2 = [2, 3, 4, 5];
  1. 次の配列を文字列の長さでソートしなさい。
   const fruits = ['apple', 'banana', 'cherry', 'date'];
  1. 次の配列の要素の平均値を求めなさい(reduceを使用)。
   const scores = [85, 90, 78, 92, 88];
  1. 次のコードの出力結果とその理由を説明しなさい。
   const arr = [1, 2, 3];
   const result = arr.map((num, index) => num * index);
   console.log(result);

上級問題(3問)

  1. 次のような動作をするgroupBy関数を実装しなさい。
const people = [ 
    { name: '太郎', age: 25 }, 
    { name: '花子', age: 30 }, 
    { name: '次郎', age: 25 } 
]; 
const grouped = groupBy(people, 'age'); /* 
結果: { '25':
         [ 
             { name: '太郎', age: 25 },
             { name: '次郎', age: 25 } 
         ],
       '30': [
             { name: '花子', age: 30 }
             ]
     } */
  1. 配列のフラット化処理を行うflatten関数を再帰的に実装しなさい(任意の深さに対応)。
const nested = [
    1, [
        2, [
            3, [
                4
               ]
            ],
        5]
    ]; 
const flat = flatten(nested); console.log(flat); // [1, 2, 3, 4, 5]
  1. 次のような動作をするchunk関数を実装しなさい。
javascript const array = [1, 2, 3, 4, 5, 6, 7]; 
const chunked = chunk(array, 3); console.log(chunked); // [[1, 2, 3], [4, 5, 6], [7]]

解答例

初級問題解答

  1. mapを使用
   const doubled = numbers.map(n => n * 2);
  1. filterを使用
   const strings = mixed.filter(item => typeof item === 'string');
  1. 出力結果
[10, 20, 30]
    const arr = [10, 20, 30];
    arr.forEach(num => {
        num = num * 2;
    });
    console.log(arr);   // [10, 20, 30]と表示される。
                        // forEach内のnumは新しい変数であり、元の配列arrには影響しない。

中級問題解答

  1. チェーンして処理
   const result = numbers
     .filter(n => n % 2 === 0)
     .map(n => n * 3)
     .reduce((sum, n) => sum + n, 0);
  1. フィルタリングとマッピング
   const names = people
     .filter(person => person.age >= 30)
     .map(person => person.name);
  1. 結合と重複除去
   const arr1 = [1, 2, 3];
   const arr2 = [2, 3, 4, 5];
   const combined = [...new Set([...arr1, ...arr2])];
  1. 文字列長でソート
   const fruits = ['apple', 'banana', 'cherry', 'date'];
   fruits.sort((a, b) => a.length - b.length);
  1. 平均値の計算
   const scores = [85, 90, 78, 92, 88];
   const average = scores.reduce((sum, score) => sum + score, 0) / scores.length;
  1. 出力結果と理由:
   [0, 2, 6]
    const arr = [1, 2, 3];
    const result = arr.map((num, index) => num * index);
    console.log(result);    // [0, 2, 6]が出力されます。
                            // mapメソッドは配列の各要素に対して関数を適用し、新しい配列を生成します。

上級問題解答

  1. groupBy関数

ステップ1: 空のオブジェクトを準備する
結果を格納するための空のオブジェクトを作成します。ここに年齢をキー、該当する人物の配列を値として保存していきます。

ステップ2: 配列の各要素を順番に処理する
配列の最初の要素から最後の要素まで、1つずつ処理を行います。各人物オブジェクトに対して以下の操作を繰り返します。

ステップ3: グループ化のキーを取得する
現在処理中の人物オブジェクトから、指定されたプロパティ(この場合は'age')の値を取得します。

ステップ4: キーが存在するかチェックする
結果オブジェクトにそのキー(年齢)が既に存在するか確認します。存在しない場合は、新しい配列を作成してそのキーに割り当てます。

ステップ5: 現在の要素を対応する配列に追加する
該当するキーの配列に、現在処理中の人物オブジェクトを追加します。

ステップ6: すべての要素を処理するまで繰り返す
配列のすべての要素に対してステップ2〜5を繰り返します。最後の要素まで処理が終わったら完了です。

ステップ7: 完成したオブジェクトを返す
グループ化が完了したオブジェクトを関数の結果として返します。

function groupBy(array, key) { 
    return array.reduce((acc, obj) => { 
        const groupKey = obj[key]; 
        if (!acc[groupKey]) { acc[groupKey] = []; } 
        acc[groupKey].push(obj); 
        return acc; 
    }, {}); 
}
  1. flatten関数

ステップ1: 結果用の空配列を準備する
フラット化した要素を格納するための空の配列を作成します。

ステップ2: 入力配列の各要素を順番に処理する
配列の最初から最後まで、各要素に対して以下のチェックを行います。

ステップ3: 要素が配列かどうかを判定する
現在の要素が配列であるかどうかを確認します。
配列でない場合はステップ4へ
配列の場合はステップ5へ

ステップ4: 通常の要素の場合
その要素を結果用の配列に直接追加します。

ステップ5: 配列要素の場合(再帰処理)

その配列に対して同じflatten関数を再度呼び出します(再帰)。これにより、任意の深さのネストに対応できます。

ステップ6: 再帰結果を結合する
再帰的にフラット化した結果の配列を取得します。その結果を現在の結果用配列に結合(追加)します。

ステップ7: すべての要素を処理するまで繰り返す
入力配列のすべての要素に対してステップ3〜6を繰り返します。

ステップ8: 完成した配列を返す
すべてのネストが解消され、フラット化された配列を返します。

function flatten(arr) { 
    return arr.reduce((flat, toFlatten) => { 
        return flat.concat( Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten ); 
    }, []); 
}
  1. chunk関数

ステップ1: 空の結果配列を準備する
チャンク(小さな配列のグループ)を格納するための空の配列を作成します。

ステップ2: 元の配列の先頭から順に処理を開始する
配列のインデックス0から処理を開始します。現在の位置を追跡するための変数を用意します。

ステップ3: 元の配列の終わりまで処理を繰り返す
現在の位置が配列の長さより小さい間、処理を継続します。

ステップ4: 現在位置からチャンクを切り取る
現在の位置を開始点として、指定されたサイズ分の要素を切り取ります。
例:サイズ3の場合、[1,2,3] → [4,5,6] → [7] のように切り取ります。

ステップ5: 切り取ったチャンクを結果に追加する
切り取った小さな配列(チャンク)を結果用の配列に追加します。

ステップ6: 処理位置を更新する
現在の位置をチャンクサイズ分だけ進めます。
例:現在位置0からサイズ3分進めて、次の開始位置を3にします。

ステップ7: 配列の終端に達するまで繰り返す
ステップ3〜6を配列の最後の要素まで繰り返します。

ステップ8: チャンク化された配列を返す
すべての要素が指定されたサイズのチャンクに分割された配列を返します。

javascript function chunk(array, size) { 
    return array.reduce((acc, _, i) => { if (i % size === 0) { acc.push(array.slice(i, i + size)); } 
    return acc; }, []); 
}