443

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
}
Penny Liu
  • 15,447
  • 5
  • 79
  • 98
Alex
  • 66,732
  • 177
  • 439
  • 641
  • See also http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values – Bergi Jan 28 '13 at 12:08
  • 1
    It's not: https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/get#Example https://developer.mozilla.org/en-US/docs/Web/API/URL/searchParams#Example (though we'll have to wait a little while longer for all browsers to have picked this up) – Arthur Jan 31 '18 at 21:30

34 Answers34

461

In the year 2021... Please consider this obsolete.

Edit

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:

  • decodeURI: abc=foo&def=[asf]&xyz=5
  • Escape quotes: same, as there are no quotes
  • Replace &: abc=foo","def=[asf]","xyz=5
  • Replace =: abc":"foo","def":"[asf]","xyz":"5
  • Suround with curlies and quotes: {"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"}

Original answer

A one-liner:

JSON.parse('{"' + decodeURI("abc=foo&def=%5Basf%5D&xyz=5".replace(/&/g, "\",\"").replace(/=/g,"\":\"")) + '"}')
chickens
  • 19,976
  • 6
  • 58
  • 55
Wolfgang Kuehn
  • 12,206
  • 2
  • 33
  • 46
  • 4
    For this to work in CoffeeScript, escape the '=' in the regex. .replace(/\=/g,"\":\"") – airlok May 29 '12 at 15:47
  • Thanks for the answer. Maybe explaining what each portion does would help some people understand it better rather than just copy+paste+hope it works. - let's leave one liners to minifiers :) – 0x6A75616E Oct 02 '12 at 17:50
  • This does not escape the quotes as necessary. Add `.replace(/"/g,'\\"')` – Bergi Jan 28 '13 at 12:05
  • 309
    That's no one-liner... it's a space station. – Ziggy Feb 20 '13 at 02:48
  • 8
    better if you use `JSON.parse('{"' + decodeURI(location.search.substring(1).replace(/&/g, "\",\"").replace(/=/g, "\":\"")) + '"}')` – Daniël Tulp Apr 03 '13 at 12:06
  • What about browsers without JSON.parse() method? Since I have jQuery on my website, If I simply change JSON.parse() to $.parseJSON(), will it work? – Leonardo Montenegro Jan 31 '14 at 17:56
  • What happens if you have urlencode & in the string? Does not abc=fo%26o&def=%5B%26asf%5D&xyz=5%26 fail? – user1122069 Feb 11 '14 at 00:20
  • The improved solution was what I needed because I used line breaks `%0D%0A` - hexadecimal for 10 (line feed) and 13 (carriage return) - in my email body. Thanks so much! – FFish Jun 11 '15 at 11:59
  • 7
    This fails if you have an equals sign character in the url to be parsed. EX: "cookie=dlksdlfj=sodkfjhsdlfj" – jholloman Aug 05 '15 at 17:28
  • 5
    Also does not work when you have one of parameters without value. – Sych Jun 23 '16 at 15:20
  • Does not handle case 'foo=bar&foo=baz&bim=bat' which I would expect to translate to `{foo: ['bar', 'baz'], bim: 'bat'}` – starsinmypockets Jul 14 '16 at 18:35
  • There is no standard I know of describing passing arrays in this way. [urlencoded-parsing](https://url.spec.whatwg.org/#urlencoded-parsing) only talks about tuples of key/value pairs. – Wolfgang Kuehn Oct 26 '16 at 10:58
  • This also fails if your result is a `bool` it will convert a `isApproved=false&message=""` to { isApproved: "false", message="" }` which still evaluates to `true`! – Serj Sagan Oct 27 '16 at 06:55
  • Well, yes. If you want to interpret the query parameters as json types. An equal valid interpretation would be plain english, i.e. isApproved=No – Wolfgang Kuehn Oct 29 '16 at 09:17
  • wait. Which is the most improved? The first one, or the second one? – jeffkmeng Feb 24 '18 at 04:40
  • The second one. Also the code for the second choice was somewhat garbled (dunno how that happend). I fixed it. – Wolfgang Kuehn Feb 26 '18 at 16:22
  • I think the first solution has the `decodeURI` closing parentheses misplaced. Right now is `decodeURI(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"')`, and it should be `decodeURI(search.replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"'))`. Also I think that `decodeURIComponent()` works better than `decodeURI()` because it decodes special chars like "@" and "$". So I'd propose: **`decodeURIComponent(search.replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"'))`**. I think it works fine always and avoids the extra complexity of the second solution. – AMS777 Mar 26 '18 at 15:29
  • 2
    What about decoding arrays like a[]=1&a[]=2 or eves subarrays like a[0][b]=1&a[0][c]=2 – Vedmant Jun 15 '18 at 10:43
  • You could also split() string if you haver the entire location url ``` var search = location.url.split('?')[1]; JSON.parse('{"' + decodeURI(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"') + '"}') ``` – mate.gvo Aug 27 '18 at 16:02
  • doesn't remove the first question's mark in my case, here my URL: ` Router.replace(`/letchat?name=${this.state.name.replace(" ", "+")}&room=${this.state.room.replace(" ", "+")}`);` – Webwoman Feb 22 '19 at 22:08
  • You have to pass the query part of the URL, which is the part *after* the question mark. – Wolfgang Kuehn Feb 25 '19 at 08:48
  • 1
    Doesn't work for `?filters[32][]=2&filters[33][]=9` , any idea? – Daniyal Javani Aug 19 '19 at 12:01
  • What is the point of testing if key is empty string? `return key===""?value:decodeURIComponent(value)`. Doesn't it work just as well if you always return `decodeURIComponent(value)`? – T Nguyen Apr 19 '21 at 03:09
  • I think SO should discourage this "LATEST YEAR!" thing because the years, keep going by. The best answer is actually the one below 2022. – httpete May 05 '22 at 19:49
349

2023 ES6/7/8 and on approach

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;
}

Basic Demo

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"}

Using Object.fromEntries and spread

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

Multiple same keys

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}
silicakes
  • 6,364
  • 3
  • 28
  • 39
  • 3
    I do not recommend this solution. URLSearchParams has illogical specs (https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams#Gotchas) – Seph Reed Sep 09 '19 at 22:29
  • 8
    I'm sorry but logic doesn't have anything to do with it. One might argue that it's a `search string` parser - which is what it was designed to do, regardless of it being associated to a URL – silicakes Sep 10 '19 at 07:50
  • `Object.fromEntries` doesn't work for repeated keys. If we try to do something like `?foo=bar1&foo=bar2`, we'll get only `{ foo: 'bar2' }`. Node.js request object, for example, parses it as `{ foo: ['bar1', 'bar2'] }` – Siipe Feb 10 '20 at 14:24
  • You're right, however this has no spec and many languages take an opinionated approach as to how they parse it: https://stackoverflow.com/a/1746566/1194694 – silicakes Feb 11 '20 at 09:54
  • 2
    This looks good to me, but to get the values for repetitive key we can use this `let temp={};Object.keys(params).map(key=>{temp[key]=urlParams.getAll(key)})` – Tirumaleshwar Keregadde Mar 20 '20 at 07:08
  • It will force every value to be returned in the form of an array which is not good or bad, but worth taking into consideration. – silicakes May 30 '20 at 12:35
  • Using `Object.fromEntries(params)` is required for correct `axios-mock-adapter` integration. – ivosh Oct 29 '20 at 18:04
  • this should be the accepted answer – papillon Jul 07 '21 at 08:39
  • 2
    Arrays keys came in the form `foo[]: [1, 2, 3]` but I wanted `foo: [1, 2, 3]` so I added one extra line: ``` const [ _key, val ] = tuple const key = _key.replace(/\[]$/, '') ``` – Emeke Ajeh Nov 01 '21 at 18:51
335

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);
chickens
  • 19,976
  • 6
  • 58
  • 55
  • 5
    This can be used, but with caution `?someValue=false` becomes `{ someValue: "false" }` – Simon Oct 23 '19 at 00:22
  • 22
    It doesn't work for repeated keys. If we try to do something like `?foo=bar1&foo=bar2`, we'll get only `{ foo: 'bar2' }`. Node.js request object parses it as `{ foo: ['bar1', 'bar2'] }` – Siipe Feb 10 '20 at 14:21
  • 4
    this fails with arrays, ex: x=1&x=2 -> result {x:2} – Sh eldeeb May 20 '20 at 17:28
  • calling `getQuery( )` breaks JavaScript execution in `` – s3c Aug 04 '20 at 11:42
  • @simon so does `new URLSearchParams(location.search).get("someValue")` which is the usual methd – RedGuy11 Jan 19 '21 at 16:55
68

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'));
David Hansen
  • 2,857
  • 2
  • 21
  • 19
  • 1
    Also `[...new URLSearchParams(window.location.search)].reduce((o, i) => ({ ...o, [i[0]]: i[1] }), {});` – dman Aug 21 '19 at 18:52
  • 2
    Note that this converts `?booleanValue=true` to `{ booleanValue: "true" }` which may be undesirable. – Chris Hayes Feb 03 '21 at 22:21
  • 7
    This is a BAD solution when you have an array field within your query params. In a case where you have ids=1&ids=2, you'd get back { ids: '2' }. – Tal Kohavy Jul 28 '22 at 11:36
34

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".

Wayne
  • 59,728
  • 15
  • 131
  • 126
29

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"
        }
    }
}

I compiled and adapted multiple solutions into one that actually works:

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;
};
Georgy Ivanov
  • 1,573
  • 1
  • 17
  • 24
22

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;
  }, {});
Jack
  • 9,448
  • 3
  • 29
  • 33
Clemens Helm
  • 3,841
  • 1
  • 22
  • 13
18

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]);
}
Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
11

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

etoxin
  • 4,908
  • 3
  • 38
  • 50
10

I found $.String.deparam the most complete pre built solution (can do nested objects etc.). Check out the documentation.

Daff
  • 43,734
  • 9
  • 106
  • 120
  • just pointing out if your input is always going to be a serialized query string there is no need to worry about nesting and a more lightweight solution is probably better – mattacular Dec 27 '11 at 20:35
  • Well of course... but it's been done and tested already (Justin e.g. forget to URI decode in the initial answer - which are small issues that can make things a lot more complex than they initially seem). – Daff Dec 27 '11 at 21:46
9

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.

Nickey
  • 1,191
  • 1
  • 13
  • 21
7

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&param[]=8&othr_param=abc&param[]=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;
}
pcigler
  • 329
  • 3
  • 3
  • 3
    This was really helpful and did almost exactly what I wanted. I didn't like, though, how the "[]" is kept in tact in the object if the URL parameters are like: bacon[]=eggs&bacon[]=toast. So I added in a line after `if(chunk[0].search("\\[\\]") !== -1) {` that is `chunk[0]=chunk[0].replace(/\[\]$/,'');` – rgbflawed Aug 12 '13 at 19:00
  • @rgbflawed you should edit the answer for the sake of future reader and legibility – Webwoman Feb 22 '19 at 22:09
  • Use `const` instead of `var` because someone might do `createObjFromURI = 'some text'` and then they would mess up the code. if you use `const` then someone running `createObjFromURI = 'some text'` will make an error cannot assign value to constant variable. – Justin Liu May 14 '20 at 16:29
6

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;
   });
Rishu Ranjan
  • 494
  • 4
  • 7
6

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"]
]
OzzyCzech
  • 9,713
  • 3
  • 50
  • 34
5

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;
}
supergentle
  • 1,001
  • 1
  • 14
  • 33
5

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

Erisan Olasheni
  • 2,395
  • 17
  • 20
5

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}, {})

fadomire
  • 1,865
  • 1
  • 15
  • 23
5

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);
  • Nice. I have tried this and it's working in Angular, i'm using `import * as querystring from "querystring";` – Snowbases May 04 '21 at 07:52
  • This is also available in the browser in any npm package with the same name and functionality (`npm install querystring`) – rattray Jun 11 '21 at 19:16
3

/**
 * 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
Alex Alan Nunes
  • 174
  • 2
  • 9
3

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()

mattacular
  • 1,849
  • 13
  • 17
2

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' }
steel
  • 11,883
  • 7
  • 72
  • 109
2

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
};
tom10271
  • 4,222
  • 5
  • 33
  • 62
2

console.log(decodeURI('abc=foo&def=%5Basf%5D&xyz=5')
  .split('&')
  .reduce((result, current) => {
    const [key, value] = current.split('=');

    result[key] = value;

    return result
  }, {}))
aagamezl
  • 49
  • 3
1

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.

1

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: ""}

Mike Causer
  • 8,196
  • 2
  • 43
  • 63
1

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.

Eric
  • 573
  • 1
  • 5
  • 11
1
//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;
}
XYz Amos
  • 1,403
  • 14
  • 17
  • I really like this method (in 2017) compared to some of the other regex-based answers. If polyfill the arrow function (or rewrite as a traditional function), I think this should work pretty well cross-browser – Scribblemacher Sep 18 '17 at 14:06
  • @Scribblemacher with `Babel`'s help you can do it well under other environment – XYz Amos Sep 19 '17 at 02:16
1

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/).

ling
  • 9,545
  • 4
  • 52
  • 49
0

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]];
}
GetFree
  • 40,278
  • 18
  • 77
  • 104
Ag.
  • 17
  • 1
  • Please merge your answers. [Edit the other one](http://stackoverflow.com/posts/13366851/edit), then delete this one. – Bergi Jan 28 '13 at 12:07
0

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);
Community
  • 1
  • 1
dlauzon
  • 1,241
  • 16
  • 23
0

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;
    }
  }
}
Duy Hoang
  • 532
  • 1
  • 4
  • 11
0

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"]
}
Gus
  • 15,397
  • 7
  • 25
  • 28
0

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 f is parseSearch

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}),{})

Edit

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; }
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
-1

Many other solutions don't account for edge cases.

This one handles

  • null keys a=1&b=2&
  • null values a=1&b
  • empty values a=1&b=
  • unencoded equals signs 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;
    }, {});
  },
Tallboy
  • 12,847
  • 13
  • 82
  • 173