6

When processing searchParams, extra = that is added for empty values, results in the wrong URL.
Is there a method to prevent unnecessary = when processing searchParams?

N.B. It would be possible to remove the extra = with RegExp() but that purpose of the question is to achieve it natively with searchParams.

const u = new URL('https://example.com/?4FEE63D94&foo=1');
params = u.searchParams;

params.delete('foo');         // Delete the foo parameter
console.log(u.href);          // https://example.com/?4FEE63D94=

Update

URLSearchParams methods seem to alter URL.
URLSearchParams.entries() & URLSearchParams.forEach() & URLSearchParams.get() replaces + with space (%20).

const u = new URL('https://example.com/?4FEE63D94&foo=1&bar=aaa+bbb+ccc');
let params = u.searchParams;

params.delete('foo');         // Delete the foo parameter
console.log(u.href);          // https://example.com/?4FEE63D94=&bar=aaa+bbb+ccc

u.search = [...params.entries()].map(([key, value]) =>  value ? `${key}=${value}` : key).join('&');
console.log(u.href);          // https://example.com/?4FEE63D94&bar=aaa%20bbb%20ccc

Even params.delete() alters URL. (auth,client to auth%2Cclient)
Note: Run code snippet doesn't show it

const url = 'https://example.com/?4FEE63D94&scope=auth,client&foo=1&bar=aaa+bbb+ccc';
const u = new URL(url);
let params = u.searchParams;
params.delete['foo'];
console.log(u.href);          // https://example.com/?4FEE63D94=&scope=auth%2Cclient&bar=aaa+bbb+ccc

Workaround: Removing unnecessary =

As per suggestion by @Bergi

const u = new URL('https://example.com?4FEE63D94&foo=1&bar=aaa+bbb+ccc');
let params = u.searchParams;

params.delete('foo');         // Delete the foo parameter
console.log(u.href);          // https://example.com/?4FEE63D94=&bar=aaa+bbb+ccc

// URL() params.delete() adds = to all params
u.search = u.search.replace(/=(?=&|$)/g, '');

console.log(u.search);        // ?4FEE63D94&bar=aaa+bbb+ccc
console.log(u.href);          // https://example.com/?4FEE63D94&bar=aaa+bbb+ccc

Performace test

Manual processing vs URLSearchParams

let t;
const n = 100000;

const url = 'https://example.com/?4FEE63D94&scope=auth,client&foo=1&bar=aaa+bbb+ccc';
const u = new URL(url);

t = performance.now();
for (let i = 0; i < n; i++) {
  let params = Object.fromEntries(u.search.substring(1).split('&').map(p => p.split('=')));
  delete params['foo'];
  u.search = Object.entries(params).map(([key, value]) => value ? `${key}=${value}` : key).join('&');
}
console.log(`Operation took ${performance.now() - t} milliseconds`); // 731 millisecond

t = performance.now();
for (let i = 0; i < n; i++) {
  let params = u.searchParams;
  params.delete('foo');
}
console.log(`Operation took ${performance.now() - t} milliseconds`); // 208 millisecond
erosman
  • 7,094
  • 7
  • 27
  • 46
  • If you are open to Libraries - Try https://github.com/unjs/ufo – Adarsh Madrecha Apr 19 '22 at 07:54
  • 2
    `https://example.com/?4FEE63D94&foo=1` this is not a valid url with query params. What are you trying to reach? – Christian Vincenzo Traina Apr 19 '22 at 07:59
  • 1
    @CristianTraìna Why is that not valid? – Álvaro González Apr 19 '22 at 08:01
  • 2
    Because query params are used to store keys and values. The pattern is: `?key1=value1&key2=value2&...`. So the value is always required, and you cannot remove it. However, you can use an empty string as a value, in this case it would become `foo=&bar=2`. In your case, the `URL` class is noticing your url is malformed and attempting to fix it – Christian Vincenzo Traina Apr 19 '22 at 08:06
  • 4
    [Is a url query parameter valid if it has no value?](https://stackoverflow.com/questions/4557387/is-a-url-query-parameter-valid-if-it-has-no-value#:~:text=Yes%2C%20it%20is%20valid.,one%20way%20to%20do%20so.&text=Show%20activity%20on%20this%20post.,-%22The%20%22http%22) – pilchard Apr 19 '22 at 08:06
  • Ok, my bad, they're strong arguments. Apparently the `URL` class thinks it needs an equal :) – Christian Vincenzo Traina Apr 19 '22 at 08:08
  • It annoys me too but I think that's just how the feature is designed. I don't think there's a way around string manipulation. – Álvaro González Apr 19 '22 at 08:10
  • I gave the regex solution but seemingly the author does not prefer the regex way. I guess the author is finding a native function usage solution – Nick Vu Apr 19 '22 at 08:12
  • `searchParams` is slightly different from queryString, parameters are expected to be key value pairs, a queryString can be almost what ever. – Teemu Apr 19 '22 at 08:13
  • "*results in the wrong URL*" - how is that URL "wrong"? Any system accepting such an URL should not differentiate between an empty value and no value. – Bergi Apr 21 '22 at 16:05
  • Btw the `params.entries()` attempt has a more serious problem than just not escaping spaces: it doesn't escape *anything* - you'd need to use `encodeURIComponent(key) + '=' + encodeURIComponent(value)` – Bergi Apr 21 '22 at 16:07
  • 2
    "*It would be possible to remove the extra `=` with RegExp but that not desired (defeats the purpose)*" - not sure what you call "purpose"? I can't see anything wrong with regex here, using `url.search.replace(/=(?=&|$)/g, '')` would be much simpler than your `split`+`map`+`join` solution – Bergi Apr 21 '22 at 16:11
  • @Bergi The 'purpose' is to use `searchParams` ;) – erosman Apr 21 '22 at 17:57
  • 1
    @erosman Yes, you use `url.searchParams.delete('foo')`, then `url.searchParams.toString().replace(…)` - or just `url.search` instead of `url.searchParams.toString()`, doesn't really make a difference. – Bergi Apr 21 '22 at 18:16
  • @Bergi Some servers do not accept `value=` for some reason. – erosman Apr 21 '22 at 18:24
  • @Bergi An example: `http://shareplace.org/?4FEE63D94=` vs `http://shareplace.org/?4FEE63D94` – erosman Apr 21 '22 at 18:43
  • @erosman Oh that's a weird one. Imo they're abusing the query string. They better use `http://shareplace.org/?id=4FEE63D94` or just `http://shareplace.org/share/4FEE63D94`, but it seems like they optimise for brevity. If you're dealing with such an url, you can only use `url.search`, not `url.searchParams` - since it doesn't make use of parameters. – Bergi Apr 21 '22 at 18:48
  • @Bergi That server is not the only example. Actually, I am removing some parameters as part of an addon feature. The resulted URL ended up with extra `=` which lead to bug reports that some URLs became unreachable. Therefore, I had to look for a way to remove the extra `=`. – erosman Apr 21 '22 at 18:58
  • It seems you're trying to provide an answer in the question itself. Can you please post the information using the "Answer Your Question" button? – Álvaro González Apr 22 '22 at 07:20
  • @ÁlvaroGonzález TBH, I was looking for a native URLSearchParams method to achieve that. What I have posted are all workarounds. – erosman Apr 22 '22 at 08:48

1 Answers1

2

The URL class understands search params to be key-value pairs, as mentioned in one of the comments. You need need think about what FEE63D94 is: a key or value?

If it's a key, then it would have an empty value, as in FEE63D94=

If it's a value, then find an appropriate key for it, key=FEE63D94

The other alternative is to have it be part of the path, https://example.com/4FEE63D94?foo=1

TheMonarch
  • 397
  • 4
  • 11
  • I am not in control of the URL. `params.delete()` adds `=` and some servers do not accepts it for some reason. – erosman Apr 21 '22 at 19:01