TypeScriptのreduceメソッドを完璧に使いこなす10のステップ – Japanシーモア

TypeScriptのreduceメソッドを完璧に使いこなす10のステップ

TypeScriptのreduceメソッドのロゴと10のステップ文字TypeScript
この記事は約20分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

このサービスは複数のSSPによる協力の下、運営されています。

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

TypeScriptを学ぶ上で、配列を効果的に操作するためのメソッドとしてreduceメソッドは外せない存在です。

この記事では、TypeScriptのreduceメソッドを10のステップで徹底的に解説していきます。

基本的な使い方から、応用例、注意点、カスタマイズ方法まで、初心者から上級者までのTypeScriptユーザーが完璧に理解できるように細かく解説していきます。

●TypeScriptとは

TypeScriptは、JavaScriptのスーパーセットとしてMicrosoftによって開発された静的型付け言語です。

TypeScriptは、JavaScriptに型システムを導入することで、大規模なアプリケーションの開発や、バグを未然に防ぐことができるように設計されています。

TypeScriptはコンパイルすると、普通のJavaScriptにトランスパイルされるため、どのJavaScript実行環境でも動作します。

○なぜTypeScriptを学ぶべきか

JavaScriptは動的型付け言語であり、変数の型が実行時に決まるため、予期せぬ型のエラーが発生することがあります。

しかし、TypeScriptを使用すると、コードがコンパイルされる段階で型のエラーを検出することができます。

これにより、実行時のエラーを大幅に減少させることができるのです。

さらに、TypeScriptにはインターフェースやジェネリクスといった、JavaScriptにはない強力な機能も備えています。

これらの機能を利用することで、より堅牢で保守性の高いコードを書くことができます。

●reduceメソッドの基本

reduceメソッドは、配列の要素を左から右に処理して、単一の結果を生成するためのJavaScriptの配列メソッドです。

TypeScriptにおいても同様に利用でき、型の情報を活用することでより安全にreduceを使用することが可能となります。

○reduceメソッドの定義と役割

reduceメソッドは、配列の各要素に対して累積的な操作を行い、その結果を返します。

具体的には、reduceメソッドは次のように定義されています。

array.reduce(callback( accumulator, currentValue[, index[, array]] )[, initialValue])

このコードでは、callbackは配列の各要素に対して実行される関数を表します。

この関数は、4つの引数を取ります。

  • accumulator:累積の結果。初回の呼び出し時にはinitialValueまたは配列の最初の要素が渡されます。
  • currentValue:現在処理している配列の要素。
  • index (オプション):現在処理している要素のインデックス。initialValueが提供された場合は0から、そうでない場合は1から開始します。
  • array (オプション):reduceを呼び出した配列。

また、initialValueはオプションで、累積の初期値として使用されます。

この値が提供されない場合、配列の最初の要素が累積の初期値として使用されます。

このコードを実行すると、配列の各要素に対してcallback関数が実行され、その結果が累積されて最終的な結果として返されます。

●reduceメソッドの使い方

TypeScriptでの配列操作には様々なメソッドが提供されていますが、その中でも非常に強力かつ汎用性が高いのがreduceメソッドです。

このメソッドを使うことで、配列の各要素を一つずつ取り出し、何らかの操作を行った結果を一つの値にまとめることができます。

しかし、reduceメソッドはその特性上、初めて触れる方には少し難しく感じるかもしれません。

そのため、ここでは、reduceメソッドの基本的な使い方から、サンプルコードを交えながら徹底的に解説していきます。

○サンプルコード1:数字の配列を合計する基本的な使い方

配列の各要素の合計を求めるための一番シンプルな使い方をまず見ていきましょう。

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

const sum = numbers.reduce((accumulator, currentValue) => {
    // こちらのコードでは、accumulatorが累積の値として使われており、
    // currentValueが現在の配列の要素として取り出される値です。
    return accumulator + currentValue;
}, 0);  // 初期値として0を設定しています。

このコードでは、numbersという名前の数字の配列が用意されています。

そして、その配列のreduceメソッドを使用して、累積値と現在の値を加算していき、全ての要素を合計しています。

特に注意したいのは、reduceメソッドの第2引数として指定されている0です。

これは累積値の初期値として使用されるもので、この例では配列の要素を合計していくため、0からスタートするために指定しています。

このコードを実行すると、1から5までの合計である15がsum変数に代入されます。

つまり、結果としてsum変数は15となります。

○サンプルコード2:オブジェクトの配列を特定のプロパティで集計

JavaScriptやTypeScriptにおいて、データの集計や変換は非常に一般的な作業となっています。

その中でも、オブジェクトの配列を特定のプロパティに基づいて集計する作業は頻繁に行われるため、その方法を理解し、実際に手を動かして試すことは大変重要です。

ここでは、reduceメソッドを使って、オブジェクトの配列を特定のプロパティで集計する方法について徹底的に解説します。

まず、次のような商品の情報を持ったオブジェクトの配列を考えてみましょう。

// 商品情報のオブジェクト配列
const products = [
    { id: 1, category: 'フルーツ', price: 120 },
    { id: 2, category: 'フルーツ', price: 150 },
    { id: 3, category: '野菜', price: 80 },
    { id: 4, category: 'フルーツ', price: 90 },
    { id: 5, category: '野菜', price: 100 },
];

この配列から、各カテゴリーごとの商品の合計価格を求めたいとします。

この時に役立つのが、reduceメソッドです。

具体的なコードは次のようになります。

const totalByCategory = products.reduce((acc, product) => {
    // カテゴリがすでに集計結果オブジェクトに存在するかを確認
    if (!acc[product.category]) {
        acc[product.category] = 0; // 存在しない場合は初期化
    }
    acc[product.category] += product.price; // 価格を加算
    return acc; // 集計結果オブジェクトを返す
}, {});

console.log(totalByCategory);

このコードでは、まず初期の集計結果オブジェクトを空のオブジェクト{}として設定しています。

reduceメソッドのコールバック関数内で、各商品オブジェクトを取り出し、そのカテゴリが集計結果オブジェクトに存在するかどうかを確認します。

存在しない場合、そのカテゴリの価格の初期値を0として設定し、存在する場合は既存の価格に加算しています。

このコードを実行すると、totalByCategoryオブジェクトには、各カテゴリごとの商品の合計価格が格納されます。

具体的には、{ フルーツ: 360, 野菜: 180 }という結果が得られます。

これにより、任意のカテゴリの商品の合計価格を簡単に知ることができるようになりました。

○サンプルコード3:ネストされた配列をフラットにする

配列の中に配列が入っているような、ネストされた配列は、実際の開発シーンでよく見かけるものです。

このようなネストされた配列を、1つのフラットな配列に変換したい場面も多いでしょう。

TypeScriptのreduceメソッドを使用すると、このような変換も簡単に行うことができます。

具体的には次のようなネストされた配列があります。

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

この配列を、次のような1つのフラットな配列に変換したいと考えます。

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

それでは、reduceメソッドを使用してこの変換を行うサンプルコードを見ていきましょう。

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

const flatArray = nestedArray.reduce((accumulator, currentValue) => {
    // このコードではconcatメソッドを使って、累積値に現在の配列の要素を結合しています。
    return accumulator.concat(currentValue);
}, []);

console.log(flatArray);  // [1, 2, 3, 4, 5, 6]

このコードではreduceメソッドを使って、ネストされた配列をフラットにする変換を行っています。

具体的には、累積値(accumulator)として初期値として空の配列を設定し、現在の値(currentValue)としてネストされた配列の各要素が取得されます。

そして、concatメソッドを使用して、累積値に現在の配列の要素を結合していきます。

この操作を全てのネストされた配列の要素に対して行い、最終的に1つのフラットな配列を取得します。

このコードを実行すると、確かにネストされた配列がフラットになった結果、[1, 2, 3, 4, 5, 6]という配列が得られます。

●reduceの応用例

reduceメソッドは、配列の要素を左から右へと順に一つずつ取り出して、前回の戻り値とともに関数を適用し、最終的に1つの値を返す機能を持っています。

その特性を活かせば、多岐にわたる応用例を実現することが可能です。

○サンプルコード4:カスタム累積関数を作成

JavaScriptやTypeScriptでのプログラミングにおいて、特定の処理を何度も繰り返す場面があるかと思います。

このような場面では、reduceメソッドを使って独自の累積関数を作成することが有効です。

カスタム累積関数を利用したサンプルコードを紹介します。

type User = {
  id: number;
  score: number;
};

const users: User[] = [
  { id: 1, score: 80 },
  { id: 2, score: 90 },
  { id: 3, score: 85 },
];

// スコアの合計を計算するカスタム累積関数
const totalScore = users.reduce((acc, user) => acc + user.score, 0);

このコードでは、Userという型を定義しています。

それをもとに、usersというUserの配列を定義しています。

そして、reduceメソッドを使って、users配列内の全ユーザーのスコアを合計しています。

コードを実行すると、totalScoreには255という値が格納されます。

これは、80 + 90 + 85 = 255という計算結果です。

このように、reduceメソッドを利用すれば、配列内のデータを柔軟に処理し、様々な形式での出力が可能となります。

○サンプルコード5:配列内のオブジェクトを特定の条件でグループ化

TypeScriptを使用して、配列内のオブジェクトを特定の条件でグループ化する方法について、reduceメソッドを利用して解説します。

特定の条件、例えばオブジェクトの特定のプロパティ値に基づいて、オブジェクトをグループ化する場面はよくあります。

このような時、reduceメソッドをうまく使えば、シンプルで読みやすいコードを実現することができます。

下記のサンプルコードでは、typeプロパティの値に基づいて、オブジェクトをグループ化しています。

// TypeScriptの配列内のオブジェクトを特定の条件でグループ化するサンプルコード
type Item = {
    id: number;
    name: string;
    type: string;
};

const items: Item[] = [
    { id: 1, name: 'A', type: 'fruit' },
    { id: 2, name: 'B', type: 'vegetable' },
    { id: 3, name: 'C', type: 'fruit' },
    { id: 4, name: 'D', type: 'vegetable' }
];

const groupedItems = items.reduce<{ [key: string]: Item[] }>((acc, item) => {
    if (!acc[item.type]) {
        acc[item.type] = [];
    }
    acc[item.type].push(item);
    return acc;
}, {});

console.log(groupedItems);

このコードでは、reduceメソッドを使って、items配列のオブジェクトをtypeプロパティの値に基づいてグループ化しています。

初期値として空のオブジェクトをセットし、typeプロパティの値をキーとして使用しています。

該当のキーが存在しない場合には、新しい配列を作成して、該当のオブジェクトをその配列に追加します。

このコードを実行すると、次のようなオブジェクトが得られます。

{
    fruit: [
        { id: 1, name: 'A', type: 'fruit' },
        { id: 3, name: 'C', type: 'fruit' }
    ],
    vegetable: [
        { id: 2, name: 'B', type: 'vegetable' },
        { id: 4, name: 'D', type: 'vegetable' }
    ]
}

この結果から、fruitvegetableの2つのキーにそれぞれ該当するオブジェクトの配列が格納されていることが分かります。

○サンプルコード6:複数のメソッドを連携してデータ変換

reduceメソッドは、単体での使用も非常に強力ですが、他の配列メソッドと組み合わせることでさらなるパワフルなデータ変換を実現することができます。

ここでは、mapメソッドやfilterメソッドと連携させて、特定の条件を持つデータの抽出や変換を行いつつ、その結果をreduceメソッドで集約する例をご紹介します。

まず、次のような商品の配列を考えます。

const products = [
    { id: 1, category: '食品', price: 120 },
    { id: 2, category: '雑貨', price: 80 },
    { id: 3, category: '食品', price: 200 },
    { id: 4, category: '書籍', price: 1500 },
    { id: 5, category: '雑貨', price: 300 }
];

このデータから、カテゴリが”食品”の商品のみを抽出し、その合計価格を求めたい場合を考えます。

このコードでは、まずfilterメソッドを使ってカテゴリが”食品”の商品のみを抽出します。

次に、reduceメソッドを使用してその合計価格を計算しています。

const totalFoodPrice = products
    .filter(product => product.category === '食品')
    .reduce((acc, product) => acc + product.price, 0);

console.log(totalFoodPrice);  // 出力結果を表示

このコードを実行すると、食品カテゴリの商品の合計価格が計算されます。

具体的には、120 + 200 = 320となり、320が表示される結果となります。

また、商品の価格が200円以上の商品のみを抽出し、その商品の名前を配列として取得する場合は次のように書くことができます。

const expensiveProductNames = products
    .filter(product => product.price >= 200)
    .map(product => product.id);

console.log(expensiveProductNames);  // 出力結果を表示

上記のコードを実行すると、商品のIDである[3, 4, 5]が表示される結果となります。

●注意点と対処法

TypeScriptのreduceメソッドを使う際、特定の状況下でユーザーが遭遇する可能性のある問題やトラップについて深く掘り下げ、それらの問題を避けるための対処法を提供します。

特に、reduceメソッドは強力なツールでありながら、誤用すると予期しない結果やエラーが生じることがあります。

○空の配列を扱う際の注意点とその対処法

このコードでは、TypeScriptのreduceメソッドを用いて、数字の配列の合計を計算します。

const numbers: number[] = [];
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue);
console.log(sum);

このコードを実行すると、エラーが発生します。

なぜなら、reduceメソッドは空の配列に対して実行された場合、第二引数として初期値が提供されていないとエラーをスローするからです。

エラーを回避するためには、reduceメソッドの第二引数に初期値を提供することが推奨されます。

下記のサンプルコードでは、初期値0を提供しています。

const numbers: number[] = [];
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum);

このコードを実行すると、0が正しく出力され、エラーは発生しません。

○初期値を設定しない場合の動作

reduceメソッドを使用する際、初期値を設定しない場合の動作を理解することは非常に重要です。

初期値を省略した場合、reduceは配列の最初の要素を初期値として扱い、次の要素からコールバック関数の実行を開始します。

例えば、次のサンプルコードを考えてみましょう。

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

このコードでは、初期値を設定していません。

そのため、1が初期値として扱われ、2から累積が開始されます。このコードを実行すると、正しく15が出力されます。

●reduceのカスタマイズ方法

JavaScript、特にTypeScriptでのプログラミングにおいて、reduceメソッドは非常に強力なツールです。

一見シンプルに見えるこのメソッドですが、適切にカスタマイズすることで、多彩なデータ変換や処理が可能となります。

今回は、このカスタマイズ方法の一例として、カスタムコールバック関数の活用を中心に説明します。

○サンプルコード7:カスタムコールバック関数を活用

基本的なreduceの動作として、配列の各要素を左から右へと処理していくことが知られています。

しかし、コールバック関数をカスタマイズすることで、その挙動を大きく変えることも可能です。

カスタムコールバック関数を使用して、配列内の文字列の長さを合計するサンプルコードを紹介します。

// 文字列の配列を定義
const words = ["apple", "banana", "cherry"];

// 文字列の長さを合計するカスタムコールバック関数
const totalLength = words.reduce((acc, word) => {
    // 現在の文字列の長さを累積値に加算
    return acc + word.length;
}, 0); // 初期値として0を設定

console.log(totalLength); // 16と表示される

このコードでは、wordsという文字列の配列を定義しています。

その後、reduceメソッドを使用して、配列内の各文字列の長さを合計しています。

コールバック関数の第一引数accは累積値、第二引数wordは現在の要素を表します。

このコードを実行すると、3つの文字列”apple”、”banana”、”cherry”の長さの合計である16が出力されます。

○サンプルコード8:データの型を変更しながらreduceを行う

reduceメソッドを用いることで、配列の要素を処理しつつ、データの型を変更することが可能です。

このステップでは、文字列の配列から、各文字列の長さをキーとして、その文字列を値とするオブジェクトを生成する方法を学びます。

考え方としては、配列の各要素に対して処理を行い、その結果を新しい型のオブジェクトとして累積していきます。

このとき、コールバック関数の第一引数(累積値)の型をオブジェクトに指定し、第二引数(現在の要素)の型を文字列に指定します。

それでは、具体的なサンプルコードを見ていきましょう。

const words: string[] = ['apple', 'banana', 'cherry'];

const wordLengthMap = words.reduce<Record<number, string>>((acc, word) => {
  acc[word.length] = word;
  return acc;
}, {});

console.log(wordLengthMap);

このコードでは、wordsという文字列の配列が与えられています。

目的は、各単語の文字数をキーとし、その単語自体を値とするオブジェクトを生成することです。

reduceメソッドの型引数として<Record<number, string>>を指定しています。

これにより、累積値の型をオブジェクトに固定しています。

コールバック関数内で、各単語の文字数word.lengthをキーとして、単語自体を値としてオブジェクトに追加しています。

このコードを実行すると、次のような結果が得られます。

オブジェクトは次のようになります。

{
  5: "apple",
  6: "banana",
  6: "cherry"
}

しかし、注意していただきたいのは、”banana”と”cherry”の文字数が同じであるため、キーが6のものが上書きされてしまいます。

この例から、キーが重複する可能性がある場合の取り扱いに注意が必要であることがわかります。

キーの重複を避けたい場合は、値を配列として保持する、あるいはキー自体を一意にするなどの対策が考えられます。

例えば、次のように修正することで、同じ文字数の単語を配列として格納することができます。

const words: string[] = ['apple', 'banana', 'cherry'];

const wordLengthMap = words.reduce<Record<number, string[]>>((acc, word) => {
  if (!acc[word.length]) {
    acc[word.length] = [];
  }
  acc[word.length].push(word);
  return acc;
}, {});

console.log(wordLengthMap);

この修正を行うと、オブジェクトは次のようになります。

{
  5: ["apple"],
  6: ["banana", "cherry"]
}

まとめ

TypeScriptのreduceメソッドは、配列の各要素を左から右へと処理して、単一の出力値を生成するための強力なツールです。

この記事では、TypeScriptのreduceメソッドの基本から応用まで、多岐にわたるサンプルコードとその詳細な説明を交えながら、初心者から上級者までの読者に対してその使い方を徹底的に解説しました。

TypeScriptのreduceメソッドを上手に活用することで、データ処理の効率やコードの可読性を向上させることが可能です。

この記事を参考に、reduceメソッドの使い方を習得し、日常のプログラミング作業に役立ててください。