GASの配列から、同じメールが何度も出る行を一意にする

GAS

名簿の同じ行を残したくない

Google Apps Script(GAS)を用いた業務自動化において、「配列から重複したデータを削除して一意にする」という処理は非常に頻出します。スプレッドシートから読み込んだ顧客名簿の整理、複数シートから集約したデータの一意化など、一次元・二次元配列問わず、重複排除のスキルは必須と言えるでしょう。

ES6から導入されたSetオブジェクトを利用した高速かつ簡潔な重複削除の手法から、オブジェクト配列におけるfilterとfindIndexを用いた実践的な手法まで、初学者にもわかりやすく書きます。

数字や文字のリストなら Set

まずは最も基本となる、一次元配列(数値や文字列といったプリミティブ値のリスト)から重複データを削除する方法を見ていきましょう。GAS(V8ランタイム)では、モダンなJavaScriptの機能であるSetオブジェクトを利用することで、非常に短く効率的なコードを記述可能です。

重複した 2 と 4 を落とす

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

Set にしたあと配列に戻す

  • 1行目:const array = [1, 2, 2, 3, 4, 4, 5];
    重複した数値(2や4)を含む一次元配列を変数arrayとして定義しています。実務では、ここがスプレッドシートの特定の列から取得した一次元配列などに置き換わります。
  • 2行目:const uniqueArray = [...new Set(array)];
    この処理の核心部です。new Set(array)で配列をSetオブジェクトに変換します。Setは仕様上「重複する値を持てない」コレクションであるため、この時点で重複データ(2や4の2つ目以降)が自動的に排除されます。さらに、その外側をスプレッド構文[... ]で囲むことにより、Setオブジェクトを再び標準的な配列(Array)に変換し直し、変数uniqueArrayに格納しています。
  • 3行目:Logger.log(uniqueArray);
    結果をログに出力します。出力結果は [1.0, 2.0, 3.0, 4.0, 5.0] のように重複が除かれた一意な値となります。

Setオブジェクトを用いるメリット

ES6のSetを用いる最大のメリットは、高速に重複排除が可能である点です。従来のindexOfなどをループで回してチェックする手法と比較すると、コードの可読性が格段に向上するだけでなく、要素数が多い配列を処理する際の計算量(パフォーマンス)の観点でも優れています。

行データは Set では一意にならない

前述のSetオブジェクトは、数値や文字列などの「プリミティブ値」に対しては強力に機能します。しかし、実務で頻繁に扱う「スプレッドシートから取得した二次元配列」や「JSON APIなどから取得したオブジェクト配列」の場合、単なるSetでは重複を排除できません。

なぜSetではオブジェクト配列の重複削除ができないのか?

JavaScriptにおいて、配列やオブジェクトは「参照型」と呼ばれるデータ構造です。中身の値が全く同じ {id: 1, name: "田中"} という2つのオブジェクトがあったとしても、それらはメモリ上の異なる場所を参照しているため、Setは「別の値」として判定してしまいます。これが重複削除処理における代表的な落とし穴です。

解決策:filterとfindIndexを併用する

プリミティブ値以外(オブジェクト配列や二次元配列)の重複データを一意にする場合は、配列メソッドであるfilterとfindIndexを組み合わせて実装します。

実装例:オブジェクト配列の重複排除

const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 1, name: "Alice" } // 重複データ
];

const uniqueUsers = users.filter((user, index, self) => {
  return self.findIndex(u => u.id === user.id) === index;
});

Logger.log(uniqueUsers);

コードの解説とメカニズム

  • filterメソッドは、条件に合致した(trueを返した)要素だけを抽出して新しい配列を生成します。コールバック関数の引数には、(現在の要素, 現在のインデックス, 配列そのもの)が入ります。
  • findIndexメソッドは、条件に合致する「最初の」要素のインデックス番号を返します。
  • つまり、self.findIndex(u => u.id === user.id) は、配列を先頭から探して「同じidを持つ最初の要素のインデックス」を返します。
  • この最初に見つかったインデックスと、現在処理中の要素のインデックス(index)が一致する場合のみtrueを返すことで、2回目以降に出現した重複データ(インデックスが一致しないデータ)を自動的に排除するメカニズムです。

メールアドレスをキーに名簿を洗う

ここでは、GASの現場でよく遭遇するよくあるのは次の2つです。

1. スプレッドシートの重複行を一掃する

アンケートフォームの回答データや、各担当者が入力した営業リストなどにおいて、メールアドレスをキーにして重複する顧客情報を排除したいケースが多々あります。取得した二次元配列を前述のfilterとfindIndexを用いて一意化し、スプレッドシートに書き戻すことで、名簿のクレンジングを完全自動化できます。

2. 複数APIのレスポンス結合と整理

複数の外部サービスからAPI経由でデータを取得し、1つの統合リストを作成する際、共通のIDを持つデータが重複して取り込まれることがあります。このような場面でも、取得データを一度結合(concatなど)した後に重複削除処理を挟むことで、精度の高いデータベースを構築することが可能です。

件数が多いと findIndex は重い

最後に、GASにおける配列の重複削除についての仕様と注意点を再確認しておきましょう。

手法 適用対象 特徴と注意点
[...new Set(array)] 一次元配列(プリミティブ値) 非常に高速で簡潔。ただし、配列やオブジェクトがネストした二次元以上のデータに対しては、参照元が異なるため重複判定できず適用できない。
filter + findIndex オブジェクト配列、二次元配列 複雑なデータ構造の一意化に必須。特定のキー(IDなど)を指定して重複排除が可能。ただし要素数が数万件などの膨大な配列に対しては処理負荷(計算量)が高くなるため、実行時間制限に注意が必要。

一次元は Set、行は filter

GASで配列の重複データを削除して一意にする場合、扱うデータの構造によって適切なアプローチを選択することが重要です。一次元配列のようなシンプルなデータであれば、ES6のSetオブジェクトを活用して高速に処理しましょう。一方、二次元配列やオブジェクト配列のように複雑な構造を持つデータの場合は、filterとfindIndexを組み合わせることで確実な重複排除が実現します。
これらの処理はGASを用いたデータクレンジングの基礎となるため、ぜひ実務のスクリプトに取り入れてみてください。

タイトルとURLをコピーしました