JavaScript
 Computer >> コンピューター >  >> プログラミング >> JavaScript

JavaScriptで任意の数の配列から共通する要素を見つける方法

問題の概要

次のように、複数の数値配列を値として持つオブジェクトがあるとします。

const obj = {
  a: [ 15, 23, 36, 49, 104, 211 ],
  b: [ 9, 12, 23 ],
  c: [ 11, 17, 18, 23, 38 ],
  d: [ 13, 21, 23, 27, 40, 85 ]
};

このオブジェクトが持つプロパティの数は固定されておらず、いくつあっても構いません。そこで必要になるのは、こうしたオブジェクトを引数として受け取り、すべての配列に共通して含まれる要素だけを集めた配列を返すJavaScript関数です。

上記のオブジェクトの場合、4つの配列すべてに存在するのは 23 だけなので、期待される出力は次のようになります。

const output = [23];

基本的な考え方

任意の個数の配列を扱う場合、次の手順で処理するのがシンプルで分かりやすい方法です。

  1. まず2つの配列の共通要素を返すヘルパー関数を用意します。

  2. オブジェクトの最初の配列を初期値とし、残りの配列と順番に共通要素の計算を繰り返します。

  3. 途中で結果が空になった時点で共通要素はもう存在しないことが確定するため、そこで処理を打ち切って空配列を返すと、無駄な計算を省けます。

コード例

const obj = {
  a: [ 15, 23, 36, 49, 104, 211 ],
  b: [ 9, 12, 23 ],
  c: [ 11, 17, 18, 23, 38 ],
  d: [ 13, 21, 23, 27, 40, 85 ]
};

// 2つの配列の共通要素を求めるヘルパー関数
const commonBetweenTwo = (arr1, arr2) => {
  const res = [];
  for (let i = 0; i < arr1.length; i++) {
    if (arr2.includes(arr1[i])) {
      res.push(arr1[i]);
    }
  }
  return res;
};

// 任意の数の配列から共通要素を求める本体の関数
const commonBetweenMany = (obj = {}) => {
  const keys = Object.keys(obj);
  let res = obj[keys[0]];
  for (let i = 1; i < keys.length; i++) {
    res = commonBetweenTwo(res, obj[keys[i]]);
    // 共通要素がなくなったら即座に終了
    if (!res.length) {
      return [];
    }
  }
  return res;
};

console.log(commonBetweenMany(obj));

コンソールには次のように出力されます。

[23]

reduce() を使ったより簡潔な書き方

Array.prototype.reduce() を使えば、同じ処理をもっと短く表現できます。

const commonBetweenMany = (obj = {}) => {
  const arrays = Object.values(obj);
  if (!arrays.length) return [];
  return arrays.reduce((acc, arr) =>
    acc.filter(value => arr.includes(value))
  );
};

最初の配列を累積値(acc)とし、以降の各配列に対して filter() で共通要素だけを残していくイメージです。こちらも配列の個数に依存せず、どんなに多くの配列があっても対応できます。

パフォーマンスを上げるポイント

配列の includes() は線形探索を行うため、要素数が多いケースではあらかじめ Set に変換しておくと検索が大幅に高速になります。

const commonBetweenMany = (obj = {}) => {
  const sets = Object.values(obj).map(arr => new Set(arr));
  const [first, ...rest] = sets;
  return [...first].filter(value =>
    rest.every(set => set.has(value))
  );
};

この方法では、先頭の配列の各要素について「残りすべての Set に存在するか」をまとめて判定できるため、大規模なデータセットでも効率的に動作します。

まとめ

  • 2配列間の共通要素を求める関数を順次適用すれば、任意の数の配列へ簡単に拡張できる。
  • 途中で共通要素が空になったら早期リターンすることで、無駄な計算を回避できる。
  • reduce() や Set を活用すると、コードを簡潔にしつつパフォーマンスも向上させられる。
  1. JavaScriptで2つの配列から作れる最大の数を求める方法

    問題1桁の数字を要素とする2つの配列 arr1 と arr2 を第1・第2引数として受け取り、さらに数値 num(num <= arr1.length + arr2.length)を第3引数として受け取るJavaScript関数を作成します。この関数が返すのは、長さ num の1桁の数字からなる新しい配列です。この配列自体が1つの数値を表しており、その値は両方の配列の要素を組み合わせて作成できる最大の数でなければなりません。ただし、重要な条件がひとつあります。それは、同じ配列内の要素の相対的な順序は維持しなければならないという点です。例えば、関数への入力が次の場合を考えてみましょう。co

  2. JavaScriptで2つの配列間の欠落した数値を見つける方法

    問題の概要 2つの配列 arr1 と arr2 を引数として受け取るJavaScript関数を作成します。 arr2 は arr1 の要素をシャッフルした複製ですが、たった1つの要素だけが欠落しています。 この関数の目的は、その欠落している1つの要素を見つけ出して返すことです。 アプローチのポイント 最もシンプルかつ効率的なのは、ハッシュマップ(オブジェクト)を使って各数値の出現回数を記録する方法です。計算量は O(n) に抑えられ、配列内に重複した値が含まれていても正しく動作します。 コード例 以下が実際のコードです。 const arr1 = [6, 1, 3, 6, 8, 2];