JavaScript Tips | 配列ユーティリティ:並び順復元

JavaScript JavaScript
スポンサーリンク

テーマの整理:「並び順復元」とは何か

「並び順復元」は、以前保存しておいた“並び順情報”を使って、最新の配列を同じ順番に並べ直すユーティリティのことです。 直前の「並び順保存」が「順番を覚える」だとしたら、 並び順復元は「覚えていた順番を、今のデータにもう一度適用する」役割です。

業務だと、こんな流れで使われます。

  1. ユーザーが画面で並び替える
  2. 並び順(ID の配列)を保存しておく
  3. 次回表示時に最新データを取得する
  4. 保存しておいた並び順を使って、最新データを並び替える(=並び順復元)

ここでの主役が「並び順復元」ユーティリティです。

基本コンセプト:「ID の並びを、最新配列に“適用する”」

並び順復元でやりたいこと

例えば、こういう状況を考えます。

// 前回、ユーザーが並び替えた結果から保存しておいた並び順
const savedOrder = [2, 1];

// 今回 API から取ってきた最新ユーザー一覧
const latestUsers = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob"   },
  { id: 3, name: "Carol" },
];
JavaScript

やりたいことはシンプルで、

「latestUsers を、savedOrder の順番に並べ直したい」 つまり、こうしたいわけです。

[
  { id: 2, name: "Bob"   }, // savedOrder の 2
  { id: 1, name: "Alice" }, // savedOrder の 1
  { id: 3, name: "Carol" }, // savedOrder にないので最後に追加
]
JavaScript

この「savedOrder を latestUsers に適用する」処理が、 並び順復元ユーティリティの中身になります。

ユーティリティの中核:applyOrder(並び順復元)

実装:applyOrder

並び順復元の本体となる関数を、改めて整理して書きます。

function applyOrder(records, order, idField = "id") {
  if (!Array.isArray(records) || records.length === 0) {
    return [];
  }
  if (!Array.isArray(order) || order.length === 0) {
    return records.slice(); // 並び順がなければそのまま返す
  }

  const map = new Map();
  records.forEach((r) => {
    map.set(r[idField], r);
  });

  const ordered = [];

  order.forEach((id) => {
    const item = map.get(id);
    if (item) {
      ordered.push(item);
    }
  });

  const usedIds = new Set(order);
  records.forEach((r) => {
    if (!usedIds.has(r[idField])) {
      ordered.push(r);
    }
  });

  return ordered;
}
JavaScript

重要ポイントをかみ砕いて説明

H3: なぜ Map を使うのか

records をそのままループして、order の順番に探してもいいのですが、 それだと「ネストしたループ」になってしまい、件数が増えると遅くなります。

そこで、

const map = new Map();
records.forEach((r) => {
  map.set(r[idField], r);
});
JavaScript

とすることで、 「id → レコード」を一発で引ける辞書を作っています。

これで、

order.forEach((id) => {
  const item = map.get(id);
  ...
});
JavaScript

と書くだけで、 savedOrder の順番に従ってレコードを素早く取り出せます。

H3: なぜ「order にない ID を最後に追加する」のか

並び順復元のとき、savedOrder に含まれていない ID が出てくるのは普通です。

  • 新しく追加されたレコード
  • 前回は存在しなかったデータ

これらを無視してしまうと、「最新データが画面に出てこない」という事故になります。

そこで、

const usedIds = new Set(order);
records.forEach((r) => {
  if (!usedIds.has(r[idField])) {
    ordered.push(r);
  }
});
JavaScript

とすることで、

  • savedOrder に含まれている ID は、その順番で並べる
  • savedOrder に含まれていない ID は、最後に追加する

という自然な挙動を実現しています。

H3: 並び順がないときは「そのまま返す」

並び順がまだ保存されていないユーザーや画面では、 savedOrder が空配列になります。

そのときに、

if (!Array.isArray(order) || order.length === 0) {
  return records.slice();
}
JavaScript

として、「元の順番のまま返す」ようにしておくと、 呼び出し側は「並び順があるかどうか」を意識せずに使えます。

例題 1:保存済みの並び順を最新データに復元する

コード例

const savedOrder = [2, 1];

const latestUsers = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob"   },
  { id: 3, name: "Carol" },
];

const orderedUsers = applyOrder(latestUsers, savedOrder);

console.log(orderedUsers);
JavaScript

結果のイメージ

出力はこうなります。

[
  { id: 2, name: "Bob"   }, // savedOrder の 2
  { id: 1, name: "Alice" }, // savedOrder の 1
  { id: 3, name: "Carol" }, // 新規追加分
]
JavaScript

ここでのポイントは、

  • 「ユーザーが前回決めた並び順」がちゃんと再現されている
  • 新しく追加された Carol も、ちゃんと一覧に出ている

というところです。

例題 2:並び順が存在しない場合の挙動を確認する

コード例

const noOrder = []; // まだ並び順を保存していない

const latestUsers = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob"   },
];

const orderedUsers = applyOrder(latestUsers, noOrder);

console.log(orderedUsers);
JavaScript

結果のイメージ

出力はこうなります。

[
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob"   },
]
JavaScript

つまり、「元の順番のまま」です。

ここでのポイントは、

  • 並び順がないときでも、関数が安全に動く
  • 呼び出し側は「並び順があるかどうか」を気にせず applyOrder を呼べる

という設計になっていることです。

並び順保存とのセット運用:保存 → 復元の流れ

H3: 並び順保存(extractOrder)と並び順復元(applyOrder)を組み合わせる

並び順復元は、並び順保存とセットで使うと威力を発揮します。

保存側(前回表示時):

function extractOrder(records, idField = "id") {
  if (!Array.isArray(records) || records.length === 0) {
    return [];
  }
  return records.map((r) => r[idField]);
}

const reorderedUsers = [
  { id: 2, name: "Bob"   },
  { id: 1, name: "Alice" },
];

const savedOrder = extractOrder(reorderedUsers);
// savedOrder を localStorage や DB に保存しておく
JavaScript

復元側(次回表示時):

// latestUsers は API などから取得した最新データ
const latestUsers = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob"   },
  { id: 3, name: "Carol" },
];

// savedOrder は保存しておいた並び順(ここでは手動で用意)
const savedOrder = [2, 1];

const orderedUsers = applyOrder(latestUsers, savedOrder);

console.log(orderedUsers);
JavaScript

この「保存 → 復元」のペアが揃うことで、 ユーザーの「並べ方の好み」を、アプリがちゃんと覚えて再現できるようになります。

並び順復元ユーティリティで意識してほしい重要ポイント

「並び順は、最新データに対して“適用するもの”」という感覚

並び順復元の本質は、 「並び順そのものを保存しておくのではなく、最新データに対して並び順を適用する」という考え方です。

だからこそ、

  • 並び順は「ID の配列」として保存する
  • 復元時に「ID → レコード」のマップを作って適用する
  • 新しいレコードもちゃんと一覧に残す

という設計が効いてきます。

「保存済みの並び順」と「最新データ」の差分をどう扱うかを決める

並び順復元では、必ず「差分」が発生します。

  • savedOrder にあるけど、最新データにない ID(削除されたレコード)
  • savedOrder にないけど、最新データにある ID(新規追加されたレコード)

今回の実装では、

  • 削除されたレコードは無視する(map にないので ordered に入らない)
  • 新規追加されたレコードは最後に追加する

というルールにしています。

このルールを明確にしておくと、 ユーザーにとって「自然な挙動」になり、 バグも減ります。

手を動かして「並び順復元」の感覚をつかむ

最後に、保存と復元をまとめたデモコードを載せます。 これを実行すると、「並び替え → 並び順保存 → 並び順復元」の流れが一気に体感できます。

function extractOrder(records, idField = "id") {
  if (!Array.isArray(records) || records.length === 0) {
    return [];
  }
  return records.map((r) => r[idField]);
}

function applyOrder(records, order, idField = "id") {
  if (!Array.isArray(records) || records.length === 0) {
    return [];
  }
  if (!Array.isArray(order) || order.length === 0) {
    return records.slice();
  }

  const map = new Map();
  records.forEach((r) => {
    map.set(r[idField], r);
  });

  const ordered = [];
  order.forEach((id) => {
    const item = map.get(id);
    if (item) ordered.push(item);
  });

  const usedIds = new Set(order);
  records.forEach((r) => {
    if (!usedIds.has(r[idField])) {
      ordered.push(r);
    }
  });

  return ordered;
}

function demo() {
  const originalUsers = [
    { id: 1, name: "Alice" },
    { id: 2, name: "Bob"   },
    { id: 3, name: "Carol" },
  ];

  const reorderedUsers = [
    { id: 3, name: "Carol" },
    { id: 1, name: "Alice" },
    { id: 2, name: "Bob"   },
  ];

  const savedOrder = extractOrder(reorderedUsers);
  console.log("savedOrder:", savedOrder);

  const latestUsers = [
    { id: 1, name: "Alice" },
    { id: 2, name: "Bob"   },
    { id: 3, name: "Carol" },
    { id: 4, name: "Dave"  },
  ];

  const restored = applyOrder(latestUsers, savedOrder);
  console.log("restored:", restored);
}

demo();
JavaScript

まとめ:並び順復元ユーティリティで「覚えていた並び方を、今の配列にもう一度宿す」

並び順復元は、 「保存しておいた並び順(ID の並び)を、最新の配列に適用して、ユーザーの“並べ方の好み”を再現するためのユーティリティ」です。

プロジェクトでは、例えばこうまとめておくと扱いやすいです。

export function extractOrder(records, idField = "id") { ... }
export function applyOrder(records, order, idField = "id") { ... }
JavaScript

そして、

  • 並び替え操作の直後(extractOrder で保存)
  • 画面表示の直前(applyOrder で復元)

という 2 箇所で必ずこのユーティリティを通す。

そうすると、 アプリは「データの中身」だけでなく、「ユーザーがどう並べたいか」まで覚えていてくれるようになります。

並び順保存が“記憶”、並び順復元が“再生”。 その再生を担うのが、この並び順復元ユーティリティです。

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