【GAS】2次元配列のデータを「ID」で一瞬で比較・突合する方法

自動化・GAS

店舗ごとの在庫データや、システムから出力された売上データ。「商品IDをキーにして、2つの表の差分を比較したい!」という場面、実務で本当によくありますよね。

しかし、スプレッドシートのVLOOKUP関数で数千件のデータを突合しようとすると、重くて画面がフリーズ…。

「GAS(Google Apps Script)を使えば自動化できそうだけど、どうコードを書けばいいかわからない!」と悩んでいませんか?

この記事では、「やりたいことは明確なのに、GASの書き方がわからない初級者」に向けて、2つの2次元配列データを一瞬で比較・結合する最強のテクニックをご紹介します。

コードはコピペで動くように用意してあるので、まずはご自身の環境で試してみてください!

1. なぜ、データ比較のGASは「エラー」になりやすいのか?

GASを学び始めた方が、2つのデータを比較しようとしたとき、よくやってしまう書き方があります。

それは「for文の中に、さらにfor文を書く(ループのネスト)」という方法です。

【GAS入門】スクリプトが重い・タイムアウトする原因!「ループのネスト(二重ループ)」の落とし穴
「GASを使って2つのデータを比較するスクリプトを書いたのに、実行が終わらない…」 「しばらく待っていると『実行時間が上限を超えました』というエラーが出てしまう」GASで業務効率化を進めていると、必ずと言っていいほどぶつかるのがこの「6分の…

実はこれ、データ量が増えるとGASの実行時間制限(6分ルール)に引っかかってエラーになってしまいます。

❌ NGな探し方(for文のネスト)

「かえで薬局」の棚にある薬を1つ手に取るたびに、「いちょう薬局」の棚の端から端まで歩いて同じ薬を探す作業。

もしデータが1,000件ずつあった場合、1,000回 × 1,000回 = 100万回も探す作業が発生してしまいます。

これでは時間がいくらあっても足りませんよね。

2. 解決策は、データを「辞書」にしておくこと

この問題を解決する魔法のテクニックが、オブジェクト {} を使った「辞書化」です。

⭕️ 正解の探し方(辞書化)

探される側(いちょう薬局)のデータを、あらかじめ「商品IDのインデックス付き辞書」に作り替えておく。

GASでは、データを { 'A001': [データの中身] } のように、「見出し(キー)」と「中身(値)」のセットにしておくことができます。

これなら、かえで薬局の薬を手に取ったとき、辞書をパッと引くだけで一瞬でいちょう薬局のデータが見つかります。

3. 【コピペOK】複数拠点のデータを高速比較するサンプルコード

それでは、実際のコードを見てみましょう。

今回は「かえで薬局」と「いちょう薬局」の在庫一覧データ(2次元配列)を読み込み、共通の「薬品ID」をキーにして突合し、在庫の差分を出すスクリプトです。

まずは以下のコードを、ご自身のGASエディタにコピペして実行してみてください。

function compareInventoryFast() {
  // ① データの準備(実際はスプレッドシート等から取得した2次元配列)
  // 並び順:[薬品ID, 薬品名, 在庫数, カテゴリ]
  const kaedeData = [
    ['A001', 'ロキソプロフェン', 500, '内服薬'],
    ['A002', 'ムコスタ', 300, '内服薬'],
    ['X999', '管理薬剤師手当', 1, '人員'] // ← 合算してはいけないノイズデータ
  ];

  const ginkgoData = [
    ['A001', 'ロキソプロフェン', 400, '内服薬'],
    ['A003', 'カロナール', 200, '内服薬'],
    ['X999', '応援事務', 1, '人員'] // ← 合算してはいけないノイズデータ
  ];

  // ② 事前処理:ノイズデータ(人員など)を除外する
  const isNotPersonnel = (row) => row[3] !== '人員';
  const cleanKaede = kaedeData.filter(isNotPersonnel);
  const cleanGinkgo = ginkgoData.filter(isNotPersonnel);

  // ③ 比較される側(いちょう薬局)のデータを「辞書化 {}」する
  const ginkgoObj = {};
  for (let i = 0; i < cleanGinkgo.length; i++) {
    const row = cleanGinkgo[i];
    const itemId = row[0]; // 薬品ID(0番目の列)を見出しにする
    ginkgoObj[itemId] = row; // IDを見出しとして、行のデータを丸ごと格納
  }

  // ④ ベースデータ(かえで薬局)を順番に見て、辞書と突き合わせる
  const result = [];
  for (let i = 0; i < cleanKaede.length; i++) {
    const kaedeRow = cleanKaede[i];
    const itemId = kaedeRow[0]; // 探したい薬品ID

    // 辞書に見出し(薬品ID)があるか一発で確認!
    if (ginkgoObj[itemId]) {
      const ginkgoRow = ginkgoObj[itemId];
      const diff = kaedeRow[2] - ginkgoRow[2]; // 在庫数の差分を計算
      
      // 結果を新しい配列として保存:[ID, 薬品名, かえで在庫, いちょう在庫, 差分]
      result.push([itemId, kaedeRow[1], kaedeRow[2], ginkgoRow[2], diff]);
    } else {
      // 辞書になかった(いちょう薬局に在庫がない)場合
      result.push([itemId, kaedeRow[1], kaedeRow[2], 0, kaedeRow[2]]);
    }
  }

  // ⑤ 抽出結果の確認(ログ出力)
  console.log(result);
}

4. あなたのデータに合わせてカスタマイズ!

コピペで動くことを確認したら、次はご自身の業務データに合わせてコードを書き換えてみましょう。変更するポイントは主に以下の2つです。

📌 ポイント1:キーとなる「列」の番号を変更する

プログラミングの世界では、配列(列)の番号は「0」から始まります。

  • A列 = 0
  • B列 = 1
  • C列 = 2

サンプルコードの const itemId = row[0]; は、「A列の薬品IDをキーにする」という意味です。もし比較したいIDがC列にある場合は、ここを row[2] に変更してください。

📌 ポイント2:抽出したい結果の形を変える

コードの後半にある result.push([...]) の部分が、最終的に出力されるデータの中身です。

ご自身がスプレッドシートに書き出したい列の順番に合わせて、kaedeRow[0](かえでのA列)などを自由に組み合わせてみてください。

5. 【実務のリアル】比較前の「ゴミ取り」を忘れずに

最後に、実務でシステムを組む際の重要なワンポイントアドバイスです。

業務システムからCSVなどで出力した生データには、純粋な在庫や売上以外に「人員計算用のダミーデータ」や「小計行」などのノイズが混ざっていることがよくあります。

これをそのまま突合してしまうと、二重計上や思わぬエラーの原因になります。

サンプルコードの「② 事前処理」で filter を使っているのはそのためです。

「データを比較する前に、不要なゴミを取り除いておく」。このひと手間をコードに組み込めるようになると、現場で重宝される実用的なツールに一気に進化します!

まとめ:GASでデータ処理の限界を突破しよう

  • VLOOKUPfor文のネストは処理が重くなる原因。
  • データを {} で「辞書化」すれば、検索スピードが劇的に上がる。
  • 実務データは、比較する前に filter で綺麗にする。

「やりたいことは分かっているのに…」と足踏みしていた方は、ぜひこの「辞書化」のテクニックを使って、GASでの自動化を一歩前に進めてみてください!