読み込み中...

【1分で解決】JavaScriptの配列要素をdistinctする方法10選

JavaScriptの配列から重複要素をdistinctする10の方法 JS
この記事は約20分で読めます。

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

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

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

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

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

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

●JavaScriptの配列とは?

JavaScriptの配列は、複数のデータを順序付けて格納するためのオブジェクトです。

配列を使うことで、関連するデータをまとめて管理し、効率的にプログラムを書くことができます。

配列は、Webアプリケーション開発において非常に重要な役割を果たしています。

○配列の基本

配列を作成するには、角括弧[]を使います。

例えば、次のようにして数値の配列を作ることができます。

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

文字列の配列も同様に作成できます。

const fruits = ['apple', 'banana', 'orange'];

配列の要素にアクセスするには、インデックス番号を使います。

インデックスは0から始まります。

console.log(numbers[0]); // 1
console.log(fruits[1]); // 'banana'

○配列の重複要素の問題点

配列を扱っていると、時々重複した要素が含まれていることがあります。

例えば、次のような配列を考えてみましょう。

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

この配列には、1, 2, 3という要素が重複しています。

重複した要素があると、データの整合性が取れなくなったり、意図しない動作を引き起こしたりする可能性があります。

また、重複要素があると、配列のサイズが不必要に大きくなってしまいます。

これはメモリの無駄遣いにつながり、パフォーマンスにも悪影響を与えかねません。

そのため、配列から重複要素を取り除き、ユニークな要素だけを残すことが重要です。

これをdistinct(重複削除)と呼びます。

●配列の重複要素を削除する10の方法

重複要素の削除、つまりdistinctを実現する方法はいくつかあります。

それぞれのアプローチにはメリットとデメリットがありますので、状況に応じて適切な方法を選ぶことが大切です。

ここでは、代表的な10の方法を紹介していきます。

○サンプルコード1:Setオブジェクトを使う方法

Setオブジェクトは、ユニークな値の集合を表すビルトインオブジェクトです。

Setは重複する値を許容しないため、配列からSetを作ることで重複要素を取り除くことができます。

const duplicatedArray = [1, 2, 3, 1, 2, 4, 5, 3];
const uniqueArray = [...new Set(duplicatedArray)];

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

ここでは、スプレッド構文を使ってSetから新しい配列を作っています。

これは、Setを配列に変換する簡単な方法です。

Setを使う方法は、シンプルで読みやすいコードになります。

ただし、配列の要素が大量にある場合、一時的にメモリを大量に消費してしまうというデメリットがあります。

○サンプルコード2:filter()メソッドを使う方法

filter()メソッドは、与えられた条件を満たす要素だけを残した新しい配列を返します。

これを利用して、重複要素を取り除くことができます。

const duplicatedArray = [1, 2, 3, 1, 2, 4, 5, 3];
const uniqueArray = duplicatedArray.filter((element, index, array) => {
  return array.indexOf(element) === index;
});

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

filter()メソッドのコールバック関数の中で、indexOf()メソッドを使って各要素の最初のインデックスを取得しています。

このインデックスが現在の要素のインデックスと一致する場合だけ、その要素を残すようにしています。

filter()を使う方法は、関数型プログラミングの考え方に沿っているため、可読性の高いコードになります。

ただし、大量の要素を扱う場合、パフォーマンスが悪くなる可能性があります。

○サンプルコード3:reduce()メソッドを使う方法

reduce()メソッドは、配列の要素を順番に処理し、単一の値にまとめます。

これを応用して、重複のない配列を作ることができます。

const duplicatedArray = [1, 2, 3, 1, 2, 4, 5, 3];
const uniqueArray = duplicatedArray.reduce((accumulator, current) => {
  if (!accumulator.includes(current)) {
    accumulator.push(current);
  }
  return accumulator;
}, []);

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

reduce()メソッドのコールバック関数の中で、アキュムレータ配列に現在の要素が含まれているかどうかを確認しています。

含まれていない場合だけ、その要素をアキュムレータに追加するようにしています。

reduce()を使う方法は、パフォーマンスが比較的良く、大量の要素を扱う場合にも適しています。

ただし、コードがやや複雑になるというデメリットがあります。

○サンプルコード4:indexOf()メソッドを使う方法

indexOf()メソッドは、配列の中で指定された要素が最初に出現するインデックスを返します。

これを利用して、重複要素を取り除くことができます。

const duplicatedArray = [1, 2, 3, 1, 2, 4, 5, 3];
const uniqueArray = [];

for (let i = 0; i < duplicatedArray.length; i++) {
  if (uniqueArray.indexOf(duplicatedArray[i]) === -1) {
    uniqueArray.push(duplicatedArray[i]);
  }
}

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

forループを使って配列を順番に処理し、重複していない要素だけを新しい配列に追加しています。

indexOf()メソッドの戻り値が-1の場合、その要素は新しい配列に存在しないことを意味します。

indexOf()を使う方法は、わかりやすいコードになります。

ただし、大量の要素を扱う場合、パフォーマンスが悪くなる可能性があります。

ここまで、配列から重複要素を取り除く4つの方法を見てきました。

どの方法も一長一短ありますが、シンプルさ、パフォーマンス、可読性などを考慮して、適切な方法を選ぶことが大切です。

次は、さらに別のアプローチを見ていきましょう。

○サンプルコード5:includes()メソッドを使う方法

includes()メソッドは、配列に特定の要素が含まれているかどうかを判定します。

これを利用して、重複要素を取り除くことができます。

const duplicatedArray = [1, 2, 3, 1, 2, 4, 5, 3];
const uniqueArray = [];

duplicatedArray.forEach((element) => {
  if (!uniqueArray.includes(element)) {
    uniqueArray.push(element);
  }
});

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

forEach()メソッドを使って配列の要素を順番に処理し、新しい配列に含まれていない要素だけを追加しています。

includes()メソッドは、ES2016で導入された比較的新しいメソッドです。

includes()を使う方法は、シンプルで読みやすいコードになります。

ただし、大量の要素を扱う場合、パフォーマンスが悪くなる可能性があります。

○サンプルコード6:オブジェクトを使う方法

JavaScriptのオブジェクトは、キーと値のペアを格納するデータ構造です。

オブジェクトのキーはユニークでなければならないという性質を利用して、重複要素を取り除くことができます。

const duplicatedArray = [1, 2, 3, 1, 2, 4, 5, 3];
const uniqueObject = {};

duplicatedArray.forEach((element) => {
  uniqueObject[element] = element;
});

const uniqueArray = Object.values(uniqueObject);

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

まず、空のオブジェクトを用意します。

そして、配列の要素をオブジェクトのキーとして設定していきます。

重複する要素は上書きされるため、最終的にオブジェクトのキーはユニークな値だけが残ります。

最後に、Object.values()メソッドを使ってオブジェクトの値を配列に変換します。

こうして、重複のない配列が得られます。

オブジェクトを使う方法は、シンプルでわかりやすいコードになります。

ただし、配列の要素が大量にある場合、オブジェクトのサイズが大きくなってしまうというデメリットがあります。

○サンプルコード7:lodashライブラリのuniq()メソッドを使う方法

lodashは、JavaScriptの配列やオブジェクトを操作するための便利なユーティリティ関数を提供するライブラリです。

lodashのuniq()メソッドを使えば、配列から重複要素を簡単に取り除くことができます。

const _ = require('lodash');

const duplicatedArray = [1, 2, 3, 1, 2, 4, 5, 3];
const uniqueArray = _.uniq(duplicatedArray);

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

lodashを使うには、まずnpmでインストールする必要があります。

そして、コード内でlodashをインポートします。

uniq()メソッドは、配列を引数として受け取り、重複のない新しい配列を返します。

とてもシンプルで使いやすいメソッドです。

ただし、lodashを使うには外部ライブラリへの依存が発生してしまうというデメリットがあります。

プロジェクトの規模や方針に応じて、適切に判断する必要があります。

○サンプルコード8:forループを使う方法

古典的ですが、forループを使って重複要素を取り除く方法もあります。

これは、JavaScriptの基本的な構文だけを使うシンプルな方法です。

const duplicatedArray = [1, 2, 3, 1, 2, 4, 5, 3];
const uniqueArray = [];

for (let i = 0; i < duplicatedArray.length; i++) {
  if (uniqueArray.indexOf(duplicatedArray[i]) === -1) {
    uniqueArray.push(duplicatedArray[i]);
  }
}

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

forループを使って配列を順番に処理し、新しい配列に含まれていない要素だけを追加していきます。

indexOf()メソッドを使って、要素が新しい配列に存在するかどうかを確認しています。

forループを使う方法は、JavaScriptの基礎を理解していれば誰でも書けるシンプルなコードになります。

ただし、大量の要素を扱う場合、パフォーマンスが悪くなる可能性があります。

○サンプルコード9:再帰関数を使う方法

再帰関数を使って、配列から重複要素を取り除くこともできます。

再帰関数とは、関数の中で自分自身を呼び出す関数のことです。

function removeDuplicates(array) {
  if (array.length <= 1) {
    return array;
  }

  const [first, ...rest] = array;
  const uniqueArray = removeDuplicates(rest);

  if (uniqueArray.includes(first)) {
    return uniqueArray;
  } else {
    return [first, ...uniqueArray];
  }
}

const duplicatedArray = [1, 2, 3, 1, 2, 4, 5, 3];
const uniqueArray = removeDuplicates(duplicatedArray);

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

再帰関数removeDuplicatesは、配列を引数として受け取ります。

配列の長さが1以下の場合、そのまま配列を返します。

配列の最初の要素をfirst、残りの要素をrestとして分割します。

そして、restに対して再帰的にremoveDuplicatesを呼び出します。

uniqueArrayにfirstが含まれている場合、そのままuniqueArrayを返します。

含まれていない場合は、firstをuniqueArrayの先頭に追加して返します。

再帰関数を使う方法は、エレガントで洗練されたコードになります。

ただし、再帰呼び出しが多くなると、コールスタックがオーバーフローするリスクがあります。

○サンプルコード10:配列のスプレッド構文を使う方法

ES2015で導入された配列のスプレッド構文を使って、配列から重複要素を取り除くこともできます。

スプレッド構文は、配列やオブジェクトを展開するための構文です。

const duplicatedArray = [1, 2, 3, 1, 2, 4, 5, 3];
const uniqueArray = [...new Set(duplicatedArray)];

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

まず、配列からSetオブジェクトを作ります。

Setは重複する値を許容しないため、結果的に重複のない値だけが残ります。

そして、スプレッド構文を使ってSetを配列に変換します。

これは、Setを配列に変換する簡単な方法です。

スプレッド構文を使う方法は、シンプルで読みやすいコードになります。

ただし、配列の要素が大量にある場合、一時的にメモリを大量に消費してしまうというデメリットがあります。

●よくあるエラーと対処法

配列操作を行う際、うっかりミスや思い違いから、エラーが発生することがあります。

エラーに遭遇した時は、落ち着いて原因を探ることが大切です。

ここでは、重複削除に関連するよくあるエラーと、その対処法を見ていきましょう。

○TypeError: Cannot read property ‘forEach’ of undefined

このエラーは、undefinedな値に対してforEach()メソッドを呼び出そうとした時に発生します。

つまり、配列だと思っていた変数が、実はundefinedだったということです。

let myArray;
myArray.forEach((element) => {
  console.log(element);
});
// TypeError: Cannot read property 'forEach' of undefined

このエラーを避けるには、変数が確かに配列であることを確認してからforEach()メソッドを呼び出すようにします。

let myArray;
if (Array.isArray(myArray)) {
  myArray.forEach((element) => {
    console.log(element);
  });
}

Array.isArray()メソッドを使えば、変数が配列かどうかを判定できます。

これを条件分岐に使うことで、undefinedに対するforEach()メソッドの呼び出しを防げます。

○TypeError: Cannot convert undefined or null to object

このエラーは、undefinedやnullな値に対して、配列のメソッドや構文を使おうとした時に発生します。

let myArray = null;
const uniqueArray = [...new Set(myArray)];
// TypeError: Cannot convert undefined or null to object

このエラーを避けるには、変数が確かに配列であることを確認してから操作を行うようにします。

let myArray = null;
if (myArray) {
  const uniqueArray = [...new Set(myArray)];
  console.log(uniqueArray);
}

変数がtruthyな値(null, undefined, 0, “”, NaNでない値)であることを確認してから処理を行うようにすれば、このエラーを防げます。

○RangeError: Maximum call stack size exceeded

このエラーは、再帰関数の呼び出しが深すぎて、コールスタックの限界を超えた時に発生します。

function removeDuplicates(array) {
  const [first, ...rest] = array;
  const uniqueArray = removeDuplicates(rest);

  if (uniqueArray.includes(first)) {
    return uniqueArray;
  } else {
    return [first, ...uniqueArray];
  }
}

const duplicatedArray = [1, 2, 3, 1, 2, 4, 5, 3];
const uniqueArray = removeDuplicates(duplicatedArray);
// RangeError: Maximum call stack size exceeded

このエラーを避けるには、再帰の終了条件を適切に設定することが重要です。

function removeDuplicates(array) {
  if (array.length <= 1) {
    return array;
  }

  const [first, ...rest] = array;
  const uniqueArray = removeDuplicates(rest);

  if (uniqueArray.includes(first)) {
    return uniqueArray;
  } else {
    return [first, ...uniqueArray];
  }
}

const duplicatedArray = [1, 2, 3, 1, 2, 4, 5, 3];
const uniqueArray = removeDuplicates(duplicatedArray);
console.log(uniqueArray); // [1, 2, 3, 4, 5]

配列の長さが1以下の場合は、そのまま配列を返すようにします。これが再帰の終了条件になります。

適切な終了条件を設定すれば、コールスタックのオーバーフローを防げます。

●重複削除の応用例

重複削除の手法は、配列操作の基本ともいえる重要なスキルです。

ここでは、その応用例をいくつか紹介していきます。

実際の開発でもよく遭遇するような、ちょっと複雑なケースにも挑戦してみましょう。

○サンプルコード11:オブジェクトの配列から重複を削除する

オブジェクトの配列から重複を削除する場合、オブジェクトのプロパティを比較する必要があります。

ここでは、idプロパティが一致するオブジェクトを重複とみなして削除してみましょう。

const duplicatedObjectArray = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Charlie' },
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
];

const uniqueObjectArray = duplicatedObjectArray.filter(
  (object, index, self) =>
    index === self.findIndex((t) => t.id === object.id)
);

console.log(uniqueObjectArray);
// [
//   { id: 1, name: 'Alice' },
//   { id: 2, name: 'Bob' },
//   { id: 3, name: 'Charlie' }
// ]

filter()メソッドのコールバック関数の中で、findIndex()メソッドを使ってオブジェクトのidプロパティが一致する最初のインデックスを取得しています。

このインデックスが現在の要素のインデックスと一致する場合だけ、その要素を残すようにしています。

○サンプルコード12:大文字小文字を区別せずに重複を削除する

文字列の配列から重複を削除する際、大文字小文字を区別しないようにしたい場合があります。

そんな時は、文字列を全て小文字(または大文字)に変換してから重複を削除します。

const duplicatedStringArray = ['apple', 'banana', 'Apple', 'orange', 'BANANA'];

const uniqueStringArray = [...new Set(duplicatedStringArray.map(str => str.toLowerCase()))];

console.log(uniqueStringArray);
// ['apple', 'banana', 'orange']

まず、map()メソッドを使って配列の全ての文字列を小文字に変換します。

そして、Setを使って重複を削除し、スプレッド構文で再び配列に変換しています。

○サンプルコード13:重複要素の出現回数をカウントする

ある要素が配列の中に何回出現するかをカウントしたい場合もありますよね。

そんな時は、reduceメソッドを使ってオブジェクトにカウントを記録していきます。

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

const countObject = duplicatedArray.reduce((acc, current) => {
  acc[current] = (acc[current] || 0) + 1;
  return acc;
}, {});

console.log(countObject);
// { '1': 2, '2': 2, '3': 2, '4': 1, '5': 1 }

reduce()メソッドを使って、配列の要素をキー、出現回数を値とするオブジェクトを作っています。

acc[current]に値が存在しない場合は0を設定し、そこに1を足すことで出現回数をカウントしています。

○サンプルコード14:重複要素を置換する

配列内の重複要素を、別の値に置換したいケースもあるでしょう。

例えば、nullの重複を削除するのではなく、空文字列に置換するなどです。

const duplicatedArray = [1, 2, 3, null, null, 4, 5, 3];

const replacedArray = duplicatedArray.map((element, index, self) =>
  self.indexOf(element) !== index ? '' : element
);

console.log(replacedArray);
// [1, 2, 3, '', '', 4, 5, '']

map()メソッドを使って配列を新しい配列に変換しています。

indexOf()メソッドを使って、現在の要素が最初に出現するインデックスを取得します。

これが現在のインデックスと一致しない場合、その要素は重複であるとみなし、空文字列に置換しています。

まとめ

JavaScriptの配列から重複要素を削除する方法について、10の具体的なコード例を交えて詳しく解説してきました。

どの方法にもメリットとデメリットがありますが、状況に応じて適切な方法を選ぶことが大切です。

また、よくあるエラーへの対処法や、重複削除を応用したテクニックも身につけることができたのではないでしょうか。

これらの知識を実際の開発に活かして、JavaScriptの配列操作スキルを更に磨いていきましょう。

常に新しいことにチャレンジし、成長し続けることが大切です。