122

Given the following form:

<form>
    <input name="foo" value="bar">
    <input name="hello" value="hello world">
</form>

I can use the $.param( .. ) construct to serialize the form:

$.param( $('form input') )

=> foo=bar&hello=hello+world

How can I deserialize the above String with JavaScript and get a hash back?

For example,

$.magicFunction("foo=bar&hello=hello+world")

=> {'foo' : 'bar', 'hello' : 'hello world'}

Reference: jQuery.param( obj ).

Pang
  • 9,564
  • 146
  • 81
  • 122
seb
  • 3,274
  • 4
  • 22
  • 16
  • 2
    Hey, I'm just adding this comment so you can see the update I did to the code, in case you're using it... I'd accidentally written it so that it only replaced the first `+`. Now it replaces all of them! – Blixt Jul 15 '09 at 21:41
  • There are plugins called `QueryString`. I made one but it doesn't accept parameters, it only reads from window.location.search. I remember others that accept parameters... – BrunoLM Aug 28 '10 at 22:03
  • 1
    refactored jquery BBQ deparam() method as NPM module without dependencies https://www.npmjs.com/package/deparam – alexey2baranov Jan 28 '17 at 13:35
  • alexey2baranov how about refactoring deparam() without NPM as dependency? :) – Andrew Mar 30 '17 at 15:04

18 Answers18

62

You should use jQuery BBQ's deparam function. It's well-tested and documented.

cce
  • 4,874
  • 2
  • 28
  • 25
  • Nice plugin! Thanks for sharing. – Jonathan Oct 23 '12 at 04:43
  • 30
    If you don't want to get the whole BBQ plugin, the deparam function was extracted as a standalone plugin here: https://github.com/chrissrogers/jquery-deparam – Felipe Castro Jan 08 '13 at 00:52
  • 2
    Unfortunately the github page hasn't seen any updates in years, and when I tried it it does not seem compatible with the latest jQuery... Looks like what I need though. – pilavdzice Nov 29 '13 at 22:16
31

This is a slightly modified version of a function I wrote a while ago to do something similar.

var QueryStringToHash = function QueryStringToHash  (query) {
  var query_string = {};
  var vars = query.split("&");
  for (var i=0;i<vars.length;i++) {
    var pair = vars[i].split("=");
    pair[0] = decodeURIComponent(pair[0]);
    pair[1] = decodeURIComponent(pair[1]);
        // If first entry with this name
    if (typeof query_string[pair[0]] === "undefined") {
      query_string[pair[0]] = pair[1];
        // If second entry with this name
    } else if (typeof query_string[pair[0]] === "string") {
      var arr = [ query_string[pair[0]], pair[1] ];
      query_string[pair[0]] = arr;
        // If third or later entry with this name
    } else {
      query_string[pair[0]].push(pair[1]);
    }
  } 
  return query_string;
};
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
21

How about this short functional approach?

function parseParams(str) {
    return str.split('&').reduce(function (params, param) {
        var paramSplit = param.split('=').map(function (value) {
            return decodeURIComponent(value.replace(/\+/g, ' '));
        });
        params[paramSplit[0]] = paramSplit[1];
        return params;
    }, {});
}

Example:

parseParams("this=is&just=an&example") // Object {this: "is", just: "an", example: undefined}
Brendan Nee
  • 5,087
  • 2
  • 33
  • 32
Joel
  • 15,496
  • 7
  • 52
  • 40
  • 1
    This should be the accepted answer, no external dependencies. – Michael Robinson Jan 07 '16 at 00:03
  • Simple, reliable and powerful solution. Respect, sir. – fmquaglia Jul 17 '16 at 21:00
  • 6
    For what it does it is certainly a good solution. But it does not take care of arrays or objects being passed. The url `field[][]=a&field[0][]=b&field[0][]=c` gets processed as `Object { field[][]: "a", field[0][]: "c" }` where it should be `resultobject.field[0] = Array [ "a", "b", "c" ]`. This is the behaviour as expected from PHP. @JackyLi's solution does take care of that. – Carsten Massmann Aug 05 '16 at 13:38
18

My answer:

function(query){
  var setValue = function(root, path, value){
    if(path.length > 1){
      var dir = path.shift();
      if( typeof root[dir] == 'undefined' ){
        root[dir] = path[0] == '' ? [] : {};
      }

      arguments.callee(root[dir], path, value);
    }else{
      if( root instanceof Array ){
        root.push(value);
      }else{
        root[path] = value;
      }
    }
  };
  var nvp = query.split('&');
  var data = {};
  for( var i = 0 ; i < nvp.length ; i++ ){
    var pair = nvp[i].split('=');
    var name = decodeURIComponent(pair[0]);
    var value = decodeURIComponent(pair[1]);

    var path = name.match(/(^[^\[]+)(\[.*\]$)?/);
    var first = path[1];
    if(path[2]){
      //case of 'array[level1]' || 'array[level1][level2]'
      path = path[2].match(/(?=\[(.*)\]$)/)[1].split('][')
    }else{
      //case of 'name'
      path = [];
    }
    path.unshift(first);

    setValue(data, path, value);
  }
  return data;
}
Jacky Li
  • 189
  • 1
  • 2
  • Yes! Yes! Yes! This is the exact one I need to parse the params that are being sent by JQuery in an $.ajax call. This returns the correctly nested hashes. Thanks so much, Jacky Li! – Pascal Lindelauf May 05 '11 at 10:34
  • The best solution so far - It behaves *pretty* good! But when I use `JSON.stringify(geturlargs('fld[2][]=2&fld[][]=3&fld[3][]=4&fld[]=bb&fld[]=cc').fld)` I get `{"2":["2"],"3":["4"],"":"cc"}` and not `[null,null,[2],[3,4],"bb","cc"]` what I would have hoped for (this is what PHP would give me). – Carsten Massmann Aug 05 '16 at 13:58
  • 1
    This is THE correct answer and please ignore all other anwers, they do not work properly – Andrew Mar 30 '17 at 15:21
  • 1
    This answer is the only one that addressed my particular use case (where I've passed an object with nested key/values to jQuery's $.param, this properly hydrates those values upon retrieval). – delinear Feb 19 '18 at 10:36
  • This might need a tweak. I just noticed if query === "" this has exception at var first = path[1];, because nvp has one item in it. – Lou K Sep 11 '20 at 20:39
  • Nice that it works for multiple values passed in the URL – luke Jan 18 '22 at 12:48
9

I am using David Dorward's answer, and realized that it doesn't behave like PHP or Ruby on Rails how they parse the params:

1) a variable is only an array if it ends with [], such as ?choice[]=1&choice[]=12, not when it is ?a=1&a=2

2) when mulitple params exist with the same name, the later ones replaces the earlier ones, as on PHP servers (Ruby on Rails keep the first one and ignore the later ones), such as ?a=1&b=2&a=3

So modifying David's version, I have:

function QueryStringToHash(query) {

  if (query == '') return null;

  var hash = {};

  var vars = query.split("&");

  for (var i = 0; i < vars.length; i++) {
    var pair = vars[i].split("=");
    var k = decodeURIComponent(pair[0]);
    var v = decodeURIComponent(pair[1]);

    // If it is the first entry with this name
    if (typeof hash[k] === "undefined") {

      if (k.substr(k.length-2) != '[]')  // not end with []. cannot use negative index as IE doesn't understand it
        hash[k] = v;
      else
        hash[k.substr(0, k.length-2)] = [v];

    // If subsequent entry with this name and not array
    } else if (typeof hash[k] === "string") {
      hash[k] = v;  // replace it

    // If subsequent entry with this name and is array
    } else {
      hash[k.substr(0, k.length-2)].push(v);
    }
  } 
  return hash;
};

which is tested fairly thoroughly.

Andre Figueiredo
  • 12,930
  • 8
  • 48
  • 74
nonopolarity
  • 146,324
  • 131
  • 460
  • 740
7

I know this is an old thread, but maybe there is still some relevance in it?

Inspired by Jacky Li's good solution I tried a slight variation of my own with the objective to also be able to take care of arbitrary combinations of arrays and objects as input. I looked at how PHP would have done it and tried to get something "similar" going. Here is my code:

function getargs(str){
   var ret={};
   function build(urlnam,urlval,obj){ // extend the return object ...
    var i,k,o=obj, x, rx=/\[([^\]]*)\]/g, idx=[urlnam.replace(rx,'')];
    while (x=rx.exec(urlnam)) idx.push(x[1]); 
    while(true){
     k=idx.shift();
     if(k.trim()=='') {// key is empty: autoincremented index
       if (o.constructor.name=='Array') k=o.length; // for Array
       else if (o===obj ) {k=null}  // for first level property name
       else {k=-1;                                  // for Object
         for(i in o) if (+i>k) k=+i;
         k++;
       }
     }
     if(idx.length) { 
       // set up an array if the next key (idx[0]) appears to be
       // numeric or empty, otherwise set up an object:
       if (o[k]==null || typeof o[k]!='object') o[k]=isNaN(idx[0])?{}:[]; 
       o=o[k]; // move on to the next level
     }
     else { // OK, time to store the urlval in its chosen place ...
       // console.log('key',k,'val',urlval);                 
       o[k]=urlval===""?null:urlval; break; // ... and leave the while loop.
     } 
    }
    return obj;
   }
   // ncnvt: is a flag that governs the conversion of
   // numeric strings into numbers
   var ncnvt=true,i,k,p,v,argarr=[],
       ar=(str||window.location.search.substring(1)).split("&"),
       l=ar.length;
   for (i=0;i<l;i++) {if (ar[i]==="") continue;
     p=ar[i].split("=");k=decodeURIComponent(p[0]);
     v=p[1];v=(v!=null)?decodeURIComponent(v.replace(/\+/g,'%20')):'';
     if (ncnvt && v.trim()>"" && !isNaN(v)) v-=0;
     argarr.push([k,v]);  // array: key-value-pairs of all arguments
   }
   for (i=0,l=argarr.length;i<l;i++) build(argarr[i][0],argarr[i][1],ret);
   return ret;
}

If the function is called without the str-argument it will assume window.location.search.slice(1) as input.

Some examples:

['a=1&a=2',                               // 1
 'x[y][0][z][]=1',                        // 2
 'hello=[%22world%22]&world=hello',       // 3
 'a=1&a=2&&b&c=3&d=&=e&',                 // 4
 'fld[2][]=2&fld[][]=3&fld[3][]=4&fld[]=bb&fld[]=cc',  // 5
 $.param({a:[[1,2],[3,4],{aa:'one',bb:'two'},[5,6]]}), // 6
 'a[]=hi&a[]=2&a[3][]=7&a[3][]=99&a[]=13',// 7
 'a[x]=hi&a[]=2&a[3][]=7&a[3][]=99&a[]=13'// 8
].map(function(v){return JSON.stringify(getargs(v));}).join('\n')

results in

{"a":2}                                    // 1
{"x":{"y":[{"z":[1]}]}}                    // 2
{"hello":"[\"world\"]","world":"hello"}    // 3
{"a":2,"b":null,"c":3,"d":null,"null":"e"} // 4 = { a: 2, b: null, c: 3, d: null, null: "e" }
{"fld":[null,null,[2],[3,4],"bb","cc"]}    // 5 
{"a":[[1,2],[3,4],{"aa":"one","bb":"two"},[5,6]]}  // 6
{"a":["hi",2,null,[7,99],13]}              // 7
{"a":{"0":2,"3":[7,99],"4":13,"x":"hi"}}   // 8

Whereas Jacky Li's solution would produce the outer container for a as a plain object

{a:{"0":["1","2"],"1":["3","4"],"2":["5","6"]}} // 6: JackyLi's output

getargs() looks at the first given index for any level to determine whether this level will be an object (non-numeric index) or an array (numeric or empty), thus resulting in the output as shown in the listing bove (no. 6).

If the current object is an array then nulls get inserted wherever necessary to represent empty positions. Arrays are always consecutively numbered and 0-based).

Note, that in the example no. 8 the "autoincrement" for empty indices still works, even though we are dealing with an object now and not an array.

As far as I have tested it, my getargs() behaves pretty much identically to Chriss Roger's great jQuery $.deparam() plugin mentioned in the accepted answer. The main difference is that getargs runs without jQuery and that it does autoincrement in objects while $.deparam() will not do that:

JSON.stringify($.deparam('a[x]=hi&a[]=2&a[3][]=7&a[3][]=99&a[]=13').a);

results in

{"3":["7","99"],"x":"hi","undefined":"13"}

In $.deparam() the index [] is interpreted as an undefined instead of an autoincremented numerical index.

Carsten Massmann
  • 26,510
  • 2
  • 22
  • 43
  • thanks for this great function. However I noticed that if you use number as key in array, e.g. like ' ?a[5][]=x ' then key is used as number when creating object and thus it creates 5 empty elements in my case. It should treat numeric key as string, and thus create object with "5" (with quotes) as key and x as value. I could force url to be ' ?a["5"][]=x ' but this is ugly... – Andrew Mar 30 '17 at 13:44
  • @Andrew: This was a deliberate design decision on my part: As far as I remember, Jack's solution will behave more in the way you expect it. I introduced the line `o[k]=isNaN(idx[0])?{}:[];` with the intention of creating an array, whenever I encounter a numeric index as the first one given for a new object. Otherwise I will make a generic object. It is up to you to modify that part of the code to better suit your needs. I. e. something like `o[k]={}` should do the trick. – Carsten Massmann Mar 31 '17 at 18:33
  • thanks. P.S. you should definitely remove console.log in the middle of your function ;) – Andrew Mar 31 '17 at 20:21
  • @Andrew: Thanks, just commented it out. Must have overlooked it when I posted the code. – Carsten Massmann Apr 01 '17 at 07:20
6

Here's how you could create a new jQuery function:

jQuery.unparam = function (value) {
    var
    // Object that holds names => values.
    params = {},
    // Get query string pieces (separated by &)
    pieces = value.split('&'),
    // Temporary variables used in loop.
    pair, i, l;

    // Loop through query string pieces and assign params.
    for (i = 0, l = pieces.length; i < l; i++) {
        pair = pieces[i].split('=', 2);
        // Repeated parameters with the same name are overwritten. Parameters
        // with no value get set to boolean true.
        params[decodeURIComponent(pair[0])] = (pair.length == 2 ?
            decodeURIComponent(pair[1].replace(/\+/g, ' ')) : true);
    }

    return params;
};
Blixt
  • 49,547
  • 13
  • 120
  • 153
  • 4
    I don't like this function. Why should parameter with no value should get the value of 'true'? Why not null? I think this design decision can lead to very subtle bugs. Also, why not handle the situation where parameter can have multiple values instead of selecting the 'last' value? Solution posted by David is much better. – SolutionYogi Jul 15 '09 at 21:45
  • 4
    It is `true` so that one can check `if (params.redirect)` or similar. If you want to differ between `true` and other value, you would use `if (params.redirect === true)`. A `null` value wouldn't help in any way that I can think of. The reason I didn't do it like David is because I value consistency over flexibility. With his method I have to check if the value is a string or an array to use its value. In my opinion, it should always be a string (the `true` value converts to `'true'` which I think is fine), or always be an array. I chose string. – Blixt Jul 15 '09 at 21:54
  • By that account, if it was params.delete, it would be set to true and I will do something harmful. As you can see, coming up with example is easy. 'null' clearly specifies that no value exists and the decision is left to the caller what how he wants to interpret it. About multiple values, look at it this way. By eating up, you are making sure that either caller will spend time debugging why he is getting only one value OR later on come and modify the code. By returning the array upfront, caller will immediately know how to handle them. Discarding information is never good, IMHO. – SolutionYogi Jul 15 '09 at 22:30
  • 2
    If the URL is `/abc?delete` then I would expect there to be an entry for `delete`. I don't see how that is different? I don't even see how you can prefer the other code for this reason, because that code will set the value to the string `'undefined'` which is not better. And as for multiple values it's a case of simplicity vs. flexibility. I rarely see the use of multiple values in query strings, but if you want them, always return an array with 1+ values. Never mix the return type; then you *will* have to spend time to debug because what was usually a string could suddenly be an array. – Blixt Jul 16 '09 at 06:36
  • Thanks. I don't think there is a silver bullet. I took your code and modified it a bit, removed the boolean values, added parsing of array-parameters. ?foo[]=1&foo[]=2 => foo : ["1","2"] – seb Jul 16 '09 at 10:49
  • Of course, I just want people to be aware of the dangers of inconsistent magic. It's so overlooked that even the best aren't prepared for it: http://pecl.php.net/package-search.php?pkg_name%5B%5D=arrays Usually it just messes up the result for the user, but it could have other consequences. – Blixt Jul 16 '09 at 11:12
6

Thanks to him http://james.padolsey.com/javascript/parsing-urls-with-the-dom/

Pretty easy :D

function params_unserialize(p){
var ret = {},
    seg = p.replace(/^\?/,'').split('&'),
    len = seg.length, i = 0, s;
for (;i<len;i++) {
    if (!seg[i]) { continue; }
    s = seg[i].split('=');
    ret[s[0]] = s[1];
}
return ret;}
brutuscat
  • 3,139
  • 2
  • 28
  • 33
  • 2
    This do not work with select boxes with multiple options, because it only keep last selected. – Fernando Fabreti Aug 16 '12 at 13:37
  • Sorry but I can't see what is the point you are referring to in the comment. The function referenced here is to unserialize/parse an URI with params like `?something=1&param2=value2` into an array. What do you mean with "do not work with select boxes with multiple options"? – brutuscat Aug 17 '12 at 10:46
5

This is really old question, but as i have coming - other people may coming to this post, and i want to a bit refresh this theme. Today no need to make custom solutions - there is URLSearchParams interface.

var paramsString = "q=URLUtils.searchParams&topic=api";
var searchParams = new URLSearchParams(paramsString);

//Iterate the search parameters.
for (let p of searchParams) {
  console.log(p);
}

The only one limitation i know - this feature not supported in IE / Edge.

Igor Benikov
  • 884
  • 6
  • 21
4

Here's my JavaScript implementation which I use in a server-side JScript ASP Classic page (demo):

// Transforms a query string in the form x[y][0][z][]=1 into {x:{y:[{z:[1]}]}}
function parseJQueryParams(p) {
    var params = {};
    var pairs = p.split('&');
    for (var i=0; i<pairs.length; i++) {
        var pair = pairs[i].split('=');
        var indices = [];
        var name = decodeURIComponent(pair[0]),
            value = decodeURIComponent(pair[1]);

        var name = name.replace(/\[([^\]]*)\]/g, 
            function(k, idx) { indices.push(idx); return ""; });

        indices.unshift(name);
        var o = params;

        for (var j=0; j<indices.length-1; j++) {
            var idx = indices[j];
            var nextIdx = indices[j+1];
            if (!o[idx]) {
                if ((nextIdx == "") || (/^[0-9]+$/.test(nextIdx)))
                    o[idx] = [];
                else
                    o[idx] = {};
            }
            o = o[idx];
        }

        idx = indices[indices.length-1];
        if (idx == "") {
            o.push(value);
        }
        else {
            o[idx] = value;
        }
    }
    return params;
}
Yuval
  • 3,207
  • 32
  • 45
1

I came up with this solution, which behaves like the .Net function HttpUtility.ParseQueryString.

In the result, the query string parameters are store in properties as lists of values, so that qsObj["param"] will be the same as calling GetValues("param") in .Net.

I hope you like it. JQuery not required.

var parseQueryString = function (querystring) {
    var qsObj = new Object();
    if (querystring) {
        var parts = querystring.replace(/\?/, "").split("&");
        var up = function (k, v) {
            var a = qsObj[k];
            if (typeof a == "undefined") {
                qsObj[k] = [v];
            }
            else if (a instanceof Array) {
                a.push(v);
            }
        };
        for (var i in parts) {
            var part = parts[i];
            var kv = part.split('=');
            if (kv.length == 1) {
                var v = decodeURIComponent(kv[0] || "");
                up(null, v);
            }
            else if (kv.length > 1) {
                var k = decodeURIComponent(kv[0] || "");
                var v = decodeURIComponent(kv[1] || "");
                up(k, v);
            }
        }
    }
    return qsObj;
};

Here is how to use it:

var qsObj = parseQueryString("a=1&a=2&&b&c=3&d=&=e&");

To preview the result in the console juste type in:

JSON.stringify(qsObj)

Output:

"{"a":["1","2"],"null":["","b",""],"c":["3"],"d":[""],"":["e"]}"
1

There's a beautiful one-liner over at CSS-Tricks (original source from Nicholas Ortenzio):

function getQueryParameters(str) {
    return (str || document.location.search).replace(/(^\?)/,'').split("&").map(function(n){return n = n.split("="),this[n[0]] = n[1],this}.bind({}))[0];
}

The really clever part is how it uses the anonymous function's this object, adding a key/value pair for each of the queries in the string. That said, there's some room for improvement. I've modified it a bit below, with the following changes:

  1. Added handling of empty strings and non-string input.

  2. Handled URI-encoded strings (%40->@, etc).

  3. Removed the default use of document.location.search when the input was empty.

  4. Changed the name, made it more readable, added comments.

function deparam(str) {
    // Uses an empty 'this' to build up the results internally
    function splitQuery(query) {
        query = query.split('=').map(decodeURIComponent);
        this[query[0]] = query[1];
        return this;
    }

    // Catch bad input
    if (!str || !(typeof str === 'string' || str instanceof String))
        return {};

    // Split the string, run splitQuery on each piece, and return 'this'
    var queries = str.replace(/(^\?)/,'').split('&');
    return queries.map(splitQuery.bind({}))[0];
}
Erik Koopmans
  • 2,292
  • 2
  • 21
  • 30
1

use this :

// convert query string to json object
var queryString = "cat=3&sort=1&page=1";

queryString
    .split("&")
    .forEach((item) => {
        const prop = item.split("=");
        filter[prop[0]] = prop[1];
    });

console.log(queryString);
0

This is my version in Coffeescript. Also works for url like http://localhost:4567/index.html?hello=[%22world%22]&world=hello#/home

getQueryString: (url)->
    return null if typeof url isnt 'string' or url.indexOf("http") is -1

    split = url.split "?"

    return null if split.length < 2 
    path = split[1]

    hash_pos = path.indexOf "#"
    path = path[0...hash_pos] if hash_pos isnt -1

    data = path.split "&"
    ret = {}
    for d in data
      [name, val] = d.split "=" 
      name = decodeURIComponent name
      val = decodeURIComponent val
      try 
        ret[name] = JSON.parse val
      catch error
        ret[name] = val
    return ret
coderek
  • 1,860
  • 14
  • 20
0

Here's a simple & compact one if you only want to quickly get the parameters from a GET request:

function httpGet() {
    var a={},b,i,q=location.search.replace(/^\?/,"").split(/\&/);
    for(i in q) if(q[i]) {b=q[i].split("=");if(b[0]) a[b[0]]=
    decodeURIComponent(b[1]).replace(/\+/g," ");} return a;
}

It converts

something?aa=1&bb=2&cc=3

into an object like

{aa:1,bb:2,cc:3}
dkellner
  • 8,726
  • 2
  • 49
  • 47
0

Creates a serialized representation of an array or object (can be used as URL query string for AJAX requests).

<button id='param'>GET</button> 
<div id="show"></div>
<script>
  $('#param').click(function () {
    var personObj = new Object();
    personObj.firstname = "vishal"
    personObj.lastname = "pambhar";
    document.getElementById('show').innerHTML=$.param(`personObj`));
  });
</script>
output:firstname=vishal&lastname=pambhar
Alex Taylor
  • 8,343
  • 4
  • 25
  • 40
-1

answers could use a bit of jQuery elegance:

(function($) {
var re = /([^&=]+)=?([^&]*)/g;
var decodeRE = /\+/g; // Regex for replacing addition symbol with a space
var decode = function (str) {return decodeURIComponent( str.replace(decodeRE, " ") );};
$.parseParams = function(query) {
    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;
    }
    return params;
};
})(jQuery);

fork at https://gist.github.com/956897

kares
  • 7,076
  • 1
  • 28
  • 38
-1

You can use the function .serializeArray() (Link) of jQuery itself. This function returns an array of key-value pair. Result example:

[
  { name: "id", value: "1" },
  { name: "version", value: "100" }
]
  • 1
    This is not what was asked. He wants to turn a string into a hash. serializeArray takes a form and returns an array. – Daniel Bang Feb 08 '15 at 19:28