Reputation: 505
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());
Upvotes: 11
Views: 22741
Reputation: 9762
You can create multiple params like this
const params = new URLSearchParams({a:1,b:2,c:3});
console.log(params.toString());
Or you can feed a single string with &
to the constructor like this new URLSearchParams("a=1&b=2&c=3")
When you apply these params to an URL-object you could overwrite all params with url.search =
. But if the url already has a param, it's better to not overwrite it and instead loop through your new UrlSearchParams object and add each param separately with url.searchParams.append
const params = new URLSearchParams("a=1&b=2&c=3");
console.log(params);
//You could apply them all to an url
const url = new URL("https://example.com");
url.search = params.toString();
console.log(url.toString())
//But if your url already contains a param, you gotta append new params separately
const url2 = new URL("https://example.com?x=1");
params.forEach((value, key) => { url2.searchParams.append(key, value); });
console.log(url2.toString())
Upvotes: 0
Reputation: 21
First process your data with the parseParam function
function parseParam(oldParams, newParams = {}, field = '') {
for (const key in oldParams) {
if (oldParams[key] !== null) {
let field3 = field.length > 0 ? field + '[' + key + ']' : key ;
switch (typeof oldParams[key]) {
case 'object':
let object = parseParam(oldParams[key], newParams, field3);
newParams = Object.assign(newParams, object);
break;
case 'string':
case 'number':
newParams[field3] = oldParams[key];
break;
}
}
}
return newParams;
}
let query = {
foo: ['bar', 'baz'],
status: 3,
products: {
door: [4, 8],
tile: 'not'
}
};
const urlParams = new URLSearchParams(parseParam(query));
console.log(urlParams.toString());
// foo[0]=bar&foo[1]=baz&status=3&products[door][0]=4&products[door][1]=8&products[tile]=not
Upvotes: 1
Reputation: 161
console.log('' + new URLSearchParams(['bar', 'baz', 'qux'].map(v=>['foo', v])))
Upvotes: 1
Reputation: 122026
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.
Upvotes: 3
Reputation: 3829
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 thatFile
entries will be serialized as[object File]
rather than as their filename (as they would in anapplication/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())
Upvotes: 15