Here is a more-streamlined version of silicakes' approach.
The following function(s) can parse a querystring from either a USVString
or Location
.
/**
* Returns a plain object representation of a URLSearchParams object.
* @param {USVString} search - A URL querystring
* @return {Object} a key-value pair object from a URL querystring
*/
const parseSearch = (search) =>
[...new URLSearchParams(search).entries()]
.reduce((acc, [key, val]) => ({
...acc,
// eslint-disable-next-line no-nested-ternary
[key]: Object.prototype.hasOwnProperty.call(acc, key)
? Array.isArray(acc[key])
? [...acc[key], val]
: [acc[key], val]
: val
}), {});
/**
* Returns a plain object representation of a URLSearchParams object.
* @param {Location} location - Either a document or window location, or React useLocation()
* @return {Object} a key-value pair object from a URL querystring
*/
const parseLocationSearch = (location) => parseSearch(location.search);
console.log(parseSearch('?foo=bar&x=y&ids=%5B1%2C2%2C3%5D&ids=%5B4%2C5%2C6%5D'));
_x000D_
.as-console-wrapper { top: 0; max-height: 100% !important; }
_x000D_
Here is a one-liner of the code above (125 bytes):
Where
f
isparseSearch
f=s=>[...new URLSearchParams(s).entries()].reduce((a,[k,v])=>({...a,[k]:a[k]?Array.isArray(a[k])?[...a[k],v]:[a[k],v]:v}),{})
Here is a method of serializing and updating:
const parseSearch = (search) =>
[...new URLSearchParams(search).entries()]
.reduce((acc, [key, val]) => ({
...acc,
// eslint-disable-next-line no-nested-ternary
[key]: Object.prototype.hasOwnProperty.call(acc, key)
? Array.isArray(acc[key])
? [...acc[key], val]
: [acc[key], val]
: val
}), {});
const toQueryString = (params) =>
`?${Object.entries(params)
.flatMap(([key, values]) =>
Array.isArray(values)
? values.map(value => [key, value])
: [[key, values]])
.map(pair => pair.map(val => encodeURIComponent(val)).join('='))
.join('&')}`;
const updateQueryString = (search, update) =>
(parsed =>
toQueryString(update instanceof Function
? update(parsed)
: { ...parsed, ...update }))
(parseSearch(search));
const queryString = '?foo=bar&x=y&ids=%5B1%2C2%2C3%5D&ids=%5B4%2C5%2C6%5D';
const parsedQuery = parseSearch(queryString);
console.log(parsedQuery);
console.log(toQueryString(parsedQuery) === queryString);
const updatedQuerySimple = updateQueryString(queryString, {
foo: 'baz',
x: 'z',
});
console.log(updatedQuerySimple);
console.log(parseSearch(updatedQuerySimple));
const updatedQuery = updateQueryString(updatedQuerySimple, parsed => ({
...parsed,
ids: [
...parsed.ids,
JSON.stringify([7,8,9])
]
}));
console.log(updatedQuery);
console.log(parseSearch(updatedQuery));
_x000D_
.as-console-wrapper { top: 0; max-height: 100% !important; }
_x000D_