Gunnar Bittersmann: URLSearchParams filtern

Hi, ich möchte aus URLSearchParams nur diejenigen Einträge haben, deren Keys mit einem bestimmten Präfix beginnen.

const url = "https://example.net?a=1&b=2&c=3&pre_d=4&pre_e=5";

const urlObject = new URL(url);
const searchParams = urlObject.searchParams;

searchParams.forEach((value, key) => {
	console.debug("key", key);
	if (!key.startsWith("pre_")) {
		searchParams.delete(key);
	}
});

console.debug("result", searchParams.toString());

// console output:
// "key" "a"
// "key" "c"
// "key" "pre_e"
// "result" "b=2&pre_d=4&pre_e=5"

(Codepen)

In der Schleife werden gar nicht alle Keys geprüft; b und pre_d fehlen.

Warum ist das so und was muss ich dagegen tun?

Jolan tru

--
Wenn der Faschismus wiederkehrt, wird er nicht sagen „Hallo, ich bin der Faschismus.“ Er wird sagen: „Ich suche in diesem Deutschen Bundestag keine anderen Mehrheiten als die in der demokratischen Mitte. Wenn es heute eine solche Mehrheit gegeben hat, bedauere ich das.“ (Friedrich Merz)

akzeptierte Antworten

  1. Hallo,

    weil du mit forEach immer direkt am Object arbeitest. Ich habe es mir inzwischen abgewöhnt, mit forEach zu arbeiten, da man schnell den Überblick verliert, ob und wie es die nachfolgende Iteration beeinflusst.

    Um sicherzustellen, dass alle keys durchlaufen werden, erstelle vorher ein Array mit den keys. Zum Beispiel:

    const url = 'https://example.net?a=1&b=2&c=3&pre_d=4&pre_e=5';
    
    const urlObject = new URL(url);
    const searchParams = urlObject.searchParams;
    
    [...searchParams].map(([key,]) => {
    	console.debug('key', key);
      if (!key.startsWith('pre_')) {
    		searchParams.delete(key);
    	}
    });
    
    console.debug('result', searchParams.toString());
    

    Gruß Michael

    1. Hallo Michael_K,

      ja, das dachte ich mir auch. Never Modify A Collection White Iterating It!

      Da die searchParams key/value-iterierbar sind, könnte man als generische Lösung einen Generator erstellen und ihn als Quelle für eine Map verwenden:

      function *selectPrefix(keyValueIterable, prefix) {
          for (const entry of keyValueIterable) {
              if (entry[0].startsWith(prefix)) {
                  yield entry;
              }
          }
      }
      
      const relevantParams = new Map(
         selectPrefix(url.searchParams, "pre_")
      );
      

      Aber sicherlich war das nur das Programmierquiz zum Wochenende, oder Gunnar? 😉

      Rolf

      --
      sumpsi - posui - obstruxi
      1. @@Rolf B

        Da die searchParams key/value-iterierbar sind, könnte man als generische Lösung einen Generator erstellen und ihn als Quelle für eine Map verwenden:

        Wenn ich das noch öfter brauchen sollte, werde ich drauf zurückkommen.

        Aber sicherlich war das nur das Programmierquiz zum Wochenende, oder Gunnar? 😉

        Leider nicht.

        Jolan tru

        --
        Wenn der Faschismus wiederkehrt, wird er nicht sagen „Hallo, ich bin der Faschismus.“ Er wird sagen: „Ich suche in diesem Deutschen Bundestag keine anderen Mehrheiten als die in der demokratischen Mitte. Wenn es heute eine solche Mehrheit gegeben hat, bedauere ich das.“ (Friedrich Merz)
      2. Generatoren bieten sich vor allem dann an, wenn das zu iterierende Objekt „groß” wird.

        Für das Beispiel hier geht es auch einfacher:

        const url = 'https://example.net?a=1&b=2&c=3&pre_d=4&pre_e=5';
        
        const urlObject = new URL(url);
        const searchParams = urlObject.searchParams;
        const results = Array.from(searchParams).filter(([key]) => key.startsWith('pre_'));
        
        console.log('result', results);
        

        Dabei habe ich aus dem URLSearchParams erst einmal ein Array gemacht (Array.from()), welches anschließend gefiltert wurde. [key] nutzt dabei destructuring assignment zum Auspacken (ohne auf ein Rest-Parameter einzugehen).

        Das Ergebnis wird wie im Beispiel ausgegeben.

        Persönlich würde ich es wohl noch in eine Funktion geben, die die url und das Präfix entgegennimmt und passende Query-Parameter zurückgibt:

        const url = 'https://example.net?a=1&b=2&c=3&pre_d=4&pre_e=5';
        const filteredSearchParams = filterUrlSearchParamsByPrefix(url, 'pre_');
        console.log(filteredSearchParams.toString());
        
        function filterUrlSearchParamsByPrefix(url, prefix) {
          const urlObject = new URL(url);
          const results = Array
            .from(urlObject.searchParams)
            .filter(([key]) => key.startsWith(prefix));
          return new URLSearchParams(results);
        }
        
        
    2. @@Michael_K

      weil du mit forEach immer direkt am Object arbeitest. Ich habe es mir inzwischen abgewöhnt, mit forEach zu arbeiten, da man schnell den Überblick verliert, ob und wie es die nachfolgende Iteration beeinflusst.

      Ja, sowas dachte ich mir schon.

      Um sicherzustellen, dass alle keys durchlaufen werden, erstelle vorher ein Array mit den keys. Zum Beispiel:

      Danke, das funzt.

      Jolan tru

      --
      Wenn der Faschismus wiederkehrt, wird er nicht sagen „Hallo, ich bin der Faschismus.“ Er wird sagen: „Ich suche in diesem Deutschen Bundestag keine anderen Mehrheiten als die in der demokratischen Mitte. Wenn es heute eine solche Mehrheit gegeben hat, bedauere ich das.“ (Friedrich Merz)