JavaScript 入門

JavaScript map、filter、reduce

本セクションでは、JavaScriptにおいて最も頻繁に使用され、かつ非常に強力な3つの高階関数(Higher-Order Functions)、mapfilterreduceについて深く掘り下げます。

これらの関数を理解しマスターすることは、JavaScriptにおける配列(Array)やその他のデータ構造の操作能力を飛躍的に向上させることにつながります。

1. map、filter、reduceとは?

mapfilterreduceは、JavaScriptに組み込まれた配列メソッドであり、同時に高階関数でもあります。

これは、これらが別の関数を引数(Parameter)として受け取ることを意味し、この引数は一般にコールバック関数(Callback Function)と呼ばれます。

これらのメソッドは配列をイテレート(走査)し、各要素(またはfilterの場合は一部の要素)に対してコールバック関数を適用します。操作の結果として、コールバック関数内で定義されたロジックに基づき、新しい配列を生成(あるいはreduceの場合は単一の値を生成)します。これらのメソッドは、従来の forループ を使用することなく、簡潔かつ宣言的(Declarative)な方法で配列を操作する手段を提供します。

2. map() メソッド

map() メソッドは、元の配列の各要素に対して提供された関数を適用し、その結果からなる新しい配列を作成します。配列の各要素を同じルールで変換(Transform)したい場合に非常に有用です。元の配列は変更されません。

2.1 文法

const newArray = array.map(callbackFunction);
  • array: 変換したい元の配列。
  • callbackFunction: 配列の各要素に対して呼び出される関数。最大3つの引数を受け取ります:
    • element: 配列内で現在処理されている要素。
    • index (任意): 現在処理されている要素のインデックス。
    • array (任意): mapを呼び出した元の配列。
  • newArray: 各要素がコールバック関数の結果である新しい配列。

2.2 例1:数値の平方(2乗)計算

数値の配列があり、各数字の平方を含む新しい配列を作成したいとします。

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

// mapを使用して、各数字の平方を含む新しい配列を作成
const squaredNumbers = numbers.map(function(number) {
  return number * number;
});

console.log(squaredNumbers); // 出力: [1, 4, 9, 16, 25]
console.log(numbers); // 出力: [1, 2, 3, 4, 5] (元の配列は変更されない)

この例では、コールバック関数 function(number) { return number * number; }numbers 配列の各要素に適用されます。map() メソッドは、その結果を格納した新しい配列 squaredNumbers を構築します。

2.3 例2:アロー関数を用いた map()

アロー関数(第6モジュール参照)を使用すると、コールバック関数をより簡潔に記述できます。

const numbers = [1, 2, 3, 4, 5];
const squaredNumbers = numbers.map(number => number * number); // アロー関数でより簡潔に

console.log(squaredNumbers); // 出力: [1, 4, 9, 16, 25]

2.4 例3:配列内のオブジェクト変換

map() はオブジェクトの配列を変換するのにも役立ちます。製品オブジェクトの配列から、製品名だけを抽出した新しい配列を作成してみましょう。

const products = [
  { id: 1, name: 'ノートPC', price: 1200 },
  { id: 2, name: 'キーボード', price: 75 },
  { id: 3, name: 'マウス', price: 25 }
];

const productNames = products.map(product => product.name);
console.log(productNames); // 出力: ['ノートPC', 'キーボード', 'マウス']

2.5 例4:map内でのインデックスの利用

必要に応じて、コールバック関数内でインデックスにアクセスできます。例えば、各要素にインデックスの値を加算する場合:

const numbers = [10, 20, 30];
const indexedNumbers = numbers.map((number, index) => number + index);
console.log(indexedNumbers); // 出力: [10, 21, 32]

3. filter() メソッド

filter() メソッドは、提供された関数によるテストに合格したすべての要素を含む新しい配列を作成します。特定の条件に基づいて配列から一部の要素を選択したい場合に非常に有用です。元の配列は変更されません。

3.1 文法

const newArray = array.filter(callbackFunction);
  • array: フィルタリングしたい元の配列。
  • callbackFunction: 配列の各要素に対して呼び出される関数。要素を新しい配列に含めるべき場合は true を、そうでない場合は false を返します。最大3つの引数を受け取ります:
    • element: 配列内で現在処理されている要素。
    • index (任意): 現在処理されている要素のインデックス。
    • array (任意): filterを呼び出した元の配列。
  • newArray: 元の配列の中でテスト(コールバック関数が true を返す)に合格した要素のみを含む新しい配列。

3.2 例1:偶数のフィルタリング

数値の配列から、偶数だけを含む新しい配列を作成します。

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

// filterを使用して偶数のみを含む新しい配列を作成
const evenNumbers = numbers.filter(function(number) {
  return number % 2 === 0; // 数字が偶数なら true を返す
});

console.log(evenNumbers); // 出力: [2, 4, 6]
console.log(numbers); // 出力: [1, 2, 3, 4, 5, 6] (元の配列は変更されない)

この例では、コールバック関数が各要素に適用され、関数が true を返した要素のみが evenNumbers 配列に格納されます。

3.3 例2:アロー関数を用いた filter()

アロー関数によるコンパクトな構文です。

const numbers = [1, 2, 3, 4, 5, 6];
const evenNumbers = numbers.filter(number => number % 2 === 0); // より簡潔な記述

console.log(evenNumbers); // 出力: [2, 4, 6]

3.4 例3:プロパティに基づくオブジェクトのフィルタリング

製品オブジェクトの配列から、価格が100ドルを超える製品を抽出します。

const products = [
  { id: 1, name: 'ノートPC', price: 1200 },
  { id: 2, name: 'キーボード', price: 75 },
  { id: 3, name: 'マウス', price: 25 },
  { id: 4, name: 'モニター', price: 300 }
];

const expensiveProducts = products.filter(product => product.price > 100);
console.log(expensiveProducts);
/*
出力:
[
  { id: 1, name: 'ノートPC', price: 1200 },
  { id: 4, name: 'モニター', price: 300 }
]
*/

3.5 例4:長さに基づく文字列のフィルタリング

const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const longWords = words.filter(word => word.length > 6);
console.log(longWords); // 出力: ["exuberant", "destruction", "present"]

4. reduce() メソッド

reduce() メソッドは、配列の各要素に対して(開発者が提供した)リデューサー(Reducer) 関数を実行し、単一の出力値を生成します。配列のすべての要素を組み合わせて一つの値(合計、平均、文字列の結合など)にしたい場合に非常に有用です。mapfilter とは異なり、必ずしも配列を返すわけではなく、累積された値を返します。元の配列は変更されません。

4.1 文法

const result = array.reduce(callbackFunction, initialValue);
  • array: 帰約(Reduce)したい元の配列。
  • callbackFunction: 配列の各要素に対して呼び出される関数。最大4つの引数を受け取ります:
    • accumulator (累算器): 前回のコールバック呼び出しで返された累積値、または initialValue(提供されている場合)。
    • currentValue (現在値): 配列内で現在処理されている要素。
    • currentIndex (任意): 現在処理されている要素のインデックス。
    • array (任意): reduceを呼び出した元の配列。
  • initialValue (任意): 最初のコールバック関数呼び出し時の最初の引数(accumulator)として使用される値。initialValue が提供されない場合、配列の最初の要素が初期の accumulator として使用され、currentValue としてはスキップされます。空の配列に対して initialValue なしで reduce() を呼び出すと TypeError がスローされます。
  • result: 帰約操作の結果として生成される単一の値。

4.2 例1:数値の合計

数値の配列の合計値を計算します。

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

// reduceを使用して数字の合計を計算
const sum = numbers.reduce(function(accumulator, currentValue) {
  return accumulator + currentValue;
}, 0); // 0 は累算器の初期値 (initialValue)

console.log(sum); // 出力: 15
console.log(numbers); // 出力: [1, 2, 3, 4, 5] (元の配列は変更されない)

この例では、accumulatorinitialValue0 から始まります。各イテレーションで currentValueaccumulator に加算され、その結果が次のイテレーションの新しい accumulator となります。

4.3 例2:アロー関数を用いた reduce()

const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);

console.log(sum); // 出力: 15

4.4 例3:製品の総額計算

製品オブジェクトの配列から、すべての製品の合計価格を計算します。

const products = [
  { id: 1, name: 'ノートPC', price: 1200 },
  { id: 2, name: 'キーボード', price: 75 },
  { id: 3, name: 'マウス', price: 25 }
];

const totalPrice = products.reduce((accumulator, product) => accumulator + product.price, 0);
console.log(totalPrice); // 出力: 1300

4.5 例4:文字列の結合

reduce は数値計算以外にも利用できます。例えば、文字列配列の結合:

const words = ['Hello', ' ', 'World', '!'];
const sentence = words.reduce((accumulator, word) => accumulator + word, '');
console.log(sentence); // 出力: Hello World!

4.6 例5:多次元配列のフラット化(平坦化)

const nestedArray = [[1, 2], [3, 4], [5, 6]];
const flatArray = nestedArray.reduce((accumulator, subArray) => accumulator.concat(subArray), []);
console.log(flatArray); // 出力: [1, 2, 3, 4, 5, 6]