JavaScript Tips | 文字列ユーティリティ:URL 系 - クエリ削除

JavaScript JavaScript
スポンサーリンク

「クエリ削除」で本当にやりたいこと

「クエリ削除」は、URL の ? 以降に付いているパラメータのうち、いらないものだけを取り除いて、新しい URL を作り直す処理です。

例えば、こんなことをやりたい場面があります。

  • 一度だけ使う token パラメータを、処理後に URL から消したい
  • page だけリセットして、他の検索条件は残したい
  • debug=true みたいな一時的なフラグを URL から取り除きたい

つまり、「URL 全体を文字列としてゴリゴリ書き換える」のではなく、
クエリを“構造として”扱って、特定のキーだけ消して再組み立てするユーティリティを作る、という話です。


全体の流れをざっくり分解する

何をどう分けて考えるか

クエリ削除は、次の 3 ステップに分解できます。

  1. URL を「ベース部分」と「クエリ部分」に分ける
    例:/search?q=js&page=2 → ベース /search、クエリ ?q=js&page=2
  2. クエリ部分をパースして、オブジェクトにする
    例:?q=js&page=2{ q: "js", page: "2" }
  3. 消したいキーを取り除いて、クエリを再生成し、ベースとくっつける

この「分解 → 編集 → 再組み立て」を毎回手でやるのは面倒なので、
ユーティリティに閉じ込めてしまいます。


基本の部品:クエリのパースと生成

すでに持っていると便利な 2 つ

ここまでの流れを前提に、クエリ削除の前に「土台」となる 2 つの関数をおさらいしておきます。

クエリ文字列 → オブジェクト

function parseQueryString(qs) {
  const result = {};

  if (!qs) return result;

  const query = qs.startsWith("?") ? qs.slice(1) : qs;
  if (query === "") return result;

  const pairs = query.split("&");

  for (const pair of pairs) {
    if (!pair) continue;

    const [rawKey, rawValue = ""] = pair.split("=", 2);

    const key = decodeURIComponent(rawKey);
    const value = decodeURIComponent(rawValue);

    result[key] = value;
  }

  return result;
}
JavaScript

オブジェクト → クエリ文字列

function toQueryString(params) {
  if (!params || typeof params !== "object") {
    return "";
  }

  const parts = [];

  for (const [key, value] of Object.entries(params)) {
    if (value == null) continue;

    const encodedKey = encodeURIComponent(key);
    const encodedValue = encodeURIComponent(String(value));

    parts.push(`${encodedKey}=${encodedValue}`);
  }

  if (parts.length === 0) {
    return "";
  }

  return "?" + parts.join("&");
}
JavaScript

この 2 つがあれば、「クエリを構造として扱う」準備は整っています。


URL から特定のクエリを削除するユーティリティ

removeQueryParams の実装

ここからが本題です。
「URL 文字列」と「消したいキーの配列」を受け取って、新しい URL を返す関数を作ります。

function removeQueryParams(url, keysToRemove) {
  if (!url) return "";

  const str = String(url);

  const [base, qsAndHash = ""] = str.split("?", 2);

  let qsPart = "";
  let hashPart = "";

  const hashIndex = qsAndHash.indexOf("#");
  if (hashIndex >= 0) {
    qsPart = qsAndHash.slice(0, hashIndex);
    hashPart = qsAndHash.slice(hashIndex); // "#" を含む
  } else {
    qsPart = qsAndHash;
  }

  const params = parseQueryString(qsPart);

  const removeSet = new Set(keysToRemove);

  for (const key of removeSet) {
    delete params[key];
  }

  const newQs = toQueryString(params);

  return base + newQs + hashPart;
}
JavaScript

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

ここ、少し長いですが、1 個ずつ整理します。

1. URL を「ベース」「クエリ」「ハッシュ」に分ける

const [base, qsAndHash = ""] = str.split("?", 2);
JavaScript

"?" で 2 つに分けて、

  • base/search など
  • qsAndHashq=js&page=2#section1 など

に分解しています。

さらに、# があるかどうかで分けます。

const hashIndex = qsAndHash.indexOf("#");
if (hashIndex >= 0) {
  qsPart = qsAndHash.slice(0, hashIndex);
  hashPart = qsAndHash.slice(hashIndex); // "#..." をそのまま保持
} else {
  qsPart = qsAndHash;
}
JavaScript

こうすることで、

  • クエリ部分:q=js&page=2
  • ハッシュ部分:#section1

のように分けて扱えます。

ハッシュ(#以降)はサーバーに送られない「フロント側だけの情報」なので、
クエリ削除では触らず、そのまま戻してあげるのが自然です。

2. クエリ部分をオブジェクトにする

const params = parseQueryString(qsPart);
JavaScript

さっきの parseQueryString を使って、
"q=js&page=2"{ q: "js", page: "2" }
のように変換します。

3. 削除したいキーを Set にして一気に消す

const removeSet = new Set(keysToRemove);

for (const key of removeSet) {
  delete params[key];
}
JavaScript

keysToRemove["page", "token"] のような配列を想定しています。

Set にしておくと、重複があっても 1 回だけ処理されるし、
「削除対象の一覧」としても扱いやすいです。

delete params[key] で、そのキーをオブジェクトから取り除きます。

4. 残ったパラメータでクエリを再生成する

const newQs = toQueryString(params);
return base + newQs + hashPart;
JavaScript

toQueryString が、残ったパラメータから "?q=js" のような文字列を作ってくれます。

クエリが空になった場合は "" を返すようにしてあるので、
base + "" + hashPart となり、? が残らないのもポイントです。


実際の使用例でイメージを固める

一度だけ使う token を削除する

const url = "https://example.com/reset-password?token=abc123&from=email";

const cleaned = removeQueryParams(url, ["token"]);
// "https://example.com/reset-password?from=email"
JavaScript

処理が終わったあとに、この cleanedhistory.replaceState などで URL に反映すれば、
「ブラウザのアドレスバーから token を消す」ことができます。

ページ番号だけリセットしたい

const url = "/search?q=JavaScript&page=3&sort=created_at";

const resetPageUrl = removeQueryParams(url, ["page"]);
// "/search?q=JavaScript&sort=created_at"
JavaScript

「検索条件はそのまま、ページだけ 1 ページ目に戻したい」という UI でよく使うパターンです。

ハッシュ付き URL でもちゃんと動く

const url = "/docs?page=2#section1";

const cleaned = removeQueryParams(url, ["page"]);
// "/docs#section1"
JavaScript

#section1 はそのまま残しつつ、page だけ消えているのが分かると思います。


設計として意識してほしいこと

「文字列置換」ではなく「構造として扱う」

クエリ削除で一番やりがちなのは、こういう書き方です。

url.replace("page=2", "");
JavaScript

これは一見動きますが、

  • page=20 の一部まで消してしまう
  • page=2 が途中にあると & の位置が崩れる
  • URL エンコードされた値に page=2 が含まれていたら誤爆する

など、地味に危険です。

だからこそ、

  • URL を「ベース」「クエリ」「ハッシュ」に分ける
  • クエリをパースしてオブジェクトにする
  • オブジェクトを編集してから再生成する

という「構造として扱う」アプローチが、業務レベルでは圧倒的に安全です。

「生成」「取得」「削除」をセットで揃える

ここまでで、

  • クエリ生成:toQueryString
  • クエリ取得:parseQueryString / getQueryParam
  • クエリ削除:removeQueryParams

という 3 兄弟が揃いました。

これらを 1 つのモジュール(queryUtils.ts みたいな)にまとめておくと、
URL 周りの処理がすべて「そこを見れば分かる」状態になります。


ちょっとだけ手を動かしてみる

コンソールで、次の順番で試してみてください。

removeQueryParams("/search?q=js&page=2&sort=created_at", ["page"]);
removeQueryParams("/search?q=js&page=2#top", ["page"]);
removeQueryParams("https://example.com/reset?token=abc&from=email", ["token"]);
JavaScript

「どのパラメータが消えて、?# がどう残るか」を、自分の目で確認してみてください。

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

export function parseQueryString(...) { ... }
export function toQueryString(...) { ... }
export function removeQueryParams(...) { ... }
JavaScript

を置いて、

  • クエリを読む → parseQueryString
  • クエリを作る → toQueryString
  • クエリを消す → removeQueryParams

という流れをルール化してみてください。

その瞬間、あなたの URL 周りのコードは、
場当たり的な文字列操作から、意図と安全性を両立した“業務レベルのクエリ操作ユーティリティ”に一段レベルアップします。

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