277

Without knowing the keys of a JavaScript Object, how can I turn something like...

var obj = {
   param1: 'something',
   param2: 'somethingelse',
   param3: 'another'
}

obj[param4] = 'yetanother';

...into...

var str = 'param1=something&param2=somethingelse&param3=another&param4=yetanother';

...?

Jasper de Vries
  • 19,370
  • 6
  • 64
  • 102
bobsoap
  • 4,844
  • 7
  • 30
  • 43

27 Answers27

289

One line with no dependencies:

new URLSearchParams(obj).toString();
// OUT: param1=something&param2=somethingelse&param3=another&param4=yetanother

Use it with the URL builtin like so:

let obj = { param1: 'something', param2: 'somethingelse', param3: 'another' }
obj['param4'] = 'yetanother';
const url = new URL(`your_url.com`);
url.search = new URLSearchParams(obj);
const response = await fetch(url);

[Edit April 4, 2020]: null values will be interpreted as the string 'null'.

[Edit Mar 9, 2022]: browser compatibility

jfunk
  • 7,176
  • 4
  • 37
  • 38
  • Note: in node < 10 you'll need to import/require e.g., `const { URLSearchParams } = require("url");` – groovecoder Nov 07 '19 at 15:57
  • 1
    @HonsaStunna I've never thought about putting multidimensional objects on a URL parameter, usually use POST with JSON for that – jfunk Nov 07 '19 at 18:12
  • 1
    Be careful with this one and null values. – Xaraxia Apr 03 '20 at 01:15
  • 1
    You should check if your target browsers to suport URLSearchParams, e.g. IE11 does not: https://caniuse.com/?search=URLSearchParams – Peter T. Oct 28 '20 at 07:13
  • Is there a way where we can use a different separator rather than default one '&'? – Dev AKS Dec 10 '20 at 12:23
  • Could you update your question to a runnable code? 10x – vsync Jan 30 '21 at 13:30
  • 1
    I find your answer elegant but I needed a one-liner that could check null. So here's an implementation that checks null and undefined ```Object.entries(obj).filter(([key, val]) => val != null && val != undefined).map(([key, val]) => `${key}=${val}`).join('&');``` – BeeShopRocks Aug 03 '21 at 15:52
  • 7
    will not work with nested objects – LaCodeM Nov 30 '21 at 10:09
  • this doesn't support nested objects – Sh eldeeb Jun 22 '22 at 14:27
  • ISO dates are parsed wrongly. Here is result: `date=2023-03-24T15%3A10%3A01.511Z`. `:` are replaced with special characters – shutsman Mar 24 '23 at 15:10
150

If you use jQuery, this is what it uses for parameterizing the options of a GET XHR request:

$.param( obj )

http://api.jquery.com/jQuery.param/

Duke
  • 7,070
  • 3
  • 38
  • 28
149

An elegant one: (assuming you are running a modern browser or node)

var str = Object.keys(obj).map(function(key) {
  return key + '=' + obj[key];
}).join('&');

And the ES2017 equivalent: (thanks to Lukas)

let str = Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&');

Note: You probably want to use encodeURIComponent() if the keys/values are not URL encoded.

benweet
  • 3,685
  • 1
  • 21
  • 26
124
var str = "";
for (var key in obj) {
    if (str != "") {
        str += "&";
    }
    str += key + "=" + encodeURIComponent(obj[key]);
}

Example: http://jsfiddle.net/WFPen/

aroth
  • 54,026
  • 20
  • 135
  • 176
  • 5
    Why not use a function with recursion? – Jared Farrish Jul 04 '11 at 00:53
  • @Jared: in JavaScript, recursive solutions are usually dreadfully slow in comparison with their iterative counterparts. Usually. – Reid Jul 04 '11 at 00:55
  • @Reid - Recursion is potentially problematic, but why would Javascript have a issue unless it was several levels deep? – Jared Farrish Jul 04 '11 at 00:57
  • @Jared - Because from the question it did not appear that the object was intended to be more than a single level deep. If the goal is to serialize an arbitrary JavaScript object into a query string, then a recursive solution may be appropriate. But I didn't get the impression that that's what was being asked for here. – aroth Jul 04 '11 at 01:01
  • @aroth - Yes, hence my comment under the question. :) I'm just thinking about utility, but I suppose it probably doesn't matter. – Jared Farrish Jul 04 '11 at 01:03
  • 1
    thanks @aroth! I only accepted @patrick's answer above over yours (they are essentially the same) because he was first, I believe. I'm very grateful for your response. – bobsoap Jul 04 '11 at 01:06
  • 4
    @bobsoap: I think @aroth was a little ahead of me, so I'd give @aroth the tick all other things being equal. – user113716 Jul 04 '11 at 01:08
  • Should theoretically use `hasownproperty`, but in real life, not really. http://phrogz.net/death-to-hasownproperty – ripper234 Jul 18 '13 at 12:35
  • 3
    Shouldn't the obj[key] be wrapped in encodeURIComponent()? What happens if 'somethingelse' was 'something&else'? – James S Nov 21 '14 at 09:22
  • @JamesS yes it should, this fixes it `Object.keys(formData).map(key => [key, formData[key]].map(encodeURIComponent).join('=')).join('&')` – johnrees Mar 01 '17 at 19:11
  • 1
    I'm pretty sure this should *not* be the accepted answer, or nearly every answer on this stackoverflow thread. Primary reason being none of them with the exception of possible @zac's answer below will satisfy properly encoding this object, `{ a:[ 1, 2 ], b:{ c:3, d:[ 4, 5 ], e:{ x:[ 6 ], y:7, z:[ 8, 9 ] }, f:true, g:false, h:undefined }, i:[ 10, 11 ], j:true, k:false, l:[ undefined, 0 ], m:"cowboy hat?" };` It does look like @UnLoCo suggested an NPM library that will also work, which pulls the functioning param method out of jQuery to be standalone. – Wes May 13 '19 at 19:51
  • @Wes agreed, the only solid answer here is jfunk's. – hraban Sep 11 '19 at 15:54
  • 1
    @Wes Point taken, although the OP asks about doing this with "something like" a simple object where each key-value pair maps to a param name and param value (as a string). Honestly I don't know why you'd want to use this approach with a complex object in the first place. If the object you want to send contains nested objects and arrays it's almost certainly better to `JSON.stringify()` the root object and just send the entire JSON string as the request body. Whomever is writing your server-side code to parse this stuff will thank you, guaranteed. :) – aroth Sep 15 '19 at 11:38
33

ES2017 approach

Object.entries(obj).map(([key, val]) => `${key}=${encodeURIComponent(val)}`).join('&')
Lukas
  • 9,752
  • 15
  • 76
  • 120
  • 2
    [ES2017](https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_Next_support_in_Mozilla), technically. – Nick Zalutskiy Oct 18 '17 at 15:15
  • 3
    this needs encoding of both key and value. see other answers re encodeURIComponent. – hraban Sep 11 '19 at 15:55
  • 1
    also will not work with nested objects – LaCodeM Nov 30 '21 at 10:10
  • I fixed the `encodeURIComponent` part but the nested object part still holds true. Although if you want to serialize and send an object as a parameter to your key, the will still work – Lukas Dec 09 '21 at 14:24
27

ES6:

function params(data) {
  return Object.keys(data).map(key => `${key}=${encodeURIComponent(data[key])}`).join('&');
}

console.log(params({foo: 'bar'}));
console.log(params({foo: 'bar', baz: 'qux$'}));
u.k
  • 3,091
  • 1
  • 20
  • 23
22

For one level deep...

var serialiseObject = function(obj) {
    var pairs = [];
    for (var prop in obj) {
        if (!obj.hasOwnProperty(prop)) {
            continue;
        }
        pairs.push(prop + '=' + obj[prop]);
    }
    return pairs.join('&');
}

jsFiddle.

There was talk about a recursive function for arbitrarily deep objects...

var serialiseObject = function(obj) {
    var pairs = [];
    for (var prop in obj) {
        if (!obj.hasOwnProperty(prop)) {
            continue;
        }
        if (Object.prototype.toString.call(obj[prop]) == '[object Object]') {
            pairs.push(serialiseObject(obj[prop]));
            continue;
        }
        pairs.push(prop + '=' + obj[prop]);
    }
    return pairs.join('&');
}

jsFiddle.

This of course means that the nesting context is lost in the serialisation.

If the values are not URL encoded to begin with, and you intend to use them in a URL, check out JavaScript's encodeURIComponent().

alex
  • 479,566
  • 201
  • 878
  • 984
  • 1
    alex: Sorry, we're closed... ;o) – user113716 Jul 04 '11 at 01:10
  • 1
    +1 for being safer than I'm willing to be: `.hasOwnProperty(prop)`. – user113716 Jul 04 '11 at 01:13
  • this is great - just need 1 level for now, but the recursive function is good to have. Thanks for adding it! – bobsoap Jul 04 '11 at 01:19
  • 1
    @ripper234 You're free to not use that method, if it suits your requirements. – alex Jul 18 '13 at 15:09
  • This works: ``` const findOptions = { where: {id: facilityId.toString()}, include: [ serialiseObject({ association: 'Insurances', where: { amount: '9.0000' } }), ] }; const searchParams = serialiseObject(findOptions); ``` – Ray Koren Aug 02 '22 at 16:21
9

If you're using NodeJS 13.1 or superior you can use the native querystring module to parse query strings.

const qs = require('querystring');
let str = qs.stringify(obj)
Robilol
  • 146
  • 1
  • 6
8
Object.keys(obj).map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`).join('&')
Henry
  • 2,870
  • 1
  • 25
  • 17
5

Just for the record and in case you have a browser supporting ES6, here's a solution with reduce:

Object.keys(obj).reduce((prev, key, i) => (
  `${prev}${i!==0?'&':''}${key}=${obj[key]}`
), '');

And here's a snippet in action!

// Just for test purposes
let obj = {param1: 12, param2: "test"};

// Actual solution
let result = Object.keys(obj).reduce((prev, key, i) => (
  `${prev}${i!==0?'&':''}${key}=${obj[key]}`
), '');

// Run the snippet to show what happens!
console.log(result);
Yan Foto
  • 10,850
  • 6
  • 57
  • 88
5

Since I made such a big deal about a recursive function, here is my own version.

function objectParametize(obj, delimeter, q) {
    var str = new Array();
    if (!delimeter) delimeter = '&';
    for (var key in obj) {
        switch (typeof obj[key]) {
            case 'string':
            case 'number':
                str[str.length] = key + '=' + obj[key];
            break;
            case 'object':
                str[str.length] = objectParametize(obj[key], delimeter);
        }
    }
    return (q === true ? '?' : '') + str.join(delimeter);
}

http://jsfiddle.net/userdude/Kk3Lz/2/

Jared Farrish
  • 48,585
  • 17
  • 95
  • 104
  • 3
    Just some random thoughts (a) `[]` is preferred over `new Array()` (b) You can use `delimiter = delimiter || '&';` for the argument default (and you spelt it wrong) (c) Iterating with `for ( in )` will iterate over all enumerable properties, including things on the prototype chain (`obj.hasOwnProperty()` defends against this) (d) `typeof` can *lie* about what things are, e.g. some numbers can be `Object` if constructed with the `Number()` constructor (e) `Array` have a `push()` method for adding members (f) comparing to `true` is redundant. I am a nitpicky bastard but you wanted feedback! :) – alex Jul 04 '11 at 22:41
  • 1
    ...and if you thing Crockford is right about everything, you shouldn't let switch cases fall through. I disagree with him on that though. :D – alex Jul 04 '11 at 22:42
  • @alex - I appreciate it. a) I had that at first, it was late and I was sleepy; b) not sure what the improvement is, the second was also a sleep-deprived moment; c) I was wondering why you used `hasOwnProperty()`; d) that's certainly true and a good point; e) I've never gotten use to using `push()` or `pop()` methods; f) `break` or not to `break`, that is the question. Thank you for your detailed input. :) – Jared Farrish Jul 04 '11 at 23:18
4

A useful code when you have the array in your query:

var queryString = Object.keys(query).map(key => {
    if (query[key].constructor === Array) {
        var theArrSerialized = ''
        for (let singleArrIndex of query[key]) {
            theArrSerialized = theArrSerialized + key + '[]=' + singleArrIndex + '&'
        }
        return theArrSerialized
    }
    else {
        return key + '=' + query[key] + '&'
    }
}
).join('');
console.log('?' + queryString)
Pouya Jabbarisani
  • 1,084
  • 3
  • 16
  • 29
4

This one-liner also handles nested objects and JSON.stringify them as needed:

let qs = Object.entries(obj).map(([k, v]) => `${k}=${encodeURIComponent(typeof (v) === "object" ? JSON.stringify(v) : v)}`).join('&')
supersan
  • 5,671
  • 3
  • 45
  • 64
  • 1
    Nice approach but it fails on this nesting: `{apple:['old'],['blue'],banana:['fresh','green']}` which should turn into something like `apple=old,blue&banana=fresh,green` – Fred Mar 13 '21 at 17:47
  • @Fred - Fails b/c `JSON.stringify` is expecting a valid JSON format. – christopher.theagen Apr 18 '22 at 20:58
3

You can use jQuery's param method:

var obj = {
  param1: 'something',
  param2: 'somethingelse',
  param3: 'another'
}
obj['param4'] = 'yetanother';
var str = jQuery.param(obj);
alert(str);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
wpp
  • 7,093
  • 4
  • 33
  • 65
SleX
  • 139
  • 1
  • 4
3

A functional approach.

var kvToParam = R.mapObjIndexed((val, key) => {
  return '&' + key + '=' + encodeURIComponent(val);
});

var objToParams = R.compose(
  R.replace(/^&/, '?'),
  R.join(''),
  R.values,
  kvToParam
);

var o = {
  username: 'sloughfeg9',
  password: 'traveller'
};

console.log(objToParams(o));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.22.1/ramda.min.js"></script>
tladuke
  • 1,337
  • 2
  • 11
  • 22
3

A quick ES6 answer:

const paramsObject = {
   foo: "bar",
   biz: "baz"
}

const params = Object.entries(paramsObject)
                 .map(([k, v]) => `${k}=${v}`)
                 .join('&');

// Output: foo=bar&biz=baz
MaxCodes
  • 82
  • 10
Adam Davis
  • 31
  • 4
3

If you need a recursive function that will produce proper URL parameters based on the object given, try my Coffee-Script one.

@toParams = (params) ->
    pairs = []
    do proc = (object=params, prefix=null) ->
      for own key, value of object
        if value instanceof Array
          for el, i in value
            proc(el, if prefix? then "#{prefix}[#{key}][]" else "#{key}[]")
        else if value instanceof Object
          if prefix?
            prefix += "[#{key}]"
          else
            prefix = key
          proc(value, prefix)
        else
          pairs.push(if prefix? then "#{prefix}[#{key}]=#{value}" else "#{key}=#{value}")
    pairs.join('&')

or the JavaScript compiled...

toParams = function(params) {
  var pairs, proc;
  pairs = [];
  (proc = function(object, prefix) {
    var el, i, key, value, _results;
    if (object == null) object = params;
    if (prefix == null) prefix = null;
    _results = [];
    for (key in object) {
      if (!__hasProp.call(object, key)) continue;
      value = object[key];
      if (value instanceof Array) {
        _results.push((function() {
          var _len, _results2;
          _results2 = [];
          for (i = 0, _len = value.length; i < _len; i++) {
            el = value[i];
            _results2.push(proc(el, prefix != null ? "" + prefix + "[" + key + "][]" : "" + key + "[]"));
          }
          return _results2;
        })());
      } else if (value instanceof Object) {
        if (prefix != null) {
          prefix += "[" + key + "]";
        } else {
          prefix = key;
        }
        _results.push(proc(value, prefix));
      } else {
        _results.push(pairs.push(prefix != null ? "" + prefix + "[" + key + "]=" + value : "" + key + "=" + value));
      }
    }
    return _results;
  })();
  return pairs.join('&');
};

This will construct strings like so:

toParams({a: 'one', b: 'two', c: {x: 'eight', y: ['g','h','j'], z: {asdf: 'fdsa'}}})

"a=one&b=two&c[x]=eight&c[y][0]=g&c[y][1]=h&c[y][2]=j&c[y][z][asdf]=fdsa"
Zac
  • 185
  • 2
  • 17
2
var str = '';

for( var name in obj ) {
    str += (name + '=' + obj[name] + '&');
}

str = str.slice(0,-1);

Give this a shot.

Example: http://jsfiddle.net/T2UWT/

user113716
  • 318,772
  • 63
  • 451
  • 440
  • 1
    @Jared: Less efficient than a loop. – user113716 Jul 04 '11 at 00:54
  • @Jared: username not needed below Qs and As. Notifications are automatic. – user113716 Jul 04 '11 at 00:54
  • "Less performant"? If I pass in an arbitrary object, and want a GET string to be returned, what does performance have to do with it? – Jared Farrish Jul 04 '11 at 00:55
  • @Jared: I'm sorry, but perhaps I'm not understanding your meaning. If you're suggesting the use of recursive function calls rather than a loop, then I'm quite certain that the recursive functions will perform more slowly than the loop. Have I misunderstood your point? – user113716 Jul 04 '11 at 00:57
  • 1
    Well, I don't know I guess. If there were a function that you send it an object and output a string-concatenated GET-qualified version of that object, I don't imagine a single loop will always deal with the input. Of course, a single-level loop will always "outperform" a multi-level recursion, but a single-level loop won't even handle a multi-level object, IMO. – Jared Farrish Jul 04 '11 at 01:00
  • @Jared: Yes, with a multi-level object I'd imagine you'd need recursion, but even then, I'd think you'd need a different function (or different sort of processing in the same function) since it wouldn't seem to make sense to have a new querystring key start as the value of its parent. – user113716 Jul 04 '11 at 01:05
  • @patrick - Yeah, I'm pretty sure if I would have answered the question I would have overthought the requirement. – Jared Farrish Jul 04 '11 at 01:07
2

I needed something that processes nested objects as well as arrays.

const Util = {
  isArray: function(val) {
    return Object.prototype.toString.call(val) === '[object Array]';
  },
  isNil: function(val) {
    return val === null || Util.typeOf(val)
  },
  typeOf: function(val, type) {
    return (type || 'undefined') === typeof val;
  },
  funEach: function(obj, fun) {
    if (Util.isNil(obj))
      return;      // empty value

    if (!Util.typeOf(obj, 'object'))
      obj = [obj]; // Convert to array

    if (Util.isArray(obj)) {
      // Iterate over array
      for (var i = 0, l = obj.length; i < l; i++)
        fun.call(null, obj[i], i, obj);
    } else {
      // Iterate over object
      for (var key in obj)
        Object.prototype.hasOwnProperty.call(obj, key) && fun.call(null, obj[key], key, obj);
    }
  }
};

const serialize = (params) => {
  let pair = [];

  const encodeValue = v => {
    if (Util.typeOf(v, 'object'))
      v = JSON.stringify(v);

    return encodeURIComponent(v);
  };

  Util.funEach(params, (val, key) => {
    let isNil = Util.isNil(val);

    if (!isNil && Util.isArray(val))
      key = `${key}[]`;
    else
      val = [val];

    Util.funEach(val, v => {
      pair.push(`${key}=${isNil ? "" : encodeValue(v)}`);
    });
  });

  return pair.join('&');
};

Usage:

serialize({
  id: null,
  lat: "27",
  lng: "53",
  polygon: ["27,53", "31,18", "22,62", "..."]
}); // "id=&lat=27&lng=53&polygon[]=27%2C53&polygon[]=31%2C18&polygon[]=22%2C62&polygon[]=..."
AamirR
  • 11,672
  • 4
  • 59
  • 73
1
Object.toparams = function ObjecttoParams(obj) 
{
  var p = [];
  for (var key in obj) 
  {
    p.push(key + '=' + encodeURIComponent(obj[key]));
  }
  return p.join('&');
};
Razi Syed
  • 11
  • 1
1

try this... this is working for nested object also..

let my_obj = {'single':'this is single', 'nested':['child1','child2']};

((o)=>{ return Object.keys(o).map(function(key){ let ret=[]; if(Array.isArray(o[key])){ o[key].forEach((item)=>{ ret.push(`${key}[]=${encodeURIComponent(item)}`); }); }else{ ret.push(`${key}=${encodeURIComponent(o[key])}`); } return ret.join("&");  }).join("&"); })(my_obj);
Syamsoul Azrien
  • 2,534
  • 4
  • 32
  • 55
0

this method uses recursion to descend into object hierarchy and generate rails style params which rails interprets as embedded hashes. objToParams generates a query string with an extra ampersand on the end, and objToQuery removes the final amperseand.

 function objToQuery(obj){
  let str = objToParams(obj,'');
  return str.slice(0, str.length);
}
function   objToParams(obj, subobj){
  let str = "";

   for (let key in obj) {
     if(typeof(obj[key]) === 'object') {
       if(subobj){
         str += objToParams(obj[key], `${subobj}[${key}]`);
       } else {
         str += objToParams(obj[key], `[${key}]`);
       }

     } else {
       if(subobj){
         str += `${key}${subobj}=${obj[key]}&`;
       }else{
         str += `${key}=${obj[key]}&`;
       }
     }
   }
   return str;
 }
oneunit
  • 9
  • 1
0

You could use npm lib query-string

const queryString = require('query-string');

querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' });
// Returns 'foo=bar&baz=qux&baz=quux&corge='
Mihail
  • 1,246
  • 1
  • 9
  • 8
0

const obj = { id: 1, name: 'Neel' };
let str = '';
str = Object.entries(obj).map(([key, val]) => `${key}=${val}`).join('&');
console.log(str);
Dharman
  • 30,962
  • 25
  • 85
  • 135
Neel Rathod
  • 2,013
  • 12
  • 28
0

We should also handle the cases when the value of any entry is undefined, that key should not be included in the serialized string.

const serialize = (obj) => {
  return Object.entries(obj)
    .filter(([, value]) => value)
    .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
    .join('&');
}
Mahesh
  • 39
  • 4
0
export const convertObjToUrlParams = (obj) =>
{
    var paramString = '';
    for (let key in obj)
    {
        if (obj[key] !== null && obj[key] !== undefined)
        {
            paramString += '&';
            paramString += key + "=" + obj[key];
        }
    }
    return paramString;
}

Output Ex: &firstName=NoDo&userId=2acf67ed-73c7-4707-9b49-17e78afce42e&email=n@n.dk&phoneNumber=12345678&password=123456

Stefan27
  • 845
  • 8
  • 19
-1

With Axios and infinite depth:

<pre>
    <style>
      textarea {
        width: 80%;
        margin-bottom: 20px;
      }
      label {
        font-size: 18px;
        font-weight: bold;
      }
    </style>
    <label>URI</label>
    <textarea id="uri"  rows="7"></textarea>
    <label>All Defaults (Bonus): </label>
    <textarea id="defaults" rows="20"></textarea>
</pre>

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

<script>
  const instance = axios.create({
    baseUrl: 'http://my-api-server',
    url: '/user'
  })
  const uri = instance.getUri({
    params: {
      id: '1234',
      favFruits: [
        'banana',
        'apple',
        'strawberry'
      ],
      carConfig: {
        items: ['keys', 'laptop'],
        type: 'sedan',
        other: {
          music: ['on', 'off', {
            foo: 'bar'
          }]
        }
      }
    }
  })
  const defaults = JSON.stringify(instance.defaults, null, 2)
  document.getElementById('uri').value = uri
  document.getElementById('defaults').value = defaults
</script>

Good Luck...

Aakash
  • 21,375
  • 7
  • 100
  • 81