Related
Is there a fast and simple way to encode a JavaScript object into a string that I can pass via a GET request?
No jQuery, no other frameworks—just plain JavaScript :)
Like this:
serialize = function(obj) {
var str = [];
for (var p in obj)
if (obj.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
return str.join("&");
}
console.log(serialize({
foo: "hi there",
bar: "100%"
}));
// foo=hi%20there&bar=100%25
This one also converts recursive objects (using PHP "array" notation for the query string):
serialize = function(obj, prefix) {
var str = [],
p;
for (p in obj) {
if (obj.hasOwnProperty(p)) {
var k = prefix ? prefix + "[" + p + "]" : p,
v = obj[p];
str.push((v !== null && typeof v === "object") ?
serialize(v, k) :
encodeURIComponent(k) + "=" + encodeURIComponent(v));
}
}
return str.join("&");
}
console.log(serialize({
foo: "hi there",
bar: {
blah: 123,
quux: [1, 2, 3]
}
}));
// foo=hi%20there&bar%5Bblah%5D=123&bar%5Bquux%5D%5B0%5D=1&bar%5Bquux%5D%5B1%5D=2&bar%5Bquux%5D%5B2%5D=3
Just use URLSearchParams This works in all current browsers
new URLSearchParams(object).toString()
jQuery has a function for this, jQuery.param(). If you're already using it, you can use this:
Example:
var params = { width:1680, height:1050 };
var str = jQuery.param( params );
str now contains width=1680&height=1050.
I suggest using the URLSearchParams interface:
const searchParams = new URLSearchParams();
const params = {foo: "hi there", bar: "100%" };
Object.keys(params).forEach(key => searchParams.append(key, params[key]));
console.log(searchParams.toString())
Or by passing the search object into the constructor like this:
const params = {foo: "hi there", bar: "100%" };
const queryString = new URLSearchParams(params).toString();
console.log(queryString);
Use:
Object.keys(obj).reduce(function(a,k){a.push(k+'='+encodeURIComponent(obj[k]));return a},[]).join('&')
I like this one-liner, but I bet it would be a more popular answer if it matched the accepted answer semantically:
function serialize( obj ) {
let str = '?' + Object.keys(obj).reduce(function(a, k){
a.push(k + '=' + encodeURIComponent(obj[k]));
return a;
}, []).join('&');
return str;
}
Here's a one liner in ES6:
Object.keys(obj).map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`).join('&');
With Node.js v6.6.3
const querystring = require('querystring')
const obj = {
foo: 'bar',
baz: 'tor'
}
let result = querystring.stringify(obj)
// foo=bar&baz=tor
Reference: Query string
Ruby on Rails and PHP style query builder
This method converts a JavaScript object into a URI query string. It also handles nested arrays and objects (in Ruby on Rails and PHP syntax):
function serializeQuery(params, prefix) {
const query = Object.keys(params).map((key) => {
const value = params[key];
if (params.constructor === Array)
key = `${prefix}[]`;
else if (params.constructor === Object)
key = (prefix ? `${prefix}[${key}]` : key);
if (typeof value === 'object')
return serializeQuery(value, key);
else
return `${key}=${encodeURIComponent(value)}`;
});
return [].concat.apply([], query).join('&');
}
Example Usage:
let params = {
a: 100,
b: 'has spaces',
c: [1, 2, 3],
d: { x: 9, y: 8}
}
serializeQuery(params)
// returns 'a=100&b=has%20spaces&c[]=1&c[]=2&c[]=3&d[x]=9&d[y]=8
A small amendment to the accepted solution by user187291:
serialize = function(obj) {
var str = [];
for(var p in obj){
if (obj.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
}
return str.join("&");
}
Checking for hasOwnProperty on the object makes JSLint and JSHint happy, and it prevents accidentally serializing methods of the object or other stuff if the object is anything but a simple dictionary. See the paragraph on for statements on Code Conventions for the JavaScript Programming Language.
Well, everyone seems to put his one-liner here so here goes mine:
const encoded = Object.entries(obj).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join("&");
If you need to send arbitrary objects, then GET is a bad idea since there are limits to the lengths of URLs that user agents and web servers will accepts. My suggestion would be to build up an array of name-value pairs to send and then build up a query string:
function QueryStringBuilder() {
var nameValues = [];
this.add = function(name, value) {
nameValues.push( {name: name, value: value} );
};
this.toQueryString = function() {
var segments = [], nameValue;
for (var i = 0, len = nameValues.length; i < len; i++) {
nameValue = nameValues[i];
segments[i] = encodeURIComponent(nameValue.name) + "=" + encodeURIComponent(nameValue.value);
}
return segments.join("&");
};
}
var qsb = new QueryStringBuilder();
qsb.add("veg", "cabbage");
qsb.add("vegCount", "5");
alert( qsb.toQueryString() );
A little bit look better
objectToQueryString(obj, prefix) {
return Object.keys(obj).map(objKey => {
if (obj.hasOwnProperty(objKey)) {
const key = prefix ? `${prefix}[${objKey}]` : objKey;
const value = obj[objKey];
return typeof value === "object" ?
this.objectToQueryString(value, key) :
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}
return null;
}).join("&");
}
This one skips null/undefined values
export function urlEncodeQueryParams(data) {
const params = Object.keys(data).map(key => data[key] ? `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}` : '');
return params.filter(value => !!value).join('&');
}
Here's the CoffeeScript version of the accepted answer.
serialize = (obj, prefix) ->
str = []
for p, v of obj
k = if prefix then prefix + "[" + p + "]" else p
if typeof v == "object"
str.push(serialize(v, k))
else
str.push(encodeURIComponent(k) + "=" + encodeURIComponent(v))
str.join("&")
Here's a concise & recursive version with Object.entries. It handles arbitrarily nested arrays, but not nested objects. It also removes empty elements:
const format = (k,v) => v !== null ? `${k}=${encodeURIComponent(v)}` : ''
const to_qs = (obj) => {
return [].concat(...Object.entries(obj)
.map(([k,v]) => Array.isArray(v)
? v.map(arr => to_qs({[k]:arr}))
: format(k,v)))
.filter(x => x)
.join('&');
}
E.g.:
let json = {
a: [1, 2, 3],
b: [], // omit b
c: 1,
d: "test&encoding", // uriencode
e: [[4,5],[6,7]], // flatten this
f: null, // omit nulls
g: 0
};
let qs = to_qs(json)
=> "a=1&a=2&a=3&c=1&d=test%26encoding&e=4&e=5&e=6&e=7&g=0"
Use:
const toQueryString = obj => "?".concat(Object.keys(obj).map(e => `${encodeURIComponent(e)}=${encodeURIComponent(obj[e])}`).join("&"));
const data = {
offset: 5,
limit: 10
};
toQueryString(data); // => ?offset=5&limit=10
Or use a predefined feature
const data = {
offset: 5,
limit: 10
};
new URLSearchParams(data).toString(); // => ?offset=5&limit=10
Note
Both the above methods will set the value as null if not present.
If you want not to set the query parameter if value is null then use:
const toQueryString = obj => "?".concat(Object.keys(obj).map(e => obj[e] ? `${encodeURIComponent(e)}=${encodeURIComponent(obj[e])}` : null).filter(e => !!e).join("&"));
const data = {
offset: null,
limit: 10
};
toQueryString(data); // => "?limit=10" else with above methods "?offset=null&limit=10"
You can freely use any method.
In ES7 you can write this in one line:
const serialize = (obj) => (Object.entries(obj).map(i => [i[0], encodeURIComponent(i[1])].join('=')).join('&'))
I have a simpler solution that does not use any third-party library and is already apt to be used in any browser that has "Object.keys" (aka all modern browsers + edge + ie):
In ES5
function(a){
if( typeof(a) !== 'object' )
return '';
return `?${Object.keys(a).map(k=>`${k}=${a[k]}`).join('&')}`;
}
In ES3
function(a){
if( typeof(a) !== 'object' )
return '';
return '?' + Object.keys(a).map(function(k){ return k + '=' + a[k] }).join('&');
}
I made a comparison of JSON stringifiers and the results are as follows:
JSON: {"_id":"5973782bdb9a930533b05cb2","isActive":true,"balance":"$1,446.35","age":32,"name":"Logan Keller","email":"logankeller#artiq.com","phone":"+1 (952) 533-2258","friends":[{"id":0,"name":"Colon Salazar"},{"id":1,"name":"French Mcneil"},{"id":2,"name":"Carol Martin"}],"favoriteFruit":"banana"}
Rison: (_id:'5973782bdb9a930533b05cb2',age:32,balance:'$1,446.35',email:'logankeller#artiq.com',favoriteFruit:banana,friends:!((id:0,name:'Colon Salazar'),(id:1,name:'French Mcneil'),(id:2,name:'Carol Martin')),isActive:!t,name:'Logan Keller',phone:'+1 (952) 533-2258')
O-Rison: _id:'5973782bdb9a930533b05cb2',age:32,balance:'$1,446.35',email:'logankeller#artiq.com',favoriteFruit:banana,friends:!((id:0,name:'Colon Salazar'),(id:1,name:'French Mcneil'),(id:2,name:'Carol Martin')),isActive:!t,name:'Logan Keller',phone:'+1 (952) 533-2258'
JSURL: ~(_id~'5973782bdb9a930533b05cb2~isActive~true~balance~'!1*2c446.35~age~32~name~'Logan*20Keller~email~'logankeller*40artiq.com~phone~'*2b1*20*28952*29*20533-2258~friends~(~(id~0~name~'Colon*20Salazar)~(id~1~name~'French*20Mcneil)~(id~2~name~'Carol*20Martin))~favoriteFruit~'banana)
QS: _id=5973782bdb9a930533b05cb2&isActive=true&balance=$1,446.35&age=32&name=Logan Keller&email=logankeller#artiq.com&phone=+1 (952) 533-2258&friends[0][id]=0&friends[0][name]=Colon Salazar&friends[1][id]=1&friends[1][name]=French Mcneil&friends[2][id]=2&friends[2][name]=Carol Martin&favoriteFruit=banana
URLON: $_id=5973782bdb9a930533b05cb2&isActive:true&balance=$1,446.35&age:32&name=Logan%20Keller&email=logankeller#artiq.com&phone=+1%20(952)%20533-2258&friends#$id:0&name=Colon%20Salazar;&$id:1&name=French%20Mcneil;&$id:2&name=Carol%20Martin;;&favoriteFruit=banana
QS-JSON: isActive=true&balance=%241%2C446.35&age=32&name=Logan+Keller&email=logankeller%40artiq.com&phone=%2B1+(952)+533-2258&friends(0).id=0&friends(0).name=Colon+Salazar&friends(1).id=1&friends(1).name=French+Mcneil&friends(2).id=2&friends(2).name=Carol+Martin&favoriteFruit=banana
The shortest among them is URL Object Notation.
There another popular library, qs. You can add it by:
yarn add qs
And then use it like this:
import qs from 'qs'
const array = { a: { b: 'c' } }
const stringified = qs.stringify(array, { encode: false })
console.log(stringified) //-- outputs a[b]=c
ES6 solution for query string encoding of a JavaScript object
const params = {
a: 1,
b: 'query stringify',
c: null,
d: undefined,
f: '',
g: { foo: 1, bar: 2 },
h: ['Winterfell', 'Westeros', 'Braavos'],
i: { first: { second: { third: 3 }}}
}
static toQueryString(params = {}, prefix) {
const query = Object.keys(params).map((k) => {
let key = k;
const value = params[key];
if (!value && (value === null || value === undefined || isNaN(value))) {
value = '';
}
switch (params.constructor) {
case Array:
key = `${prefix}[]`;
break;
case Object:
key = (prefix ? `${prefix}[${key}]` : key);
break;
}
if (typeof value === 'object') {
return this.toQueryString(value, key); // for nested objects
}
return `${key}=${encodeURIComponent(value)}`;
});
return query.join('&');
}
toQueryString(params)
"a=1&b=query%20stringify&c=&d=&f=&g[foo]=1&g[bar]=2&h[]=Winterfell&h[]=Westeros&h[]=Braavos&i[first][second][third]=3"
A single line to convert an object into a query string in case somebody needs it again:
let Objs = { a: 'obejct-a', b: 'object-b' }
Object.keys(objs).map(key => key + '=' + objs[key]).join('&')
// The result will be a=object-a&b=object-b
This is an addition for the accepted solution. This works with objects and array of objects:
parseJsonAsQueryString = function (obj, prefix, objName) {
var str = [];
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
var v = obj[p];
if (typeof v == "object") {
var k = (objName ? objName + '.' : '') + (prefix ? prefix + "[" + p + "]" : p);
str.push(parseJsonAsQueryString(v, k));
} else {
var k = (objName ? objName + '.' : '') + (prefix ? prefix + '.' + p : p);
str.push(encodeURIComponent(k) + "=" + encodeURIComponent(v));
//str.push(k + "=" + v);
}
}
}
return str.join("&");
}
Also I have added objName if you're using object parameters, like in ASP.NET MVC action methods.
If you want to convert a nested object recursively and the object may or may not contain arrays (and the arrays may contain objects or arrays, etc), then the solution gets a little more complex. This is my attempt.
I've also added some options to choose if you want to record for each object member at what depth in the main object it sits, and to choose if you want to add a label to the members that come from converted arrays.
Ideally you should test if the thing parameter really receives an object or array.
function thingToString(thing,maxDepth,recordLevel,markArrays){
//thing: object or array to be recursively serialized
//maxDepth (int or false):
// (int) how deep to go with converting objects/arrays within objs/arrays
// (false) no limit to recursive objects/arrays within objects/arrays
//recordLevel (boolean):
// true - insert "(level 1)" before transcript of members at level one (etc)
// false - just
//markArrays (boolean):
// insert text to indicate any members that came from arrays
var result = "";
if (maxDepth !== false && typeof maxDepth != 'number') {maxDepth = 3;}
var runningDepth = 0;//Keeps track how deep we're into recursion
//First prepare the function, so that it can call itself recursively
function serializeAnything(thing){
//Set path-finder values
runningDepth += 1;
if(recordLevel){result += "(level " + runningDepth + ")";}
//First convert any arrays to object so they can be processed
if (thing instanceof Array){
var realObj = {};var key;
if (markArrays) {realObj['type'] = "converted array";}
for (var i = 0;i < thing.length;i++){
if (markArrays) {key = "a" + i;} else {key = i;}
realObj[key] = thing[i];
}
thing = realObj;
console.log('converted one array to ' + typeof realObj);
console.log(thing);
}
//Then deal with it
for (var member in thing){
if (typeof thing[member] == 'object' && runningDepth < maxDepth){
serializeAnything(thing[member]);
//When a sub-object/array is serialized, it will add one to
//running depth. But when we continue to this object/array's
//next sibling, the level must go back up by one
runningDepth -= 1;
} else if (maxDepth !== false && runningDepth >= maxDepth) {
console.log('Reached bottom');
} else
if (
typeof thing[member] == "string" ||
typeof thing[member] == 'boolean' ||
typeof thing[member] == 'number'
){
result += "(" + member + ": " + thing[member] + ") ";
} else {
result += "(" + member + ": [" + typeof thing[member] + " not supported]) ";
}
}
}
//Actually kick off the serialization
serializeAnything(thing);
return result;
}
This is a solution that will work for .NET backends out of the box. I have taken the primary answer of this thread and updated it to fit our .NET needs.
function objectToQuerystring(params) {
var result = '';
function convertJsonToQueryString(data, progress, name) {
name = name || '';
progress = progress || '';
if (typeof data === 'object') {
Object.keys(data).forEach(function (key) {
var value = data[key];
if (name == '') {
convertJsonToQueryString(value, progress, key);
} else {
if (isNaN(parseInt(key))) {
convertJsonToQueryString(value, progress, name + '.' + key);
} else {
convertJsonToQueryString(value, progress, name + '[' + key+ ']');
}
}
})
} else {
result = result ? result.concat('&') : result.concat('?');
result = result.concat(`${name}=${data}`);
}
}
convertJsonToQueryString(params);
return result;
}
To do it in a better way.
It can handle recursive objects or arrays in the standard query form, like a=val&b[0]=val&b[1]=val&c=val&d[some key]=val. Here's the final function.
Logic, Functionality
const objectToQueryString = (initialObj) => {
const reducer = (obj, parentPrefix = null) => (prev, key) => {
const val = obj[key];
key = encodeURIComponent(key);
const prefix = parentPrefix ? `${parentPrefix}[${key}]` : key;
if (val == null || typeof val === 'function') {
prev.push(`${prefix}=`);
return prev;
}
if (['number', 'boolean', 'string'].includes(typeof val)) {
prev.push(`${prefix}=${encodeURIComponent(val)}`);
return prev;
}
prev.push(Object.keys(val).reduce(reducer(val, prefix), []).join('&'));
return prev;
};
return Object.keys(initialObj).reduce(reducer(initialObj), []).join('&');
};
Example
const testCase1 = {
name: 'Full Name',
age: 30
}
const testCase2 = {
name: 'Full Name',
age: 30,
children: [
{name: 'Child foo'},
{name: 'Foo again'}
],
wife: {
name: 'Very Difficult to say here'
}
}
console.log(objectToQueryString(testCase1));
console.log(objectToQueryString(testCase2));
Live Test
Expand the snippet below to verify the result in your browser -
const objectToQueryString = (initialObj) => {
const reducer = (obj, parentPrefix = null) => (prev, key) => {
const val = obj[key];
key = encodeURIComponent(key);
const prefix = parentPrefix ? `${parentPrefix}[${key}]` : key;
if (val == null || typeof val === 'function') {
prev.push(`${prefix}=`);
return prev;
}
if (['number', 'boolean', 'string'].includes(typeof val)) {
prev.push(`${prefix}=${encodeURIComponent(val)}`);
return prev;
}
prev.push(Object.keys(val).reduce(reducer(val, prefix), []).join('&'));
return prev;
};
return Object.keys(initialObj).reduce(reducer(initialObj), []).join('&');
};
const testCase1 = {
name: 'Full Name',
age: 30
}
const testCase2 = {
name: 'Full Name',
age: 30,
children: [
{name: 'Child foo'},
{name: 'Foo again'}
],
wife: {
name: 'Very Difficult to say here'
}
}
console.log(objectToQueryString(testCase1));
console.log(objectToQueryString(testCase2));
Things to consider.
It skips values for functions, null, and undefined
It skips keys and values for empty objects and arrays.
It doesn't handle Number or String objects made with new Number(1) or new String('my string') because no one should ever do that
ok, it's a older post but i'm facing this problem and i have found my personal solution.. maybe can help someone else..
function objToQueryString(obj){
var k = Object.keys(obj);
var s = "";
for(var i=0;i<k.length;i++) {
s += k[i] + "=" + encodeURIComponent(obj[k[i]]);
if (i != k.length -1) s += "&";
}
return s;
};
URLSearchParams looks good, but it didn't work for nested objects.
Try to use
encodeURIComponent(JSON.stringify(object))
The previous answers do not work if you have a lot of nested objects.
Instead you can pick the function parameter from jquery-param/jquery-param.js. It worked very well for me!
var param = function (a) {
var s = [], rbracket = /\[\]$/,
isArray = function (obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}, add = function (k, v) {
v = typeof v === 'function' ? v() : v === null ? '' : v === undefined ? '' : v;
s[s.length] = encodeURIComponent(k) + '=' + encodeURIComponent(v);
}, buildParams = function (prefix, obj) {
var i, len, key;
if (prefix) {
if (isArray(obj)) {
for (i = 0, len = obj.length; i < len; i++) {
if (rbracket.test(prefix)) {
add(prefix, obj[i]);
} else {
buildParams(prefix + '[' + (typeof obj[i] === 'object' ? i : '') + ']', obj[i]);
}
}
} else if (obj && String(obj) === '[object Object]') {
for (key in obj) {
buildParams(prefix + '[' + key + ']', obj[key]);
}
} else {
add(prefix, obj);
}
} else if (isArray(obj)) {
for (i = 0, len = obj.length; i < len; i++) {
add(obj[i].name, obj[i].value);
}
} else {
for (key in obj) {
buildParams(key, obj[key]);
}
}
return s;
};
return buildParams('', a).join('&').replace(/%20/g, '+');
};
After going through some top answers here, I have wrote another implementation that tackles some edge cases as well
function serialize(params, prefix) {
return Object.entries(params).reduce((acc, [key, value]) => {
// remove whitespace from both sides of the key before encoding
key = encodeURIComponent(key.trim());
if (params.constructor === Array ) {
key = `${prefix}[]`;
} else if (params.constructor === Object) {
key = (prefix ? `${prefix}[${key}]` : key);
}
/**
* - undefined and NaN values will be skipped automatically
* - value will be empty string for functions and null
* - nested arrays will be flattened
*/
if (value === null || typeof value === 'function') {
acc.push(`${key}=`);
} else if (typeof value === 'object') {
acc = acc.concat(serialize(value, key));
} else if(['number', 'boolean', 'string'].includes(typeof value) && value === value) { // self-check to avoid NaN
acc.push(`${key}=${encodeURIComponent(value)}`);
}
return acc;
}, []);
}
function objectToQueryString(queryParameters) {
return queryParameters ? serialize(queryParameters).join('&'): '';
}
let x = objectToQueryString({
foo: 'hello world',
bar: {
blah: 123,
list: [1, 2, 3],
'nested array': [[4,5],[6,7]] // will be flattened
},
page: 1,
limit: undefined, // field will be ignored
check: false,
max: NaN, // field will be ignored
prop: null,
' key value': 'with spaces' // space in key will be trimmed out
});
console.log(x); // foo=hello%20world&bar[blah]=123&bar[list][]=1&bar[list][]=2&bar[list][]=3&bar[nested%20array][][]=4&bar[nested%20array][][]=5&bar[nested%20array][][]=6&bar[nested%20array][][]=7&page=1&check=false&prop=&key%20value=with%20spaces
I have the following object:
var ob = {
view: {
name: 'zpo',
params: {
taskId: 3,
zuka: 'v'
}
}
}
I need to have this object in the following form:
{
"view.name":"zpo",
"view.params.taskId":3,
"view.params.zuka":"v"
}
I have written a function that can do that, but the problem is that it requires external variables passed to it. Here is this function:
function inline(o, result, container) {
for (var p in o) {
if (typeof o[p] === "object") {
inline(o[p], result.length > 0 ? result+'.'+p : p, container);
} else {
container[result + '.' + p] = o[p];
}
}
}
var ob = {
view: {
name: 'zpo',
params: {
taskId: 3,
zuka: 'v'
}
}
}
var c = {};
var r = inline(ob, '', c);
Is there any way to write this function to return correct result without the need to pass result and container external variables?
If i understood you correctly, you want to avoid to call your inline() function with "empty" params.
You could catch this case in your function directly:
function inline(o, result, container) {
result = result || '';
container = container || {};
...
}
var r = inline(ob);
you would still need this params for the recursive part of your function.
Here is a version that does not require any parameters.
// Return an array containing the [key, value] couples of an object.
const objEntries = o => Object.keys(o).map(k => [k, o[k]]);
// Create an object from an array of [key, value] couples.
const entriesObj = (entries, init={}) => entries.reduce((result, [key, val]) => {
result[key] = val;
return result;
}, init);
// Reduce on the object entries (as returned by objEntries) with an empty object as
// initialiser.
const inline = (o) => objEntries(o).reduce((result, [key, val]) => {
if(val instanceof Object){
// If the value is an object, recursively inline it.
const inlineVal = inline(val);
// Prefix each property names of inlineVal with the key of val and add the
// properties to the result object.
entriesObj(
objEntries(inlineVal).map(([subK, subVal]) => [key + '.' + subK, subVal]),
result
);
} else {
// If val is not an object, just add it to the result as is.
result[key] = val;
}
// Return the result.
return result;
}, {});
var ob = {
view: {
name: 'zpo',
params: {
taskId: 3,
zuka: 'v'
}
}
}
var r = inline(ob);
console.log(r);
I used arrow functions and destructuring. Old browsers won't support it. If you need to support them, just replace the arrow functions with regular functions and manually destructure the arguments.
javascript is awesome!
function inline(o, result, container) {
result = result || '';
container = container || {};
for (var p in o) {
if (typeof o[p] === "object") {
inline(o[p], result.length > 0 ? result+'.'+p : p, container);
} else {
container[result + '.' + p] = o[p];
}
}
}
var r = inline(ob);
I realize that fundamentally I'm probably going about this the wrong way so I'm open to any pushes in the right direction.
I'm trying to use the HipChat API to send a notification to a room like so:
https://www.hipchat.com/docs/api/method/rooms/message
I'm trying to build the URL in the example with a js object's parameters, so basically I'm trying to convert this:
var hipChatSettings = {
format:"json",
auth_token:token,
room_id: 1,
from: "Notifications",
message: "Message"
}
To this:
https://api.hipchat.com/v1/rooms/message?format=json&auth_token=token&room_id=1&from=Notifications&message=Message
You should check this jQuery.param function.
var params = { width:1680, height:1050 };
var str = jQuery.param( params );
console.log(str);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Object.keys(hipChatSettings).map(function(k) {
return encodeURIComponent(k) + "=" + encodeURIComponent(hipChatSettings[k]);
}).join('&')
// => "format=json&auth_token=token&room_id=1&from=Notifications&message=Message"
Warning: newish JavaScript. If you want it to work on ancients, shim or rewrite into for.
Something like this could work for you
var str = "?" + Object.keys(hipChatSettings).map(function(prop) {
return [prop, hipChatSettings[prop]].map(encodeURIComponent).join("=");
}).join("&");
// "?format=json&auth_token=token&room_id=1&from=Notifications&message=Message"
If you can't depend on ECMAScript 5, you can use a simple for loop
var pairs = [];
for (var prop in hipChatSettings) {
if (hipChatSettings.hasOwnProperty(prop)) {
var k = encodeURIComponent(prop),
v = encodeURIComponent(hipChatSettings[prop]);
pairs.push( k + "=" + v);
}
}
var str = "?" + pairs.join("&");
ES6 version, can do really nested objects with arrays
encodeURI(getUrlString({a: 1, b: [true, 12.3, "string"]}))
getUrlString (params, keys = [], isArray = false) {
const p = Object.keys(params).map(key => {
let val = params[key]
if ("[object Object]" === Object.prototype.toString.call(val) || Array.isArray(val)) {
if (Array.isArray(params)) {
keys.push("")
} else {
keys.push(key)
}
return getUrlString(val, keys, Array.isArray(val))
} else {
let tKey = key
if (keys.length > 0) {
const tKeys = isArray ? keys : [...keys, key]
tKey = tKeys.reduce((str, k) => { return "" === str ? k : `${str}[${k}]` }, "")
}
if (isArray) {
return `${ tKey }[]=${ val }`
} else {
return `${ tKey }=${ val }`
}
}
}).join('&')
keys.pop()
return p
}
Late to the dance but I quite enjoyed the brevity of this:
Object.entries(hipChatSettings)
.map(
([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`
)
.join("&");
I have an object like
{ "status": "success", "auth": { "code": "23123213", "name": "qwerty asdfgh" } }
I want to convert it to dot notation (one level) version like:
{ "status": "success", "auth.code": "23123213", "auth.name": "qwerty asdfgh" }
Currently I am converting the object by hand using fields but I think there should be a better and more generic way to do this. Is there any?
Note: There are some examples showing the opposite way, but i couldn't find the exact method.
Note 2: I want it for to use with my serverside controller action binding.
You can recursively add the properties to a new object, and then convert to JSON:
var res = {};
(function recurse(obj, current) {
for(var key in obj) {
var value = obj[key];
var newKey = (current ? current + "." + key : key); // joined key with dot
if(value && typeof value === "object") {
recurse(value, newKey); // it's a nested object, so do it again
} else {
res[newKey] = value; // it's not an object, so set the property
}
}
})(obj);
var result = JSON.stringify(res); // convert result to JSON
Here is a fix/hack for when you get undefined for the first prefix. (I did)
var dotize = dotize || {};
dotize.parse = function(jsonobj, prefix) {
var newobj = {};
function recurse(o, p) {
for (var f in o)
{
var pre = (p === undefined ? '' : p + ".");
if (o[f] && typeof o[f] === "object"){
newobj = recurse(o[f], pre + f);
} else {
newobj[pre + f] = o[f];
}
}
return newobj;
}
return recurse(jsonobj, prefix);
};
You can use the NPM dot-object (Github) for transform to object to dot notation and vice-versa.
var dot = require('dot-object');
var obj = {
id: 'my-id',
nes: { ted: { value: true } },
other: { nested: { stuff: 5 } },
some: { array: ['A', 'B'] }
};
var tgt = dot.dot(obj);
Produces
{
"id": "my-id",
"nes.ted.value": true,
"other.nested.stuff": 5,
"some.array[0]": "A",
"some.array[1]": "B"
}
const sourceObj = { "status": "success", "auth": { "code": "23123213", "name": "qwerty asdfgh" } }
;
const { auth, ...newObj } = sourceObj;
const resultObj = {
...newObj,
..._.mapKeys(auth, (val, key) => `auth.${key}`)
}
// function approach
const dotizeField = (obj, key) => {
const { ...newObj } = sourceObj;
delete newObj[key];
return {
...newObj,
..._.mapKeys(obj[key], (val, subKey) => `${key}.${subKey}`)
}
}
const resultObj2 = dotizeField(sourceObj, 'auth');
console.log(sourceObj, resultObj, resultObj2);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.20/lodash.min.js"></script>
i have done some fix:
export function toDotNotation(obj,res={}, current='') {
for(const key in obj) {
let value = obj[key];
let newKey = (current ? current + "." + key : key); // joined key with dot
if(value && typeof value === "object") {
toDotNotation(value,res, newKey); // it's a nested object, so do it again
} else {
res[newKey] = value; // it's not an object, so set the property
}
}
return res;
}
I wrote another function with prefix feature. I couldn't run your code but I got the answer.
Thanks
https://github.com/vardars/dotize
var dotize = dotize || {};
dotize.convert = function(jsonobj, prefix) {
var newobj = {};
function recurse(o, p, isArrayItem) {
for (var f in o) {
if (o[f] && typeof o[f] === "object") {
if (Array.isArray(o[f]))
newobj = recurse(o[f], (p ? p + "." : "") + f, true); // array
else {
if (isArrayItem)
newobj = recurse(o[f], (p ? p : "") + "[" + f + "]"); // array item object
else
newobj = recurse(o[f], (p ? p + "." : "") + f); // object
}
} else {
if (isArrayItem)
newobj[p + "[" + f + "]"] = o[f]; // array item primitive
else
newobj[p + "." + f] = o[f]; // primitive
}
}
return newobj;
}
return recurse(jsonobj, prefix);
};
Following what #pimvdb did (a compact and effective solution he submitted), I added a little modification that allows me have a function that can be easily exported:
function changeObjectToDotNotationFormat(inputObject, current, prefinalObject) {
const result = prefinalObject ? prefinalObject : {}; // This allows us to use the most recent result object in the recursive call
for (let key in inputObject) {
let value = inputObject[key];
let newKey = current ? `${current}.${key}` : key;
if (value && typeof value === "object") {
changeObjectToDotNotationFormat(value, newKey, result);
} else {
result[newKey] = value;
}
}
return result;
}
i think this would be more elegant...
const toDotNot = (input, parentKey) => Object.keys(input || {}).reduce((acc, key) => {
const value = input[key];
const outputKey = parentKey ? `${parentKey}.${key}` : `${key}`;
// NOTE: remove `&& (!Array.isArray(value) || value.length)` to exclude empty arrays from the output
if (value && typeof value === 'object' && (!Array.isArray(value) || value.length)) return ({ ...acc, ...toDotNot(value, outputKey) });
return ({ ...acc, [outputKey]: value });
}, {});
const input = {a: {b: 'c', e: {f: ['g', null, {g: 'h'}]}}, d: []};
const output = toDotNot(input);
console.log(output);
results in:
// output:
{
"a.b": "c",
"a.e.f.0": "g",
"a.e.f.1": null,
"a.e.f.2.g": "h",
"d": []
}
There are already lots of answers here, but for Typescript this solution works pretty well for me and is typed:
type EncapsulatedStringObject = Record<string, string | object>;
export function convertKeysToDotNotation( object: EncapsulatedStringObject, prefix: string = '' ): Record<string, string> {
const result: Record<string, string> = {};
Object.keys( object ).forEach( key => {
const newPrefix = prefix ? `${prefix}.${key}` : key;
const value = object[ key ];
if ( typeof value === 'object' ) {
Object.assign( result, convertKeysToDotNotation( object[ key ] as EncapsulatedStringObject, newPrefix ) );
} else {
result[ newPrefix ] = value;
}
} );
return result;
}
I've come up with
function keysToLowerCase (obj) {
var keys = Object.keys(obj);
var n = keys.length;
while (n--) {
var key = keys[n]; // "cache" it, for less lookups to the array
if (key !== key.toLowerCase()) { // might already be in its lower case version
obj[key.toLowerCase()] = obj[key] // swap the value to a new lower case key
delete obj[key] // delete the old key
}
}
return (obj);
}
But I'm not sure how will v8 behave with that, for instance, will it really delete the other keys or will it only delete references and the garbage collector will bite me later ?
Also, I created these tests, I'm hoping you could add your answer there so we could see how they match up.
EDIT 1:
Apparently, according to the tests, it's faster if we don't check if the key is already in lower case, but being faster aside, will it create more clutter by ignoring this and just creating new lower case keys ? Will the garbage collector be happy with this ?
The fastest I come up with is if you create a new object:
var key, keys = Object.keys(obj);
var n = keys.length;
var newobj={}
while (n--) {
key = keys[n];
newobj[key.toLowerCase()] = obj[key];
}
I'm not familiar enough with the current inner working of v8 to give you a definitive answer. A few years ago I saw a video where the developers talked about objects, and IIRC
it will only delete the references and let the garbage collector take care of it. But it was years ago so even if it was like that then, it doesn't need to be like that now.
Will it bite you later? It depends on what you are doing, but probably not. It is very common to create short lived objects so the code is optimized to handle it. But every environment has its limitations, and maybe it will bite you. You have to test with actual data.
Using Object.fromEntries (ES10)
Native and immutable solution using the new Object.fromEntries method:
const newObj = Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v])
);
Until that function becomes widely available you could define it yourself with the following polyfill:
Object.fromEntries = arr => Object.assign({}, ...Array.from(arr, ([k, v]) => ({[k]: v}) ));
A nice thing is that this method does the opposite of Object.entries, so now you can go back and forth between the object and array representation.
I'd use Lo-Dash.transform like this:
var lowerObj = _.transform(obj, function (result, val, key) {
result[key.toLowerCase()] = val;
});
Personally, I'd use:
let objectKeysToLowerCase = function (origObj) {
return Object.keys(origObj).reduce(function (newObj, key) {
let val = origObj[key];
let newVal = (typeof val === 'object') ? objectKeysToLowerCase(val) : val;
newObj[key.toLowerCase()] = newVal;
return newObj;
}, {});
}
It's succinct, recurs to handle nested objects and returns a new object rather than modifying the original.
In my limited local testing this function is faster than the other recursive solution currently listed (once fixed). I'd love to benchmark it against the others but jsperf is down at the moment (???).
It's also written in ES5.1 so, according to the docs on MDN, should work in FF 4+, Chrome 5+, IE 9.0+, Opera 12+, Safari 5+ (so, pretty much everything).
Object.keys()
Array. prototype.reduce()
Vanilla JS for the win.
I wouldn't worry too much about the garbage collection aspect of all this. Once all references to the old object are destroyed it will be GC's but the new object will still reference basically all it's properties, so they will not.
Any Functions, Arrays or RegExp will be "copied" across by reference. In terms of memory, even Strings will not be duplicated by this process since most (all?) modern JS engines user string interning. I think that leaves just the Numbers, Booleans and the Objects that formed the original structure left to be GC'd.
Note that (all implementations of) this process will lose values if the original has multiple properties with the same lowercase representation. Ie:
let myObj = { xx: 'There', xX: 'can be', Xx: 'only', XX: 'one!' };
console.log(myObj);
// { xx: 'There', xX: 'can be', Xx: 'only', XX: 'one!' }
let newObj = objectKeysToLowerCase(myObj);
console.log(newObj);
// { xx: 'one!' }
Of course, sometimes this is exactly what you want.
Update 2018-07-17
A few people have noted the original function doesn't work well with arrays. Here's an expanded, more resilient version. It recurs correctly through arrays and works if the initial value is an array or simple value:
let objectKeysToLowerCase = function (input) {
if (typeof input !== 'object') return input;
if (Array.isArray(input)) return input.map(objectKeysToLowerCase);
return Object.keys(input).reduce(function (newObj, key) {
let val = input[key];
let newVal = (typeof val === 'object') && val !== null ? objectKeysToLowerCase(val) : val;
newObj[key.toLowerCase()] = newVal;
return newObj;
}, {});
};
ES6 version:
Object.keys(source)
.reduce((destination, key) => {
destination[key.toLowerCase()] = source[key];
return destination;
}, {});
The loDash/fp way, quite nice as its essentially a one liner
import {
mapKeys
} from 'lodash/fp'
export function lowerCaseObjectKeys (value) {
return mapKeys(k => k.toLowerCase(), value)
}
Using forEach seems to be a bit quicker in my tests- and the original reference is gone, so deleting the new one will put it in reach of the g.c.
function keysToLowerCase(obj){
Object.keys(obj).forEach(function (key) {
var k = key.toLowerCase();
if (k !== key) {
obj[k] = obj[key];
delete obj[key];
}
});
return (obj);
}
var O={ONE:1,two:2,tHree:3,FOUR:4,Five:5,SIX:{a:1,b:2,c:3,D:4,E:5}};
keysToLowerCase(O);
/* returned value: (Object) */
{
five:5,
four:4,
one:1,
six:{
a:1,
b:2,
c:3,
D:4,
E:5
},
three:3,
two:2
}
Simplified Answer
For simple situations, you can use the following example to convert all keys to lower case:
Object.keys(example).forEach(key => {
const value = example[key];
delete example[key];
example[key.toLowerCase()] = value;
});
You can convert all of the keys to upper case using toUpperCase() instead of toLowerCase():
Object.keys(example).forEach(key => {
const value = example[key];
delete example[key];
example[key.toUpperCase()] = value;
});
Here is easiest solution to convert all the json keys to lower case.
let o = {"Account_Number ":"0102301", "customer_NaME":"name"}
o = Object.keys(o).reduce((c, k) => (c[k.toLowerCase().trim()] = o[k], c), {})
console.log(o)
With TypeScript
/**
* Lowercase the keys of an object
* #example
lowercaseKeys({FOO: true, bAr: false}); // {foo: true, bar: false}
*/
export function lowercaseKeys<T>(object: { [key: string]: T }): { [key: string]: T } {
const result: { [key: string]: T } = {};
for (const [key, value] of Object.entries(object)) {
result[key.toLowerCase()] = value;
}
return result;
}
Usage
lowercaseKeys({FOO: true, bAr: false}); // {foo: true, bar: false}
I used ES6 and TypeScript.
toLowerCaseObject function takes an Array as parameter and looking through Object tree recursively and make every node lowercase:
function toLowerCaseObject(items: any[]) {
return items.map(x => {
let lowerCasedObject = {}
for (let i in x) {
if (x.hasOwnProperty(i)) {
lowerCased[i.toLowerCase()] = x[i] instanceof Array ? toLowerCaseObject(x[i]) : x[i];
}
}
return lowerCasedObject;
});
}
One-liner (only for top level keys):
Object.assign(...Object.keys(obj).map(key => ({[key.toLowerCase()]: obj[key]})))
Converts:
{ a: 1, B: 2, C: { Z: 4 } }
To:
{ a: 1, b: 2, c: { Z: 4 } }
While the ES10 Object.fromentries() method works
const newObj = Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v])
);
You can similarly use the snippet below for ES2015 and below
this.htmlWorkbookJSON = jsonData.map((element: Object) => {
let entriesArray = Object.entries(element)
const data = new Object()
entriesArray.forEach(([key, value]) => {
data[key.toLocaleLowerCase()] = value;
})
return data
})
This is not the cleanest way but it has worked for my team so it is worth sharing.
I created this method as our backend is running a language that is not case sensitive and the database and backend will produce different key cases. For us, it has worked flawlessly. Mind you we send dates as Strings and we don't send functions.
We have reduced it to this one line.
const toLowerCase = (data) => JSON.parse(JSON.stringify(data).replace(/"([^"]+)":/g, ($0, key) => '"' + key.toString().toLowerCase() + '":'))
We clone the object by using the JSON.parse(JSON.stringify(obj)) method. This produces a string version of the object in the JSON format. While the object is in the string form you can use regex as JSON is a predictable format to convert all keys.
Broken up it looks like this.
const toLowerCase = function (data) {
return JSON.parse(JSON.stringify(data)
.replace(/"([^"]+)":/g, ($0, key) => {
return '"' + key.toString().toLowerCase() + '":'
}))
}
const keysToLowerCase = object => {
return Object.keys(object).reduce((acc, key) => {
let val = object[key];
if (typeof val === 'object') {
val = keysToLowerCase(val);
}
acc[key.toLowerCase()] = val;
return acc;
}, {});
};
Works for nested object.
Consider lowering case just once, storing it in a lowKey var:
function keysToLowerCase (obj) {
var keys = Object.keys(obj);
var n = keys.length;
var lowKey;
while (n--) {
var key = keys[n];
if (key === (lowKey = key.toLowerCase()))
continue
obj[lowKey] = obj[key]
delete obj[key]
}
return (obj);
}
Here's my recursive version based on one of the above examples.
//updated function
var lowerObjKeys = function(obj) {
Object.keys(obj).forEach(function(key) {
var k = key.toLowerCase();
if (k != key) {
var v = obj[key]
obj[k] = v;
delete obj[key];
if (typeof v == 'object') {
lowerObjKeys(v);
}
}
});
return obj;
}
//plumbing
console = {
_createConsole: function() {
var pre = document.createElement('pre');
pre.setAttribute('id', 'console');
document.body.insertBefore(pre, document.body.firstChild);
return pre;
},
info: function(message) {
var pre = document.getElementById("console") || console._createConsole();
pre.textContent += ['>', message, '\n'].join(' ');
}
};
//test case
console.info(JSON.stringify(lowerObjKeys({
"StackOverflow": "blah",
"Test": {
"LULZ": "MEH"
}
}), true));
Beware, it doesn't track circular references, so you can end up with an infinite loop resulting in stack overflow.
For all values:
to_lower_case = function(obj) {
for (var k in obj){
if (typeof obj[k] == "object" && obj[k] !== null)
to_lower_case(obj[k]);
else if(typeof obj[k] == "string") {
obj[k] = obj[k].toLowerCase();
}
}
return obj;
}
Same can be used for keys with minor tweaks.
This is how I do it. My input can be anything and it recuses through nested objects as well as arrays of objects.
const fixKeys = input => Array.isArray(input)
? input.map(fixKeys)
: typeof input === 'object'
? Object.keys(input).reduce((acc, elem) => {
acc[elem.toLowerCase()] = fixKeys(input[elem])
return acc
}, {})
: input
tested using mocha
const { expect } = require('chai')
const fixKeys = require('../../../src/utils/fixKeys')
describe('utils/fixKeys', () => {
const original = {
Some: 'data',
With: {
Nested: 'data'
},
And: [
'an',
'array',
'of',
'strings'
],
AsWellAs: [
{ An: 'array of objects' }
]
}
const expected = {
some: 'data',
with: {
nested: 'data'
},
and: [
'an',
'array',
'of',
'strings'
],
aswellas: [{ an: 'array of objects' }]
}
let result
before(() => {
result = fixKeys(original)
})
it('left the original untouched', () => {
expect(original).not.to.deep.equal(expected)
})
it('fixed the keys', () => {
expect(result).to.deep.equal(expected)
})
})
var aa = {ID:1,NAME:'Guvaliour'};
var bb= {};
var cc = Object.keys(aa);
cc.forEach(element=>{
bb[element.toLowerCase()]=aa[element];
});
cosole.log(bb)
The below code to convert the all key in lower case
array.forEach(item=>{
let data = Object.keys(item).reduce((result, p) => (result[p.toLowerCase().trim()] = item[p], result), {})
if(data.hasOwnProperty(fieldname)){
if(data[fieldname]){
if(!response['data'].includes(data[fieldname].toLowerCase()))
response['data'].push(data[fieldname])
}
}
})
const newObj = {};
for(const key in obj){
newObj[key.toLowerCase()] = obj[key];
}
Most of the above answers do not handle null and undefined values. To get around it why not use the transform helper function from lodash?
const query = {
Company: 'GH Works',
Items: {
Construction: 'FB',
LineItems: {
Quantity: '100',
QUALity: 'checked'
}
}
}
function deepLowercaseKeys(hash) {
return _.transform(hash, function(result, value, key) {
const valueIsObject = typeof value === 'object';
result[key.toLowerCase()] = valueIsObject ? deepLowercaseKeys(value) : value;
});
}
console.log(deepLowercaseKeys(query))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
Additionally, you can customize the function and then use it to transform the object in any way you like.
const query = {
Company: 'GH Works',
Items: {
Construction: 'FB',
LineItems: {
Quantity: '100',
QUALity: 'checked'
}
}
}
// Base function
function deepTransform(hash, callback) {
return _.transform(hash, function(result, value, key) {
if (typeof value === 'object') {
return callback(result, deepTransform(value, callback), key)
}
callback(result, value, key)
})
}
// Custom function (can be anything)
function appendHello(hash) {
return deepTransform(hash, function(result, value, key) {
result[`${key}_hello`.toLowerCase()] = value;
})
}
console.log(appendHello(query))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
const objectToLowercase = (data) => {
const values = Object.values(data);
if (values.length === 0) {
return data;
}
return Object.keys(data).reduce((toLowerKeyObj, key) => {
const isObject = typeof data[key] === 'object';
const isArray = Array.isArray(data[key]);
let value = null;
if (isObject) {
if (!isArray) {
value = objectToLowercase(data[key]);
}
}
if (isArray) {
value = data[key].map(_value => {
return objectToLowercase(_value);
});
}
toLowerKeyObj[key.toLowerCase()] = isObject ? value : data[key];
return toLowerKeyObj;
}, {});
};