Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

URLSearchParams with multiple values

Tags:

javascript

I have multiples URLSearchParams created from object mainly because it's lighter to write and to read but for some of them, I need multiple values for a "variable" like this : foo=bar&foo=baz.
For now I do it with .append but it's heavy to read having multiple lines pretty identical.
Is there a way to do it from the constructor, with an object ?

let params;

// Currently used (and working) code
params = new URLSearchParams()
params.append("foo", "bar");
params.append("foo", "baz");

console.log(params.toString()); //Wanted result but heavy to read with more values

// Wanted code (but not the wanted result for now)

params = new URLSearchParams({
    "foo": ["bar", "baz"]
});

console.log(params.toString());

params = new URLSearchParams({
    "foo": "bar",
    "foo": "baz"
});

console.log(params.toString());
like image 476
Marius ROBERT Avatar asked Jan 25 '26 08:01

Marius ROBERT


2 Answers

The URLSearchParams can takes an init value as argument for its constructor containing the following:

One of:

  • A string, which will be parsed from application/x-www-form-urlencoded format. A leading '?' character is ignored.

  • A literal sequence of name-value string pairs, or any object — such as a FormData object — with an iterator that produces a sequence of string pairs. Note that File entries will be serialized as [object File] rather than as their filename (as they would in an application/x-www-form-urlencoded form).

  • A record of string keys and string values.

In your case the second one seems to work the best and can be done like this:

const searchParams = new URLSearchParams([['foo', 'bar'],['foo', 'baz'],['foo', 'qux']])

console.log(searchParams.toString())

If you want to deal with objects, you can create your own structure and use a function to create the wanted data

For example :

const params = [
  {name: "foo", values: ["bar", "baz", "qux"]},
  {name: "bar", values: ["foo", "foo2", "foo3"]},
]

const initParams = (params) => params.reduce((acc, curr) => {
  const arr = curr.values.map(x => [curr.name, x])
  return acc.concat(arr)
}, [])

const searchParams = new URLSearchParams(initParams(params))

console.log(searchParams.toString())
like image 176
RenaudC5 Avatar answered Jan 26 '26 22:01

RenaudC5


I would introduce a function to build the thing you need (URLSearchParams) from the structure you want (an object containing strings or arrays of strings), pushing the "heavy" details down below an interface you own. A simple implementation would just use .append as appropriate:

// For TypeScript (e.g. Angular) users:
// function createSearchParams(params: { [key: string]: string | string[] }): URLSearchParams {
function createSearchParams(params) {
  const searchParams = new URLSearchParams();
  Object.entries(params).forEach(([key, values]) => {
    if (Array.isArray(values)) {
      values.forEach((value) => {
        searchParams.append(key, value);
      });
    } else {
      searchParams.append(key, values);
    }
  });
  return searchParams;
}

console.log(createSearchParams({
  foo: ["bar", "baz"],
  hello: "world",
}).toString());

This provides an abstraction, so if you decide you'd rather use the init approach under the hood, you can do so, e.g.:

function createSearchParams(params) {
  return new URLSearchParams(Object.entries(params).flatMap(([key, values]) => Array.isArray(values) ? values.map((value) => [key, value]) : [[key, values]]));

}

console.log(createSearchParams({
  foo: ["bar", "baz"],
  hello: "world",
}).toString());

All of your tests will continue to pass, you don't have to change the consuming code. This is much neater than defining a function to convert the structure you want to the thing URLSearchParams needs, as shown in RenaudC5's answer.

like image 27
jonrsharpe Avatar answered Jan 26 '26 22:01

jonrsharpe



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!