62

I have a javascript object that I need to flatten into a string so that I can pass as querystring, how would I do that? i.e:

{ cost: 12345, insertBy: 'testUser' } would become cost=12345&insertBy=testUser

I can't use jQuery AJAX call for this call, I know we can use that and pass the object in as data but not in this case. Using jQuery to flatten to object would be okay though.

Thank you.

Saxman
  • 5,009
  • 11
  • 51
  • 72

12 Answers12

83

Here's a non-jQuery version:

function toQueryString(obj) {
    var parts = [];
    for (var i in obj) {
        if (obj.hasOwnProperty(i)) {
            parts.push(encodeURIComponent(i) + "=" + encodeURIComponent(obj[i]));
        }
    }
    return parts.join("&");
}
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • When I try this it uses the parameter number, not the name for the first part of the query? Javascript drives me nuts. – Nick.Mc Jun 13 '14 at 01:03
  • @ElectricLlama: Are you using an array? Do you have some example code? – Tim Down Jun 15 '14 at 14:11
  • My mistake, I mixed up the parameters being passed into the function and it performed the operation on a string rather then a object. – Nick.Mc Jun 15 '14 at 22:47
  • Should you actually call encodeURIComponent on the key? – Russ Savage Apr 03 '15 at 04:10
  • @RussSavage: Yes. The whole thing needs to be URL-encoded. – Tim Down Apr 03 '15 at 09:49
  • this seems to only work for shallow objects. with deep-nested objects you get =object – codeMonkey May 03 '16 at 17:02
  • @codeMonkey: Yes, that's intentional. It's a simple example. If you want to pass an object in a query string then you'll need some way of serializing that object, which is outside the scope of this question. – Tim Down May 04 '16 at 14:24
  • Why are you checking `i` existance in `obj` when you are iterating through the properties with `for in` loop? – razz Mar 06 '17 at 21:27
  • @razzak: Because `obj` could have inherited properties from its prototype that we don't want to include in the query string we're generating. Example: http://jsbin.com/botapabomo/edit?html,js – Tim Down Mar 13 '17 at 17:08
72

You want jQuery.param:

var str = $.param({ cost: 12345, insertBy: 'testUser' });
// "cost=12345&insertBy=testUser"

Note that this is the function used internally by jQuery to serialize objects passed as the data argument.

lonesomeday
  • 233,373
  • 50
  • 316
  • 318
39

My ES6 version (pure Javascript, no jQuery):

function toQueryString(paramsObject) {
  return Object
    .keys(paramsObject)
    .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(paramsObject[key])}`)
    .join('&')
  ;
}
Michaël Perrin
  • 5,903
  • 5
  • 40
  • 65
  • Thanks for the snippet! – Mirage Apr 12 '17 at 08:52
  • 1
    Great! But I added `.filter(key => paramsObject[key] !== null)` (or something else) before calling `.map(...)` to prevent including null values – Frankie Drake Aug 15 '17 at 13:55
  • @Daria you can actually just omit the test and `.filter` will automatically strip null values iirc – robertmain Jul 30 '18 at 15:25
  • 2
    @robertmain you wouldn't want to do that if you cared about values like 0 or false. [0, 1, 2, false, null, undefined].filter(x => x) // Array [ 1, 2 ] – shmup Aug 05 '18 at 15:48
19

This is an old question, but at the top of Google searches, so I'm adding this for completeness.

If 1) you don't want to user jQuery, but 2) you want to covert a nested object to a query string, then (building off of Tim Down and Guy's answers), use this:

function toQueryString(obj, urlEncode) {
    //
    // Helper function that flattens an object, retaining key structer as a path array:
    //
    // Input: { prop1: 'x', prop2: { y: 1, z: 2 } }
    // Example output: [
    //     { path: [ 'prop1' ],      val: 'x' },
    //     { path: [ 'prop2', 'y' ], val: '1' },
    //     { path: [ 'prop2', 'z' ], val: '2' }
    // ]
    //
    function flattenObj(x, path) {
        var result = [];

        path = path || [];
        Object.keys(x).forEach(function (key) {
            if (!x.hasOwnProperty(key)) return;

            var newPath = path.slice();
            newPath.push(key);

            var vals = [];
            if (typeof x[key] == 'object') {
                vals = flattenObj(x[key], newPath);
            } else {
                vals.push({ path: newPath, val: x[key] });
            }
            vals.forEach(function (obj) {
                return result.push(obj);
            });
        });

        return result;
    } // flattenObj

    // start with  flattening `obj`
    var parts = flattenObj(obj); // [ { path: [ ...parts ], val: ... }, ... ]

    // convert to array notation:
    parts = parts.map(function (varInfo) {
        if (varInfo.path.length == 1) varInfo.path = varInfo.path[0];else {
            var first = varInfo.path[0];
            var rest = varInfo.path.slice(1);
            varInfo.path = first + '[' + rest.join('][') + ']';
        }
        return varInfo;
    }); // parts.map

    // join the parts to a query-string url-component
    var queryString = parts.map(function (varInfo) {
        return varInfo.path + '=' + varInfo.val;
    }).join('&');
    if (urlEncode) return encodeURIComponent(queryString);else return queryString;
}

Use like:

console.log(toQueryString({
    prop1: 'x',
    prop2: {
        y: 1,
        z: 2
    }
}, false));

Which outputs:

prop1=x&prop2[y]=1&prop2[z]=2
Jonathan Apodaca
  • 5,458
  • 6
  • 30
  • 41
18

Here is another non-jQuery version that utilizes lodash or underscore if you're already using one of those libraries:

var toQueryString = function(obj) {
  return _.map(obj,function(v,k){
    return encodeURIComponent(k) + '=' + encodeURIComponent(v);
  }).join('&');
};

^ I wrote that 5 years ago. An updated and more succinct version of this would now (Oct 2019) be:

var input = { cost: 12345, insertBy: 'testUser' };
Object.entries(input)
  .map(([k,v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
  .join('&');
// cost=12345&insertBy=testUser

Check that the runtime that you're targeting supports Object.entries() or that you're using a transpiler like Babel or TypeScript if it doesn't.

Guy
  • 65,082
  • 97
  • 254
  • 325
16

Try the $.param() method:

var result = $.param({ cost: 12345, insertBy: 'testUser' });
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
4

Another version:

function toQueryString(obj) {
    return Object.keys(obj).map(k => {
      return encodeURIComponent(k) + "=" + encodeURIComponent(obj[k])
    })
    .join("&");
}
blablabla
  • 1,468
  • 15
  • 17
4

General JavaScript:

function toParam(obj) {
  var str = "";
  var seperator = "";
  for (key in obj) {
    str += seperator;
    str += enncodeURIComponent(key) + "=" + encodeURIComponent(obj[key]);
    seperator = "&";
  }
  return str;
}


toParam({ cost: 12345, insertBy: 'testUser' })
"cost=12345&insertBy=testUser"
Jim Blackler
  • 22,946
  • 12
  • 85
  • 101
3
var myObj = { cost: 12345, insertBy: 'testUser' },
    param = '',
    url   = 'http://mysite.com/mypage.php';    

for (var p in myObj) {
  if (myObj.hasOwnProperty(p)) {
    param += encodeURIComponent(p) + "=" + encodeURIComponent(myObj[p]) + "&";
  }
}

window.location.href = url + "?" + param;
mVChr
  • 49,587
  • 11
  • 107
  • 104
1

you can use this

function serialize(obj)
{
    let str = []

    for(var p in obj)
    {
      if(obj.hasOwnProperty(p)) str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]))
    }

    return str.join('&')
}

try on JSFiddle on this link https://jsfiddle.net/yussan/kwmnkca6/

yussan
  • 2,277
  • 1
  • 20
  • 24
1

ES6 version of Jrop's answer (also parses nested params)

const toQueryString = (obj, urlEncode = false) => {
  if (!obj) return null;
  const flattenObj = (x, path = []) => {
    const result = [];
    Object.keys(x).forEach((key) => {
      if (!Object.prototype.hasOwnProperty.call(x, key)) return;
      const newPath = path.slice();
      newPath.push(key);
      let vals = [];
      if (typeof x[key] === 'object') {
        vals = flattenObj(x[key], newPath);
      } else {
        vals.push({ path: newPath, val: x[key] });
      }
      vals.forEach((v) => {
        return result.push(v);
      });
    });
    return result;
  };

  let parts = flattenObj(obj);
  parts = parts.map((varInfo) => {
    if (varInfo.path.length === 1) {
      varInfo.path = varInfo.path[0]; // eslint-disable-line no-param-reassign
    } else {
      const first = varInfo.path[0];
      const rest = varInfo.path.slice(1);
      varInfo.path = `${first}[${rest.join('][')}]`; // eslint-disable-line no-param-reassign
    }
    return varInfo;
  });

  const queryString = parts.map((varInfo) => {
    return `${varInfo.path}=${varInfo.val}`;
  }).join('&');
  if (urlEncode) {
    return encodeURIComponent(queryString);
  }
  return queryString;
};
Mythical Fish
  • 293
  • 2
  • 7
  • 20
  • 1
    While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. Please read this [how-to-answer](http://stackoverflow.com/help/how-to-answer) for providing quality answer. – thewaywewere Jun 25 '17 at 15:15
1

Using Lodash library it can be done as:

let data = {}

_.map(data, (value, key) => `${key}=${value}`)
.join("&");

Note that this library has been imported as:

window._ = require('lodash');
Bedram Tamang
  • 3,748
  • 31
  • 27