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
How can I loop through all members in a JavaScript object, including values that are objects?
For example, how could I loop through this (accessing the "your_name" and "your_message" for each)?
var validation_messages = {
"key_1": {
"your_name": "jimmy",
"your_msg": "hello world"
},
"key_2": {
"your_name": "billy",
"your_msg": "foo equals bar"
}
}
for (var key in validation_messages) {
// skip loop if the property is from prototype
if (!validation_messages.hasOwnProperty(key)) continue;
var obj = validation_messages[key];
for (var prop in obj) {
// skip loop if the property is from prototype
if (!obj.hasOwnProperty(prop)) continue;
// your code
alert(prop + " = " + obj[prop]);
}
}
Under ECMAScript 5, you can combine Object.keys() and Array.prototype.forEach():
var obj = {
first: "John",
last: "Doe"
};
//
// Visit non-inherited enumerable keys
//
Object.keys(obj).forEach(function(key) {
console.log(key, obj[key]);
});
In ES6/2015 you can loop through an object like this (using the arrow function):
Object.keys(myObj).forEach(key => {
console.log(key); // the name of the current key.
console.log(myObj[key]); // the value of the current key.
});
JS Bin
In ES7/2016 you can use Object.entries instead of Object.keys and loop through an object like this:
Object.entries(myObj).forEach(([key, val]) => {
console.log(key); // the name of the current key.
console.log(val); // the value of the current key.
});
The above would also work as a one-liner:
Object.entries(myObj).forEach(([key, val]) => console.log(key, val));
jsbin
In case you want to loop through nested objects as well, you can use a recursive function (ES6):
const loopNestedObj = obj => {
Object.keys(obj).forEach(key => {
if (obj[key] && typeof obj[key] === "object") loopNestedObj(obj[key]); // recurse.
else console.log(key, obj[key]); // or do something with key and val.
});
};
JS Bin
The same as function above, but with ES7 Object.entries() instead of Object.keys():
const loopNestedObj = obj => {
Object.entries(obj).forEach(([key, val]) => {
if (val && typeof val === "object") loopNestedObj(val); // recurse.
else console.log(key, val); // or do something with key and val.
});
};
Here we loop through nested objects change values and return a new object in one go using Object.entries() combined with Object.fromEntries() (ES10/2019):
const loopNestedObj = obj =>
Object.fromEntries(
Object.entries(obj).map(([key, val]) => {
if (val && typeof val === "object") [key, loopNestedObj(val)]; // recurse
else [key, updateMyVal(val)]; // or do something with key and val.
})
);
Another way of looping through objects is by using for ... in and for ... of. See vdegenne's nicely written answer.
The problem with this
for (var key in validation_messages) {
var obj = validation_messages[key];
for (var prop in obj) {
alert(prop + " = " + obj[prop]);
}
}
is that you’ll also loop through the primitive object's prototype.
With this one you will avoid it:
for (var key in validation_messages) {
if (validation_messages.hasOwnProperty(key)) {
var obj = validation_messages[key];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
alert(prop + " = " + obj[prop]);
}
}
}
}
Using Underscore.js’s _.each:
_.each(validation_messages, function(value, key){
_.each(value, function(value, key){
console.log(value);
});
});
This answer is an aggregate of the solutions that were provided in this
post with some performance feedbacks. I think there is two
use cases and the OP didn't mention if he needs to access the keys in order use them
during the loop process.
I. The keys need to be accessed
✔ The of and Object.keys approach
let k;
for (k of Object.keys(obj)) {
/* k : key
* obj[k] : value
*/
}
✔ The in approach
let k;
for (k in obj) {
/* k : key
* obj[k] : value
*/
}
Use this one with caution, as it could print prototype'd properties of obj
✔ The ES7 approach
for (const [key, value] of Object.entries(obj)) {
}
However, at the time of the edit I wouldn't recommend the ES7 method, because JavaScript initializes a lot of variables internally to build this procedure (see the feedbacks for proof). Unless you are not developing a huge application which deserves optimization, then it is OK, but if optimization is your priority, you should think about it.
II. We just need to access each value
✔ The of and Object.values approach
let v;
for (v of Object.values(obj)) {
}
More feedbacks about the tests:
Caching Object.keys or Object.values performance is negligible
For instance,
const keys = Object.keys(obj);
let i;
for (i of keys) {
//
}
// same as
for (i of Object.keys(obj)) {
//
}
For Object.values case, using a native for loop with cached variables in Firefox seems to be a little faster than using a for...of loop. However, the difference is not that important and Chrome is running for...of faster than native for loop, so I would recommend to use for...of when dealing with Object.values in any cases (4th and 6th tests).
In Firefox, the for...in loop is really slow, so when we want to cache the key during the iteration it is better to use Object.keys. Plus Chrome is running both structure at equal speed (first and last tests).
You can check the tests here: https://jsperf.com/es7-and-misc-loops
If you use recursion you can return object properties of any depth-
function lookdeep(object){
var collection= [], index= 0, next, item;
for(item in object){
if(object.hasOwnProperty(item)){
next= object[item];
if(typeof next== 'object' && next!= null){
collection[index++]= item +
':{ '+ lookdeep(next).join(', ')+'}';
}
else collection[index++]= [item+':'+String(next)];
}
}
return collection;
}
//example
var O={
a:1, b:2, c:{
c1:3, c2:4, c3:{
t:true, f:false
}
},
d:11
};
var lookdeepSample= 'O={'+ lookdeep(O).join(',\n')+'}';
/* returned value: (String)
O={
a:1,
b:2,
c:{
c1:3, c2:4, c3:{
t:true, f:false
}
},
d:11
}
*/
for(var k in validation_messages) {
var o = validation_messages[k];
do_something_with(o.your_name);
do_something_else_with(o.your_msg);
}
An optimized and improved version of AgileJon's answer:
var key, obj, prop, owns = Object.prototype.hasOwnProperty;
for (key in validation_messages ) {
if (owns.call(validation_messages, key)) {
obj = validation_messages[key];
for (prop in obj ) {
// Using obj.hasOwnProperty might cause you headache if there is
// obj.hasOwnProperty = function(){return false;}
// but 'owns' will always work
if (owns.call(obj, prop)) {
console.log(prop, "=", obj[prop]);
}
}
}
}
p is the value
for (var key in p) {
alert(key + ' => ' + p[key]);
}
OR
Object.keys(p).forEach(key => { console.log(key, p[key]) })
In ES7 you can do:
for (const [key, value] of Object.entries(obj)) {
//
}
for(var key in validation_messages){
for(var subkey in validation_messages[key]){
//code here
//subkey being value, key being 'yourname' / 'yourmsg'
}
}
A few ways to do that...
1) A two-layer for...in loop...
for (let key in validation_messages) {
const vmKeys = validation_messages[key];
for (let vmKey in vmKeys) {
console.log(vmKey + vmKeys[vmKey]);
}
}
2) Using Object.key
Object.keys(validation_messages).forEach(key => {
const vmKeys = validation_messages[key];
Object.keys(vmKeys).forEach(key => {
console.log(vmKeys + vmKeys[key]);
});
});
3) Recursive function
const recursiveObj = obj => {
for(let key in obj){
if(!obj.hasOwnProperty(key)) continue;
if(typeof obj[key] !== 'object'){
console.log(key + obj[key]);
} else {
recursiveObj(obj[key]);
}
}
}
And call it like:
recursiveObj(validation_messages);
Another option:
var testObj = {test: true, test1: false};
for(let x of Object.keys(testObj)){
console.log(x);
}
There are so many ways to traverse an object. Please have a look at below examples.
var obj = {'name':'John Doe','email':'johndoe#example.com'}
Approach 1
var keys = Object.keys(obj)
for(var i= 0; i < keys.length;i++){
console.log(keys[i]+ ': ' + obj[keys[i]])
}
Approach 2
for(var key in obj){
console.log(key+': '+ obj[key])
}
Approach 3
Object.keys(obj).forEach(function (key) {
console.log(key+ ': ' + obj[key])
})
Here comes the improved and recursive version of AgileJon's solution (demo):
function loopThrough(obj){
for(var key in obj){
// skip loop if the property is from prototype
if(!obj.hasOwnProperty(key)) continue;
if(typeof obj[key] !== 'object'){
//your code
console.log(key+" = "+obj[key]);
} else {
loopThrough(obj[key]);
}
}
}
loopThrough(validation_messages);
This solution works for all kinds of different depths.
ECMAScript 2017, just finalized a month ago, introduces Object.values(). So now you can do this:
let v;
for (v of Object.values(validation_messages))
console.log(v.your_name); // jimmy billy
var validation_messages = {
"key_1": {
"your_name": "jimmy",
"your_msg": "hello world"
},
"key_2": {
"your_name": "billy",
"your_msg": "foo equals bar"
}
}
for (var i in validation_messages) {
console.log("i = \"" + i + "\"");
console.log("validation_messages[\"" + i + "\"] = ");
console.log(validation_messages[i]);
console.log("\n");
for (var j in validation_messages[i]) {
console.log("j = \"" + j + "\"");
console.log("validation_messages[\"" + i + "\"][\"" + j + "\"] = \"" + validation_messages[i][j] + "\"");
console.log("\n");
}
console.log('\n');
}
Outputs:
i = "key_1"
validation_messages["key_1"] =
{
your_name:"jimmy",
your_msg:"hello world"
}
j = "your_name"
validation_messages["key_1"]["your_name"] = "jimmy"
j = "your_msg"
validation_messages["key_1"]["your_msg"] = "hello world"
i = "key_2"
validation_messages["key_2"] =
{
your_name:"billy",
your_msg:"foo equals bar"
}
j = "your_name"
validation_messages["key_2"]["your_name"] = "billy"
j = "your_msg"
validation_messages["key_2"]["your_msg"] = "foo equals bar"
I think it's worth pointing out that jQuery sorts this out nicely with $.each().
See: .each()
Example:
$('.foo').each(function() {
console.log($(this));
});
$(this) being the single item inside the object. Swap $('.foo') to a variable if you don't want to use jQuery's selector engine.
I couldn't get the previous answere to do quite what I was after.
After playing around with the other replies here, I made this. It's hacky, but it works!
For this object:
var myObj = {
pageURL : "BLAH",
emailBox : {model:"emailAddress", selector:"#emailAddress"},
passwordBox: {model:"password" , selector:"#password"}
};
... this code:
// Get every value in the object into a separate array item ...
function buildArray(p_MainObj, p_Name) {
var variableList = [];
var thisVar = "";
var thisYes = false;
for (var key in p_MainObj) {
thisVar = p_Name + "." + key;
thisYes = false;
if (p_MainObj.hasOwnProperty(key)) {
var obj = p_MainObj[key];
for (var prop in obj) {
var myregex = /^[0-9]*$/;
if (myregex.exec(prop) != prop) {
thisYes = true;
variableList.push({item:thisVar + "." + prop,value:obj[prop]});
}
}
if ( ! thisYes )
variableList.push({item:thisVar,value:obj});
}
}
return variableList;
}
// Get the object items into a simple array ...
var objectItems = buildArray(myObj, "myObj");
// Now use them / test them etc... as you need to!
for (var x=0; x < objectItems.length; ++x) {
console.log(objectItems[x].item + " = " + objectItems[x].value);
}
... produces this in the console:
myObj.pageURL = BLAH
myObj.emailBox.model = emailAddress
myObj.emailBox.selector = #emailAddress
myObj.passwordBox.model = password
myObj.passwordBox.selector = #password
var obj = {
name: "SanD",
age: "27"
}
Object.keys(obj).forEach((key) => console.log(key,obj[key]));
To loop through the JavaScript Object we can use forEach and to optimize the code we can use the arrow function.
using lodash _.forEach:
_.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
console.log(key, value);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
forEach2
(Found here):
var lunch = {
sandwich: 'ham',
age: 48,
};
lunch.forEach2(function (item, key) {
console.log(key);
console.log(item);
});
Code:
if (!Object.prototype.forEach2) {
Object.defineProperty(Object.prototype, 'forEach2', {
value: function (callback, thisArg) {
if (this == null) {
throw new TypeError('Not an object');
}
thisArg = thisArg || window;
for (var key in this) {
if (this.hasOwnProperty(key)) {
callback.call(thisArg, this[key], key, this);
}
}
}
});
}
Using ES8 Object.entries() should be a more compact way to achieve this.
Object.entries(validation_messages).map(([key,object]) => {
alert(`Looping through key : ${key}`);
Object.entries(object).map(([token, value]) => {
alert(`${token} : ${value}`);
});
});
The solution that works for me is the following:
_private.convertParams = function(params){
var params = [];
Object.keys(values).forEach(function(key) {
params.push({"id":key, "option":"Igual", "value":params[key].id})
});
return params;
}
Exotic one - deep traverse
JSON.stringify(validation_messages,(field,value)=>{
if(!field) return value;
// ... your code
return value;
})
In this solution we use replacer which allows to deep traverse the whole object and nested objects - on each level you will get all fields and values. If you need to get the full path to each field, look here.
var validation_messages = {
"key_1": {
"your_name": "jimmy",
"your_msg": "hello world"
},
"key_2": {
"your_name": "billy",
"your_msg": "foo equals bar",
"deep": {
"color": "red",
"size": "10px"
}
}
}
JSON.stringify(validation_messages,(field,value)=>{
if(!field) return value;
console.log(`key: ${field.padEnd(11)} - value: ${value}`);
return value;
})
In 2020 you want immutable and universal functions
This walks through your multidimensional object composed of sub-objects, arrays and string and apply a custom function:
export const iterate = (object, func) => {
const entries = Object.entries(object).map(([key, value]) =>
Array.isArray(value)
? [key, value.map(e => iterate(e, func))]
: typeof value === 'object'
? [key, iterate(value, func)]
: [key, func(value)]
);
return Object.fromEntries(entries);
};
Usage:
const r = iterate(data, e=>'converted_'+e);
console.log(r);
In my case (on the basis of the preceding) it is possible for any number of levels.
var myObj = {
rrr: undefined,
pageURL : "BLAH",
emailBox : {model:"emailAddress", selector:"#emailAddress"},
passwordBox: {model:"password" , selector:"#password"},
proba: {odin:{dva:"rr",trr:"tyuuu"}, od:{ff:5,ppa:{ooo:{lll:'lll'}},tyt:'12345'}}
};
function lookdeep(obj,p_Name,gg){
var A=[], tem, wrem=[], dd=gg?wrem:A;
for(var p in obj){
var y1=gg?'':p_Name, y1=y1 + '.' + p;
if(obj.hasOwnProperty(p)){
var tem=obj[p];
if(tem && typeof tem=='object'){
a1=arguments.callee(tem,p_Name,true);
if(a1 && typeof a1=='object'){for(i in a1){dd.push(y1 + a1[i])};}
}
else{
dd.push(y1 + ':' + String(tem));
}
}
};
return dd
};
var s=lookdeep(myObj,'myObj',false);
for (var x=0; x < s.length; ++x) {
console.log(s[x]+'\n');}
Result:
["myObj.rrr:undefined",
"myObj.pageURL:BLAH",
"myObj.emailBox.model:emailAddress",
"myObj.emailBox.selector:#emailAddress",
"myObj.passwordBox.model:password",
"myObj.passwordBox.selector:#password",
"myObj.proba.odin.dva:rr",
"myObj.proba.odin.trr:tyuuu",
"myObj.proba.od.ff:5",
"myObj.proba.od.ppa.ooo.lll:lll",
"myObj.proba.od.tyt:12345"]
I am trying to compare two elements inside an object in Javascript. The letter that has the highest value, should be returned along with the number it carries.
This is the object and we should return a and 39.
obj({a:39,b:21,c:12,d:4}) // should return a : 39
Here is my code so far.
let obj = object => {
for(let i in object) { // i represents the "key" of the objects, a,b,c,d
if(object[i] > object[i + 1]) { // object[i] represents the "value" held 39,21,12,4
console.log(i + ":" + object[i]);
} else {
console.log(i + ":" + object[i]);
}
}
}
obj({a:39,b:21,c:12,d:4})
I thought object[i + 1] in the if statement would compare the next indexed element to the current one but it doesn't, how would you accomplish this?
EDIT if there are two elements with the same value then return both of the elements
This is probably the easiest way to accomplish this for people new to coding like me. This code returns the highest number held in the object.
let getMax = (obj) => {
let highestValue = 0;
for(var i in obj) {
if(highestValue < obj[i]) {
highestValue = obj[i];
} else if (highestValue == obj[i]) {
highestValue = highestValue + " " + obj[i];
}
}
alert(highestValue);
}
getMax({John:300000,Kary:360000,David:2700000,Michelle:2700000})
On each iteration check if the current or previous key value is the largest, and store. Store the largest in the largest variable. In the end return the largest variable, and it's value (object[largest]):
let obj = object => {
let largest;
for(const i in object) { // i represents the "key" of the objects, a,b,c,d
if(!largest || object[i] > object[largest]) {
largest = i;
}
}
return { [largest]: object[largest] };
}
console.log(obj({a:39,b:21,c:12,d:4}));
Suggested solution:
Use Object.entries() to get key|value pairs. Iterate with Array.reduce(), choose the pair with the highest value (index 1). Destructure the reduce's result (an array with [key, value]) into key and value consts, and use them to build the return object.
const obj = (object) => {
const [key, value] = Object.entries(object)
.reduce((r, e) => e[1] > r[1] ? e : r);
return { [key]: value };
};
console.log(obj({a:39,b:21,c:12,d:4}));
for(let i in object)
returns object keys
so i+1 = a : a1, b:b1, c:c1
This will be the correct code:
let obj = object => {
let returnValue;
for(let i in object) { // i represents the "key" of the objects, a,b,c,d
if(typeof(returnValue)==="undefined"){
returnValue = object[i];
} else if (object[i] > returnValue) {
returnValue=object[i];
}
}
return returnValue;
}
obj({a:39,b:21,c:12,d:4})
You could collect the keys and the render the object with the max values.
function getMax(object) {
return Object.assign(
...Object
.keys(object).reduce((r, k) => {
if (!r || object[r[0]] < object[k]) {
return [k];
}
if (object[r[0]] === object[k]) {
r.push(k);
}
return r;
}, undefined)
.map(k => ({ [k]: object[k] }))
);
}
console.log(getMax({ a: 39, b: 21, c: 12, d: 4, foo: 39 }));
When you do let i in object, you're iterating through every key in the object. So in this case, i would be a, b, c, and d, respectively after each iteration.
That's why object[i+1] doesn't work. Because on the first iteration when i is "a", the interpretter reads it as object['a' + 1] which results to object['a1'].
There's a few ways you can approach this.
One method would be to use the Object class's keys function, which returns an array of keys and then you can call map on that result to loop through each key and you'll also have a index to each one. I.e:
let obj = object => {
var keys = Object.keys(object);
keys.map(function(key, index) {
console.log("Current value: " + object[key]);
console.log("Current index: " + index);
console.log("Current key: " + key);
console.log("Next value: " + object[keys[index + 1]]); // you should probably check if you're at the last index before you do this
console.log("-------------");
});
};
obj( {a:39,b:21,c:12,d:4} );
Another route you can go is using a for loop and creating an iterator variable, like so:
let obj = object => {
var keys = Object.keys(object);
for(var i = 0; i < keys.length; i++) {
console.log('Current Value: ' + object[keys[i]]);
console.log('Next Value: ' + object[keys[i + 1]]); // also probably do a check here to see if you're already at the last index
}
};
obj( {a:39,b:21,c:12,d:4} );
In the case above, i would always be a number. Then we loop through the number of keys there are in the object, and then use i to get the key we want. Then put that key into the object and we'll get the values you want to compare by.
To get all index/value (if exists multiple max values),
use Object.entries to get key/value pairs,
then when using Array.reduce to loop the pairs, set the initial value of reduce = {max:0, data:[]}
Comment: max saves the max value, data saves the pairs with max value.
And assuming the values is > 0, so set the initial value of max = 0 (you can change the initial value as you need).
In the loop, compare the value,
if cur > max, push it into pre.data and update pre.max.
if cur===max, push it to pre.data,
if cur<max, do nothing.
const obj = {a:39,b:21,c:12,d:4,e:39}
result = Object.entries(obj).reduce(function(pre, cur){
if(pre.max < cur[1]){
pre.data = []
pre.max = cur[1]
pre.data.push({[cur[0]]:cur[1]})
}
else if(pre.max === cur[1]){
pre.data.push({[cur[0]]:cur[1]})
}
return pre
},{max:0, data:[]})
console.log(result.data)
This question already has answers here:
Accessing nested JavaScript objects and arrays by string path
(44 answers)
Closed 5 years ago.
How do i get object property using array of string (name of properties)? (the last element in array is the inner property of object)
See the code below:
Handy way:
let myObject = {
"property": {
"subproperty": {
"targetproperty": "Hi, We done it!"
}
}
};
let myString = "property:subproperty:targetproperty";
let parts = myString.split( ":" );
console.log( myObject[ parts[ 0 ] ][ parts[ 1 ] ][ parts[ 2 ] ] ); // Output: "Hi, We done it!"
Eval way:
let myObject = {
"property": {
"subproperty": {
"targetproperty": "Hi, We done it!"
}
}
};
let myString = "property:subproperty:targetproperty";
let parts = myString.split( ":" );
let code = "myObject";
for ( let i = 0; i < parts.length; i++ ) {
code += "['" + parts[ i ] + "']";
}
code += ";";
console.log( code );
console.log( eval( code ) ); // Output: "Hi, We done it!"
Eval is evil. so i need a cleaner way to do it.
How do i do it without eval and handy job?
You can use .reduce():
let myObject = {
"property": {
"subproperty": {
"targetproperty": "Hi, We done it!"
}
}
};
let myString = "property:subproperty:targetproperty";
let value = myString.split(":").reduce(function(obj, prop) {
return obj && obj[prop];
}, myObject);
console.log(value);
For loop:
function getByValue(arr, value) {
for (var i=0, iLen=arr.length; i<iLen; i++) {
if (arr[i].b == value) return arr[i];
}
}
.filter
function getByValue2(arr, value) {
var result = arr.filter(function(o){return o.b == value;} );
return result? result[0] : null; // or undefined
}
.forEach
function getByValue3(arr, value) {
var result = [];
arr.forEach(function(o){if (o.b == value) result.push(o);} );
return result? result[0] : null; // or undefined
}
If, on the other hand you really did mean for..in and want to find an object with any property with a value of 6, then you must use for..in unless you pass the names to check. e.g.
function getByValue4(arr, value) {
var o;
for (var i=0, iLen=arr.length; i<iLen; i++) {
o = arr[i];
for (var p in o) {
if (o.hasOwnProperty(p) && o[p] == value) {
return o;
}
}
}
}
You can use a reduce solution:
var obj = {prop1: {prop2: {prop3: 'xpto'}}};
var props = ['prop1','prop2','prop3'];
var result = props.reduce((acc,val)=>acc[val],obj);
console.log(result);
The recursive way ;)
Create a function that takes the current property, allparts and index.
Start with zero, return call to next index, try read and return with next call and increment index untill there are no more props to read/extract then return the value you got as the current property.
Let me know if you need working code
Here is a recursive approach, it will return undefined if the property isn't found:
const getPath = (o, keyPath, delimiter = '.') => {
if (Array.isArray(keyPath)) {
keyPath = keyPath.join(delimiter)
}
// o might not be an object when called recursively
if(Object(o) === o) {
let keys = keyPath.split(delimiter);
let key = keys.shift();
if(o.hasOwnProperty(key)) {
if(keys.length) {
// there are more keys to check, call with attribute and remaining keys
return getPath(o[key], keys.join(delimiter), delimiter);
} else {
// no more keys to check and object does have property
return o[key];
}
}
// didn't early return from having the key above, object does not have property
return undefined;
} else if(keyPath.length === 0) {
// o is not an object, but there is no remaining keyPath, so we will assume we've unwound the stack
return o;
}
// not an object and keyLength is non-zero, object does not contain property
return undefined;
};
let myObject = {
"property": {
"subproperty": {
"targetproperty": "Hi, We done it!"
}
}
};
console.log(getPath(myObject, "property:subproperty:targetproperty", ":"));
You can loop through your parts array, accessing the value of each key in each iteration.
function valueFromPath(obj, path) {
for (var i = 0; i < path.length; ++i) {
obj = obj[path[i]];
}
return obj;
};
valueFromPath(myObject, parts);
You probably want to clone the object first, in case you are going to use it for something else.
Alternatively, you can use traverse. Specifically traverse#getpath.
traverse(myObject).get(parts);
You may do as follows;
function getNestedValue(o,...a){
var val = o;
for (var prop of a) val = typeof val === "object" &&
val !== null &&
val[prop] !== void 0 ? val[prop]
: undefined;
return val;
}
let myObject = {
"property": {
"subproperty": {
"targetproperty": "Hi, We done it!"
}
}
};
let myString = "property:subproperty:targetproperty";
console.log(getNestedValue(myObject, ...myString.split(":")));
I have the string "presentation.launchBehavior.newWindow", which needs to be de-serialized into object:
var obj = {presentation: {launchBehavior: 'newWindow'}}
The last string after dot should be value, while other strings should be converted to strings. I've written the function to do that:
function des(obj, property, index, ar) {
var isLastCall = ar.length - 1 === index;
if (isLastCall) {
return;
}
var isNextLastCall = ar.length - 2 === index;
if (isNextLastCall) {
obj[property] = ar[ar.length - 1];
return obj;
} else {
obj[property] = {};
return obj[property];
}
}
var obj = {};
"presentation.launchBehavior.newWindow".split(".").reduce(des, obj);
obj.presentation.launchBehavior // newWindow
It's working, but I'd like to improve it to return the resulting object instead of having to create a variable outside the function. How can I do that? Maybe internals of the function can be improved as well.
You can use reduceRight:
const des = str => str.split(".").reduceRight( (prev, key) => ({[key]: prev}) );
console.log(des("presentation.launchBehavior.newWindow"));
Here is how I would do it.
function des(str) {
var props = (typeof str === "string") ? str.split('.') : str;
return props.length > 1 ? {[props[0]]: des(props.slice(1))}
: props[0];
}
console.log(des("presentation.launchBehavior.newWindow"));
It uses array computed properties (var o = {[key]: val}). It may not work on old browsers. For old browsers, you will have to first create the object assign then property (var o = {}; o[key] = val;).