テーマの整理:「並び順復元」とは何か
「並び順復元」は、以前保存しておいた“並び順情報”を使って、最新の配列を同じ順番に並べ直すユーティリティのことです。 直前の「並び順保存」が「順番を覚える」だとしたら、 並び順復元は「覚えていた順番を、今のデータにもう一度適用する」役割です。
業務だと、こんな流れで使われます。
- ユーザーが画面で並び替える
- 並び順(ID の配列)を保存しておく
- 次回表示時に最新データを取得する
- 保存しておいた並び順を使って、最新データを並び替える(=並び順復元)
ここでの主役が「並び順復元」ユーティリティです。
基本コンセプト:「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 箇所で必ずこのユーティリティを通す。
そうすると、 アプリは「データの中身」だけでなく、「ユーザーがどう並べたいか」まで覚えていてくれるようになります。
並び順保存が“記憶”、並び順復元が“再生”。 その再生を担うのが、この並び順復元ユーティリティです。
