I have a string like this:
abc=foo&def=%5Basf%5D&xyz=5
How can I convert it into a JavaScript object like this?
{
abc: 'foo',
def: '[asf]',
xyz: 5
}
I have a string like this:
abc=foo&def=%5Basf%5D&xyz=5
How can I convert it into a JavaScript object like this?
{
abc: 'foo',
def: '[asf]',
xyz: 5
}
This edit improves and explains the answer based on the comments.
var search = location.search.substring(1);
JSON.parse('{"' + decodeURI(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"') + '"}')
Example
Parse abc=foo&def=%5Basf%5D&xyz=5 in five steps:
abc=foo","def=[asf]","xyz=5abc":"foo","def":"[asf]","xyz":"5{"abc":"foo","def":"[asf]","xyz":"5"}which is legal JSON.
An improved solution allows for more characters in the search string. It uses a reviver function for URI decoding:
var search = location.search.substring(1);
JSON.parse('{"' + search.replace(/&/g, '","').replace(/=/g,'":"') + '"}', function(key, value) { return key===""?value:decodeURIComponent(value) })
Example
search = "abc=foo&def=%5Basf%5D&xyz=5&foo=b%3Dar";
gives
Object {abc: "foo", def: "[asf]", xyz: "5", foo: "b=ar"}
A one-liner:
JSON.parse('{"' + decodeURI("abc=foo&def=%5Basf%5D&xyz=5".replace(/&/g, "\",\"").replace(/=/g,"\":\"")) + '"}')
Starting ES6 and on, Javascript offers several constructs in order to create a performant solution for this issue.
This includes using URLSearchParams and iterators
let params = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5');
params.get("abc"); // "foo"
Should your use case requires you to actually convert it to object, you can implement the following function:
function paramsToObject(entries) {
const result = {}
for(const [key, value] of entries) { // each 'entry' is a [key, value] tupple
result[key] = value;
}
return result;
}
const urlParams = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5');
const entries = urlParams.entries(); //returns an iterator of decoded [key,value] tuples
const params = paramsToObject(entries); //{abc:"foo",def:"[asf]",xyz:"5"}
We can use Object.fromEntries, replacing paramsToObject with Object.fromEntries(entries).
The value pairs to iterate over are the list name-value pairs with the key being the name and the value being the value.
Since URLParams, returns an iterable object, using the spread operator instead of calling .entries will also yield entries per its spec:
const urlParams = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5');
const params = Object.fromEntries(urlParams); // {abc: "foo", def: "[asf]", xyz: "5"}
Note: All values are automatically strings as per the URLSearchParams spec
As @siipe pointed out, strings containing multiple same-key values will be coerced into the last available value: foo=first_value&foo=second_value will in essence become: {foo: "second_value"}.
As per this answer: https://stackoverflow.com/a/1746566/1194694 there's no spec for deciding what to do with it and each framework can behave differently.
A common use case will be to join the two same values into an array, making the output object into:
{foo: ["first_value", "second_value"]}
This can be achieved with the following code:
const groupParamsByKey = (params) => [...params.entries()].reduce((acc, tuple) => {
// getting the key and value from each tuple
const [key, val] = tuple;
if(acc.hasOwnProperty(key)) {
// if the current key is already an array, we'll add the value to it
if(Array.isArray(acc[key])) {
acc[key] = [...acc[key], val]
} else {
// if it's not an array, but contains a value, we'll convert it into an array
// and add the current value to it
acc[key] = [acc[key], val];
}
} else {
// plain assignment if no special case is present
acc[key] = val;
}
return acc;
}, {});
const params = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5&def=dude');
const output = groupParamsByKey(params) // {abc: "foo", def: ["[asf]", "dude"], xyz: 5}
One liner. Clean and simple.
const params = Object.fromEntries(new URLSearchParams(location.search));
For your specific case, it would be:
const str = 'abc=foo&def=%5Basf%5D&xyz=5';
const params = Object.fromEntries(new URLSearchParams(str));
console.log(params);
2023 One-Liner Approach
For the general case where you want to parse query params to an object:
Object.fromEntries(new URLSearchParams(location.search));
For your specific case:
Object.fromEntries(new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5'));
Split on & to get name/value pairs, then split each pair on =. Here's an example:
var str = "abc=foo&def=%5Basf%5D&xy%5Bz=5"
var obj = str.split("&").reduce(function(prev, curr, i, arr) {
var p = curr.split("=");
prev[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
return prev;
}, {});
Another approach, using regular expressions:
var obj = {};
str.replace(/([^=&]+)=([^&]*)/g, function(m, key, value) {
obj[decodeURIComponent(key)] = decodeURIComponent(value);
});
This is adapted from John Resig's "Search and Don’t Replace".
The proposed solutions I found so far do not cover more complex scenarios.
I needed to convert a query string like
https://random.url.com?Target=Offer&Method=findAll&filters%5Bhas_goals_enabled%5D%5BTRUE%5D=1&filters%5Bstatus%5D=active&fields%5B%5D=id&fields%5B%5D=name&fields%5B%5D=default_goal_name
into an object like:
{
"Target": "Offer",
"Method": "findAll",
"fields": [
"id",
"name",
"default_goal_name"
],
"filters": {
"has_goals_enabled": {
"TRUE": "1"
},
"status": "active"
}
}
OR:
https://random.url.com?Target=Report&Method=getStats&fields%5B%5D=Offer.name&fields%5B%5D=Advertiser.company&fields%5B%5D=Stat.clicks&fields%5B%5D=Stat.conversions&fields%5B%5D=Stat.cpa&fields%5B%5D=Stat.payout&fields%5B%5D=Stat.date&fields%5B%5D=Stat.offer_id&fields%5B%5D=Affiliate.company&groups%5B%5D=Stat.offer_id&groups%5B%5D=Stat.date&filters%5BStat.affiliate_id%5D%5Bconditional%5D=EQUAL_TO&filters%5BStat.affiliate_id%5D%5Bvalues%5D=1831&limit=9999
INTO:
{
"Target": "Report",
"Method": "getStats",
"fields": [
"Offer.name",
"Advertiser.company",
"Stat.clicks",
"Stat.conversions",
"Stat.cpa",
"Stat.payout",
"Stat.date",
"Stat.offer_id",
"Affiliate.company"
],
"groups": [
"Stat.offer_id",
"Stat.date"
],
"limit": "9999",
"filters": {
"Stat.affiliate_id": {
"conditional": "EQUAL_TO",
"values": "1831"
}
}
}
CODE:
var getParamsAsObject = function (query) {
query = query.substring(query.indexOf('?') + 1);
var re = /([^&=]+)=?([^&]*)/g;
var decodeRE = /\+/g;
var decode = function (str) {
return decodeURIComponent(str.replace(decodeRE, " "));
};
var params = {}, e;
while (e = re.exec(query)) {
var k = decode(e[1]), v = decode(e[2]);
if (k.substring(k.length - 2) === '[]') {
k = k.substring(0, k.length - 2);
(params[k] || (params[k] = [])).push(v);
}
else params[k] = v;
}
var assign = function (obj, keyPath, value) {
var lastKeyIndex = keyPath.length - 1;
for (var i = 0; i < lastKeyIndex; ++i) {
var key = keyPath[i];
if (!(key in obj))
obj[key] = {}
obj = obj[key];
}
obj[keyPath[lastKeyIndex]] = value;
}
for (var prop in params) {
var structure = prop.split('[');
if (structure.length > 1) {
var levels = [];
structure.forEach(function (item, i) {
var key = item.replace(/[?[\]\\ ]/g, '');
levels.push(key);
});
assign(params, levels, params[prop]);
delete(params[prop]);
}
}
return params;
};
A concise solution:
location.search
.slice(1)
.split('&')
.map(p => p.split('='))
.reduce((obj, pair) => {
const [key, value] = pair.map(decodeURIComponent);
obj[key] = value;
return obj;
}, {});
This is the simple version, obviously you'll want to add some error checking:
var obj = {};
var pairs = queryString.split('&');
for(i in pairs){
var split = pairs[i].split('=');
obj[decodeURIComponent(split[0])] = decodeURIComponent(split[1]);
}
For Node JS, you can use the Node JS API querystring:
const querystring = require('querystring');
querystring.parse('abc=foo&def=%5Basf%5D&xyz=5&foo=b%3Dar');
// returns the object
Documentation: https://nodejs.org/api/querystring.html
I found $.String.deparam the most complete pre built solution (can do nested objects etc.). Check out the documentation.
Another solution based on the latest standard of URLSearchParams (https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
function getQueryParamsObject() {
const searchParams = new URLSearchParams(location.search.slice(1));
return searchParams
? _.fromPairs(Array.from(searchParams.entries()))
: {};
}
Please note that this solution is making use of
Array.from (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from)
and _.fromPairs (https://lodash.com/docs#fromPairs) of lodash for the sake of simplicity.
It should be easy to create a more compatible solution since you have access to searchParams.entries() iterator.
I had the same problem, tried the solutions here, but none of them really worked, since I had arrays in the URL parameters, like this:
?param[]=5¶m[]=8&othr_param=abc¶m[]=string
So I ended up writing my own JS function, which makes an array out of the param in URI:
/**
* Creates an object from URL encoded data
*/
var createObjFromURI = function() {
var uri = decodeURI(location.search.substr(1));
var chunks = uri.split('&');
var params = Object();
for (var i=0; i < chunks.length ; i++) {
var chunk = chunks[i].split('=');
if(chunk[0].search("\\[\\]") !== -1) {
if( typeof params[chunk[0]] === 'undefined' ) {
params[chunk[0]] = [chunk[1]];
} else {
params[chunk[0]].push(chunk[1]);
}
} else {
params[chunk[0]] = chunk[1];
}
}
return params;
}
One of the simplest way to do this using URLSearchParam interface.
Below is the working code snippet:
let paramObj={},
querystring=window.location.search,
searchParams = new URLSearchParams(querystring);
//*** :loop to add key and values to the param object.
searchParams.forEach(function(value, key) {
paramObj[key] = value;
});
There is quite simple and incorrect answer with ES6:
console.log(
Object.fromEntries(new URLSearchParams(`abc=foo&def=%5Basf%5D&xyz=5`))
);
But this one line code do not cover multiple same keys, you have to use something more complicated:
function parseParams(params) {
const output = [];
const searchParams = new URLSearchParams(params);
// Set will return only unique keys()
new Set([...searchParams.keys()])
.forEach(key => {
output[key] = searchParams.getAll(key).length > 1 ?
searchParams.getAll(key) : // get multiple values
searchParams.get(key); // get single value
});
return output;
}
console.log(
parseParams('abc=foo&cars=Ford&cars=BMW&cars=Skoda&cars=Mercedes')
)
Code will generate follow structure:
[
abc: "foo"
cars: ["Ford", "BMW", "Skoda", "Mercedes"]
]
Using ES6, URL API and URLSearchParams API.
function objectifyQueryString(url) {
let _url = new URL(url);
let _params = new URLSearchParams(_url.search);
let query = Array.from(_params.keys()).reduce((sum, value)=>{
return Object.assign({[value]: _params.get(value)}, sum);
}, {});
return query;
}
Pretty easy using the URLSearchParams JavaScript Web API,
var paramsString = "abc=foo&def=%5Basf%5D&xyz=5";
//returns an iterator object
var searchParams = new URLSearchParams(paramsString);
//Usage
for (let p of searchParams) {
console.log(p);
}
//Get the query strings
console.log(searchParams.toString());
//You can also pass in objects
var paramsObject = {abc:"forum",def:"%5Basf%5D",xyz:"5"}
//returns an iterator object
var searchParams = new URLSearchParams(paramsObject);
//Usage
for (let p of searchParams) {
console.log(p);
}
//Get the query strings
console.log(searchParams.toString());
##Useful Links
NOTE: Not Supported in IE
ES6 one liner (if we can call it that way seeing the long line)
[...new URLSearchParams(location.search).entries()].reduce((prev, [key,val]) => {prev[key] = val; return prev}, {})
One simple answer with build in native Node module.(No third party npm modules)
The querystring module provides utilities for parsing and formatting URL query strings. It can be accessed using:
const querystring = require('querystring');
const body = "abc=foo&def=%5Basf%5D&xyz=5"
const parseJSON = querystring.parse(body);
console.log(parseJSON);
/**
* Parses and builds Object of URL query string.
* @param {string} query The URL query string.
* @return {!Object<string, string>}
*/
function parseQueryString(query) {
if (!query) {
return {};
}
return (/^[?#]/.test(query) ? query.slice(1) : query)
.split('&')
.reduce((params, param) => {
const item = param.split('=');
const key = decodeURIComponent(item[0] || '');
const value = decodeURIComponent(item[1] || '');
if (key) {
params[key] = value;
}
return params;
}, {});
}
console.log(parseQueryString('?v=MFa9pvnVe0w&ku=user&from=89&aw=1'))
see log
There is no native solution that I'm aware of. Dojo has a built-in unserialization method if you use that framework by chance.
Otherwise you can implement it yourself rather simply:
function unserialize(str) {
str = decodeURIComponent(str);
var chunks = str.split('&'),
obj = {};
for(var c=0; c < chunks.length; c++) {
var split = chunks[c].split('=', 2);
obj[split[0]] = split[1];
}
return obj;
}
edit: added decodeURIComponent()
There's a lightweight library called YouAreI.js that's tested and makes this really easy.
YouAreI = require('YouAreI')
uri = new YouAreI('http://user:pass@www.example.com:3000/a/b/c?d=dad&e=1&f=12.3#fragment');
uri.query_get() => { d: 'dad', e: '1', f: '12.3' }
If you are using URI.js, you can use:
https://medialize.github.io/URI.js/docs.html#static-parseQuery
var result = URI.parseQuery("?foo=bar&hello=world&hello=mars&bam=&yup");
result === {
foo: "bar",
hello: ["world", "mars"],
bam: "",
yup: null
};
console.log(decodeURI('abc=foo&def=%5Basf%5D&xyz=5')
.split('&')
.reduce((result, current) => {
const [key, value] = current.split('=');
result[key] = value;
return result
}, {}))
This seems to be the best solution as it takes multiple parameters of the same name into consideration.
function paramsToJSON(str) {
var pairs = str.split('&');
var result = {};
pairs.forEach(function(pair) {
pair = pair.split('=');
var name = pair[0]
var value = pair[1]
if( name.length )
if (result[name] !== undefined) {
if (!result[name].push) {
result[name] = [result[name]];
}
result[name].push(value || '');
} else {
result[name] = value || '';
}
});
return( result );
}
<a href="index.html?x=1&x=2&x=3&y=blah">something</a>
paramsToJSON("x=1&x=2&x=3&y=blah");
console yields => {x: Array[3], y: "blah"} where x is an array as is proper JSON
I later decided to convert it to a jQuery plugin too...
$.fn.serializeURLParams = function() {
var result = {};
if( !this.is("a") || this.attr("href").indexOf("?") == -1 )
return( result );
var pairs = this.attr("href").split("?")[1].split('&');
pairs.forEach(function(pair) {
pair = pair.split('=');
var name = decodeURI(pair[0])
var value = decodeURI(pair[1])
if( name.length )
if (result[name] !== undefined) {
if (!result[name].push) {
result[name] = [result[name]];
}
result[name].push(value || '');
} else {
result[name] = value || '';
}
});
return( result )
}
<a href="index.html?x=1&x=2&x=3&y=blah">something</a>
$("a").serializeURLParams();
console yields => {x: Array[3], y: "blah"} where x is an array as is proper JSON
Now, the first will accept the parameters only but the jQuery plugin will take the whole url and return the serialized parameters.
Here's one I use:
var params = {};
window.location.search.substring(1).split('&').forEach(function(pair) {
pair = pair.split('=');
if (pair[1] !== undefined) {
var key = decodeURIComponent(pair[0]),
val = decodeURIComponent(pair[1]),
val = val ? val.replace(/\++/g,' ').trim() : '';
if (key.length === 0) {
return;
}
if (params[key] === undefined) {
params[key] = val;
}
else {
if ("function" !== typeof params[key].push) {
params[key] = [params[key]];
}
params[key].push(val);
}
}
});
console.log(params);
Basic usage, eg.
?a=aa&b=bb
Object {a: "aa", b: "bb"}
Duplicate params, eg.
?a=aa&b=bb&c=cc&c=potato
Object {a: "aa", b: "bb", c: ["cc","potato"]}
Missing keys, eg.
?a=aa&b=bb&=cc
Object {a: "aa", b: "bb"}
Missing values, eg.
?a=aa&b=bb&c
Object {a: "aa", b: "bb"}
The above JSON/regex solutions throw a syntax error on this wacky url:
?a=aa&b=bb&c=&=dd&e
Object {a: "aa", b: "bb", c: ""}
Here's my quick and dirty version, basically its splitting up the URL parameters separated by '&' into array elements, and then iterates over that array adding key/value pairs separated by '=' into an object. I'm using decodeURIComponent() to translate the encoded characters to their normal string equivalents (so %20 becomes a space, %26 becomes '&', etc):
function deparam(paramStr) {
let paramArr = paramStr.split('&');
let paramObj = {};
paramArr.forEach(e=>{
let param = e.split('=');
paramObj[param[0]] = decodeURIComponent(param[1]);
});
return paramObj;
}
example:
deparam('abc=foo&def=%5Basf%5D&xyz=5')
returns
{
abc: "foo"
def:"[asf]"
xyz :"5"
}
The only issue is that xyz is a string and not a number (due to using decodeURIComponent()), but beyond that its not a bad starting point.
//under ES6
const getUrlParamAsObject = (url = window.location.href) => {
let searchParams = url.split('?')[1];
const result = {};
//in case the queryString is empty
if (searchParams!==undefined) {
const paramParts = searchParams.split('&');
for(let part of paramParts) {
let paramValuePair = part.split('=');
//exclude the case when the param has no value
if(paramValuePair.length===2) {
result[paramValuePair[0]] = decodeURIComponent(paramValuePair[1]);
}
}
}
return result;
}
If you need recursion, you can use the tiny js-extension-ling library.
npm i js-extension-ling
const jsx = require("js-extension-ling");
console.log(jsx.queryStringToObject("a=1"));
console.log(jsx.queryStringToObject("a=1&a=3"));
console.log(jsx.queryStringToObject("a[]=1"));
console.log(jsx.queryStringToObject("a[]=1&a[]=pomme"));
console.log(jsx.queryStringToObject("a[0]=one&a[1]=five"));
console.log(jsx.queryStringToObject("http://blabla?foo=bar&number=1234"));
console.log(jsx.queryStringToObject("a[fruits][red][]=strawberry"));
console.log(jsx.queryStringToObject("a[fruits][red][]=strawberry&a[1]=five&a[fruits][red][]=cherry&a[fruits][yellow][]=lemon&a[fruits][yellow][688]=banana"));
This will output something like this:
{ a: '1' }
{ a: '3' }
{ a: { '0': '1' } }
{ a: { '0': '1', '1': 'pomme' } }
{ a: { '0': 'one', '1': 'five' } }
{ foo: 'bar', number: '1234' }
{
a: { fruits: { red: { '0': 'strawberry' } } }
}
{
a: {
'1': 'five',
fruits: {
red: { '0': 'strawberry', '1': 'cherry' },
yellow: { '0': 'lemon', '688': 'banana' }
}
}
}
Note: it's based on locutus parse_str function (https://locutus.io/php/strings/parse_str/).
FIRST U NEED TO DEFINE WHAT'S A GET VAR:
function getVar()
{
this.length = 0;
this.keys = [];
this.push = function(key, value)
{
if(key=="") key = this.length++;
this[key] = value;
this.keys.push(key);
return this[key];
}
}
Than just read:
function urlElement()
{
var thisPrototype = window.location;
for(var prototypeI in thisPrototype) this[prototypeI] = thisPrototype[prototypeI];
this.Variables = new getVar();
if(!this.search) return this;
var variables = this.search.replace(/\?/g,'').split('&');
for(var varI=0; varI<variables.length; varI++)
{
var nameval = variables[varI].split('=');
var name = nameval[0].replace(/\]/g,'').split('[');
var pVariable = this.Variables;
for(var nameI=0;nameI<name.length;nameI++)
{
if(name.length-1==nameI) pVariable.push(name[nameI],nameval[1]);
else var pVariable = (typeof pVariable[name[nameI]] != 'object')? pVariable.push(name[nameI],new getVar()) : pVariable[name[nameI]];
}
}
}
and use like:
var mlocation = new urlElement();
mlocation = mlocation.Variables;
for(var key=0;key<mlocation.keys.length;key++)
{
console.log(key);
console.log(mlocation[mlocation.keys[key]];
}
I needed to also deal with + in the query part of the URL (decodeURIComponent doesn't), so I adapted Wolfgang's code to become:
var search = location.search.substring(1);
search = search?JSON.parse('{"' + search.replace(/\+/g, ' ').replace(/&/g, '","').replace(/=/g,'":"') + '"}',
function(key, value) { return key===""?value:decodeURIComponent(value)}):{};
In my case, I'm using jQuery to get URL-ready form parameters, then this trick to build an object out of it and I can then easily update parameters on the object and rebuild the query URL, e.g.:
var objForm = JSON.parse('{"' + $myForm.serialize().replace(/\+/g, ' ').replace(/&/g, '","').replace(/=/g,'":"') + '"}',
function(key, value) { return key===""?value:decodeURIComponent(value)});
objForm.anyParam += stringToAddToTheParam;
var serializedForm = $.param(objForm);
Using phpjs
function parse_str(str, array) {
// discuss at: http://phpjs.org/functions/parse_str/
// original by: Cagri Ekin
// improved by: Michael White (http://getsprink.com)
// improved by: Jack
// improved by: Brett Zamir (http://brett-zamir.me)
// bugfixed by: Onno Marsman
// bugfixed by: Brett Zamir (http://brett-zamir.me)
// bugfixed by: stag019
// bugfixed by: Brett Zamir (http://brett-zamir.me)
// bugfixed by: MIO_KODUKI (http://mio-koduki.blogspot.com/)
// reimplemented by: stag019
// input by: Dreamer
// input by: Zaide (http://zaidesthings.com/)
// input by: David Pesta (http://davidpesta.com/)
// input by: jeicquest
// note: When no argument is specified, will put variables in global scope.
// note: When a particular argument has been passed, and the returned value is different parse_str of PHP. For example, a=b=c&d====c
// test: skip
// example 1: var arr = {};
// example 1: parse_str('first=foo&second=bar', arr);
// example 1: $result = arr
// returns 1: { first: 'foo', second: 'bar' }
// example 2: var arr = {};
// example 2: parse_str('str_a=Jack+and+Jill+didn%27t+see+the+well.', arr);
// example 2: $result = arr
// returns 2: { str_a: "Jack and Jill didn't see the well." }
// example 3: var abc = {3:'a'};
// example 3: parse_str('abc[a][b]["c"]=def&abc[q]=t+5');
// returns 3: {"3":"a","a":{"b":{"c":"def"}},"q":"t 5"}
var strArr = String(str)
.replace(/^&/, '')
.replace(/&$/, '')
.split('&'),
sal = strArr.length,
i, j, ct, p, lastObj, obj, lastIter, undef, chr, tmp, key, value,
postLeftBracketPos, keys, keysLen,
fixStr = function(str) {
return decodeURIComponent(str.replace(/\+/g, '%20'));
};
if (!array) {
array = this.window;
}
for (i = 0; i < sal; i++) {
tmp = strArr[i].split('=');
key = fixStr(tmp[0]);
value = (tmp.length < 2) ? '' : fixStr(tmp[1]);
while (key.charAt(0) === ' ') {
key = key.slice(1);
}
if (key.indexOf('\x00') > -1) {
key = key.slice(0, key.indexOf('\x00'));
}
if (key && key.charAt(0) !== '[') {
keys = [];
postLeftBracketPos = 0;
for (j = 0; j < key.length; j++) {
if (key.charAt(j) === '[' && !postLeftBracketPos) {
postLeftBracketPos = j + 1;
} else if (key.charAt(j) === ']') {
if (postLeftBracketPos) {
if (!keys.length) {
keys.push(key.slice(0, postLeftBracketPos - 1));
}
keys.push(key.substr(postLeftBracketPos, j - postLeftBracketPos));
postLeftBracketPos = 0;
if (key.charAt(j + 1) !== '[') {
break;
}
}
}
}
if (!keys.length) {
keys = [key];
}
for (j = 0; j < keys[0].length; j++) {
chr = keys[0].charAt(j);
if (chr === ' ' || chr === '.' || chr === '[') {
keys[0] = keys[0].substr(0, j) + '_' + keys[0].substr(j + 1);
}
if (chr === '[') {
break;
}
}
obj = array;
for (j = 0, keysLen = keys.length; j < keysLen; j++) {
key = keys[j].replace(/^['"]/, '')
.replace(/['"]$/, '');
lastIter = j !== keys.length - 1;
lastObj = obj;
if ((key !== '' && key !== ' ') || j === 0) {
if (obj[key] === undef) {
obj[key] = {};
}
obj = obj[key];
} else { // To insert new dimension
ct = -1;
for (p in obj) {
if (obj.hasOwnProperty(p)) {
if (+p > ct && p.match(/^\d+$/g)) {
ct = +p;
}
}
}
key = ct + 1;
}
}
lastObj[key] = value;
}
}
}
Building on top of Mike Causer's answer I've made this function which takes into consideration multiple params with the same key (foo=bar&foo=baz) and also comma-separated parameters (foo=bar,baz,bin). It also lets you search for a certain query key.
function getQueryParams(queryKey) {
var queryString = window.location.search;
var query = {};
var pairs = (queryString[0] === '?' ? queryString.substr(1) : queryString).split('&');
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
var key = decodeURIComponent(pair[0]);
var value = decodeURIComponent(pair[1] || '');
// Se possui uma vírgula no valor, converter em um array
value = (value.indexOf(',') === -1 ? value : value.split(','));
// Se a key já existe, tratar ela como um array
if (query[key]) {
if (query[key].constructor === Array) {
// Array.concat() faz merge se o valor inserido for um array
query[key] = query[key].concat(value);
} else {
// Se não for um array, criar um array contendo o valor anterior e o novo valor
query[key] = [query[key], value];
}
} else {
query[key] = value;
}
}
if (typeof queryKey === 'undefined') {
return query;
} else {
return query[queryKey];
}
}
Example input:
foo.html?foo=bar&foo=baz&foo=bez,boz,buz&bar=1,2,3
Example output
{
foo: ["bar","baz","bez","boz","buz"],
bar: ["1","2","3"]
}
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'));
.as-console-wrapper { top: 0; max-height: 100% !important; }
Here is a one-liner of the code above (125 bytes):
Where
fisparseSearch
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));
.as-console-wrapper { top: 0; max-height: 100% !important; }
Many other solutions don't account for edge cases.
This one handles
a=1&b=2&a=1&ba=1&b=a=1&b=2=3=4 decodeQueryString: qs => {
// expects qs to not have a ?
// return if empty qs
if (qs === '') return {};
return qs.split('&').reduce((acc, pair) => {
// skip no param at all a=1&b=2&
if (pair.length === 0) return acc;
const parts = pair.split('=');
// fix params without value
if (parts.length === 1) parts[1] = '';
// for value handle multiple unencoded = signs
const key = decodeURIComponent(parts[0]);
const value = decodeURIComponent(parts.slice(1).join('='));
acc[key] = value;
return acc;
}, {});
},