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());
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-urlencodedformat. A leading'?'character is ignored.A literal sequence of name-value string pairs, or any object — such as a
FormDataobject — with an iterator that produces a sequence of string pairs. Note thatFileentries will be serialized as[object File]rather than as their filename (as they would in anapplication/x-www-form-urlencodedform).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())
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With