JavaScript Tips | 配列ユーティリティ:結合

JavaScript JavaScript
スポンサーリンク

何をしたいユーティリティか:「配列の結合」

ここでの「結合」は、複数の配列を 1 つの配列にまとめる処理のことです。
英語だと「merge」「concat」と呼ばれます。

例えば、こんな場面で使います。
月ごとの売上データを、四半期分まとめて 1 つの配列にしたい。
ページングされた API レスポンスを、全部つなげて 1 本の配列にしたい。
「検索結果」と「おすすめ一覧」を 1 つの配列として扱いたい。

毎回 forpush を書くのではなく、「結合ユーティリティ」として関数を持っておくと、コードの意図がとても読みやすくなります。


基本中の基本:Array.prototype.concat とスプレッド構文

concat の基本

JavaScript には、配列を結合するための標準メソッド concat があります。

const a = [1, 2];
const b = [3, 4];

const merged = a.concat(b);
// [1, 2, 3, 4]
JavaScript

concat のポイントは、「元の配列を変更せず、新しい配列を返す」ことです。
業務コードでは「元データはそのまま」「変換結果だけ新しく作る」ことが多いので、この性質はとても重要です。

複数まとめて結合することもできます。

const a = [1];
const b = [2, 3];
const c = [4, 5];

const merged = a.concat(b, c);
// [1, 2, 3, 4, 5]
JavaScript

スプレッド構文での結合

最近の書き方としては、スプレッド構文(...)で結合することも多いです。

const a = [1, 2];
const b = [3, 4];

const merged = [...a, ...b];
// [1, 2, 3, 4]
JavaScript

concat と同じく、「元の配列を壊さず、新しい配列を作る」書き方です。
見た目がシンプルで、「配列を展開して並べている」ことが直感的に分かるので、現場でもよく使われます。


実務でよくある「配列結合」のパターン

例1:ページングされた結果を 1 本にまとめる

API が「page=1」「page=2」…のようにページングされた配列を返してくる場合、
それらを全部つなげて 1 本の配列にしたいことがあります。

const page1 = [{ id: 1 }, { id: 2 }];
const page2 = [{ id: 3 }, { id: 4 }];

const all = page1.concat(page2);
// または
const all2 = [...page1, ...page2];
JavaScript

これで、「全件を 1 つの配列として扱う」ことができます。

例2:条件ごとに集めた配列を最後にまとめる

例えば、「エラー」「警告」「情報ログ」を別々に集めておき、
最後に「ログ一覧」として 1 本にまとめたい場合。

const errors = [/* ... */];
const warnings = [/* ... */];
const infos = [/* ... */];

const logs = [...errors, ...warnings, ...infos];
JavaScript

このように、「途中までは別々に処理して、最後に結合する」というパターンは業務でよく出てきます。


ユーティリティとしての「安全な結合」関数

null や単体要素が混ざっても安全に結合する

実務では、「配列かもしれないし null かもしれない」「配列か単体のオブジェクトか分からない」といったケースがよくあります。
そのたびに if 文を書くのは面倒なので、「とりあえず全部まとめて 1 本の配列にしてくれる」ユーティリティがあると便利です。

function toArray(value) {
  if (value == null) {
    return [];
  }
  return Array.isArray(value) ? value : [value];
}

function mergeArrays(...values) {
  const result = [];
  for (const v of values) {
    result.push(...toArray(v));
  }
  return result;
}
JavaScript

toArray は、「null/undefined なら空配列」「配列ならそのまま」「それ以外なら 1 要素の配列」に変換する関数です。
mergeArrays は、それらを全部展開して 1 本の配列にまとめます。

実際の動き

mergeArrays([1, 2], 3, null, [4, 5]);
// [1, 2, 3, 4, 5]

mergeArrays(null, undefined);
// []

mergeArrays({ id: 1 }, [{ id: 2 }]);
// [{ id: 1 }, { id: 2 }]
JavaScript

こうしておくと、「呼び出し側は配列か単体かを気にせず、とにかく mergeArrays に渡せばいい」という設計にできます。


「結合」と「重複排除」をセットにしたユーティリティ

結合したあとに重複を消したいケース

配列を結合するとき、「同じ要素が重複してもいいか?」はよく悩むポイントです。
ID の一覧などでは、「結合したあとに重複を取り除きたい」ことが多いです。

その場合は、Set を使ったユーティリティを用意しておくと便利です。

function mergeUnique(...arrays) {
  const merged = arrays.flat();
  return Array.from(new Set(merged));
}
JavaScript

arrays.flat() で「配列の配列」を 1 段だけ平らにし、
new Set(merged) で重複を排除し、
Array.from でふたたび配列に戻しています。

実際の動き

mergeUnique([1, 2], [2, 3], [3, 4]);
// [1, 2, 3, 4] (順序は元の出現順)

mergeUnique(["a", "b"], ["b", "c"]);
// ["a", "b", "c"]
JavaScript

「結合」と「重複排除」がセットになっていると、
ID リストやタグ一覧などを扱うときにとても重宝します。


オブジェクト配列の「結合」と「マージ」の違い

単純結合と「キーでマージ」は別物

ここまでの「結合」は、単純に「配列をつなげる」だけです。
オブジェクト配列の場合、「同じ id の要素をマージしたい」というニーズもありますが、
それは「結合」ではなく「マージ(統合)」の領域になります。

単純結合はこうです。

const a = [{ id: 1, name: "A" }];
const b = [{ id: 1, name: "A2" }];

const merged = a.concat(b);
// [{ id: 1, name: "A" }, { id: 1, name: "A2" }]
JavaScript

「id が同じなら 1 つにまとめたい」という場合は、
「結合 → キーでユニーク化」という別のユーティリティが必要になります。

function mergeByIdUnique(...arrays) {
  const merged = arrays.flat();
  const map = new Map();
  for (const item of merged) {
    if (!item || typeof item !== "object") continue;
    map.set(item.id, item);
  }
  return Array.from(map.values());
}
JavaScript

これはもう「結合ユーティリティ」というより「マージユーティリティ」なので、
用途に応じて名前を分けておくと、コードを読む人に優しいです。


実務で意識してほしい設計のポイント

「元の配列を壊さない」ことを前提にする

concat やスプレッド構文は、どちらも「元の配列を変更せず、新しい配列を返す」スタイルです。
業務コードでは、これを基本にしておくのがおすすめです。

Array.prototype.push による結合(a.push(...b))は、a を破壊的に変更します。

const a = [1, 2];
const b = [3, 4];

a.push(...b);
// a は [1, 2, 3, 4] に変わる
JavaScript

「意図的に破壊したい」場面以外では、concat やスプレッド構文を使うほうが安全です。

「null や単体要素」をどう扱うかを決める

実務では、「配列かもしれないし null かもしれない」「配列か単体オブジェクトか分からない」ということが頻繁に起こります。
そのたびに

const arr = value ? (Array.isArray(value) ? value : [value]) : [];
JavaScript

のようなコードを書くのはつらいので、toArray のようなユーティリティを用意しておくと、
結合処理がかなりスッキリします。

「結合だけ」か「結合+重複排除」かを明確に分ける

結合ユーティリティは、「重複を許す版」と「重複を消す版」を分けておくとよいです。

単純結合
mergeArraysconcat、スプレッド構文。
重複を消さない。順序をそのまま保つ。

結合+重複排除
mergeUnique のような関数。
ID リストやタグ一覧など、「同じものが複数あっても意味がない」場面で使う。

この 2 つを混ぜてしまうと、「なぜか要素が消えた」「なぜか重複している」といったバグにつながるので、
関数名と仕様でしっかり分けておくのが大事です。


少し手を動かして感覚をつかむ

コンソールで、次のようなコードを実際に打ってみてください。

const a = [1, 2];
const b = [3, 4];

a.concat(b);
[...a, ...b];

mergeArrays([1, 2], 3, null, [4, 5]);
mergeUnique([1, 2], [2, 3], [3, 4]);

const page1 = [{ id: 1 }, { id: 2 }];
const page2 = [{ id: 3 }, { id: 4 }];

mergeArrays(page1, page2);
JavaScript

「元の配列が壊れていないか」「重複がどう扱われているか」「null や単体要素がどう扱われているか」を、自分の目で確認してみてください。

そのうえで、自分のプロジェクトに

export function toArray(...) { ... }
export function mergeArrays(...) { ... }
export function mergeUnique(...) { ... }
JavaScript

のような関数を置き、

「配列を結合したくなったら、必ずこの“結合ユーティリティ”を通す」

というルールを作ってみてください。
そうすると、あなたのコードは、「場当たり的な concat や push」から、「意図がはっきりした安全な結合処理」に一段ステップアップしていきます。

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