Deeply changing values in a JavaScript object
I have an object that contains an unknown number of other objects. Each (sub) object can contain boolean values as strings and I want to change them to real boolean values. Here's an example of an object:
var myObj = {
my1stLevelKey1: "true",
my1stLevelKey2: "a normal string",
my1stLevelKey3: {
my2ndLevelKey1: {
my3rdLevelKey1: {
my4thLevelKey1: "true",
my4thLevelKey2: "false"
}
},
my2ndLevelKey2: {
my3rdLevelKey2: "FALSE"
}
}
}
In the end I want:
var myObj = {
my1stLevelKey1: true,
my1stLevelKey2: "a normal string",
my1stLevelKey3: {
my2ndLevelKey1: {
my3rdLevelKey1: {
my4thLevelKey1: true,
my4thLevelKey2: false
}
},
my2ndLevelKey2: {
my3rdLevelKey2: false
}
}
}
It is important that the number of sub-objects / levels of the number is unknown. How can I do this efficiently using classic JavaScript or Mootools?
source to share
Recursion is your friend
(function (obj) { // IIFE so you don't pollute your namespace
// define things you can share to save memory
var map = Object.create(null);
map['true'] = true;
map['false'] = false;
// the recursive iterator
function walker(obj) {
var k,
has = Object.prototype.hasOwnProperty.bind(obj);
for (k in obj) if (has(k)) {
switch (typeof obj[k]) {
case 'object':
walker(obj[k]); break;
case 'string':
if (obj[k].toLowerCase() in map) obj[k] = map[obj[k].toLowerCase()]
}
}
}
// set it running
walker(obj);
}(myObj));
obj[k].toLowerCase()
is to make it case insensitive
source to share
Go through each level of the object and replace the boolean string values with the corresponding booleans. If you find an object, repeat the procedure and replace it again.
You can use Object.keys
to grab all members of each object without worrying about getting inherited properties that you don't need.
var myObj = {
my1stLevelKey1: "true",
my1stLevelKey2: "a normal string",
my1stLevelKey3: {
my2ndLevelKey1: {
my3rdLevelKey1: {
my4thLevelKey1: "true",
my4thLevelKey2: "false"
}
},
my2ndLevelKey2: {
my3rdLevelKey2: "FALSE"
}
}
};
function booleanizeObject(obj) {
var keys = Object.keys(obj);
keys.forEach(function(key) {
var value = obj[key];
if (typeof value === 'string') {
var lvalue = value.toLowerCase();
if (lvalue === 'true') {
obj[key] = true;
} else if (lvalue === 'false') {
obj[key] = false;
}
} else if (typeof value === 'object') {
booleanizeObject(obj[key]);
}
});
}
booleanizeObject(myObj);
document.getElementById('results').textContent = JSON.stringify(myObj);
<pre id="results"></pre>
source to share
JavaScript data structures can be elegantly sanitized with recursive functional approaches reduce
.
var myObj = {
my1stLevelKey1: "true",
my1stLevelKey2: "a normal string",
my1stLevelKey3: {
my2ndLevelKey1: {
my3rdLevelKey1: {
my4thLevelKey1: "true",
my4thLevelKey2: "false"
}
},
my2ndLevelKey2: {
my3rdLevelKey2: "FALSE"
}
}
};
myObj = Object.keys(myObj).reduce(function sanitizeBooleanStructureRecursively (collector, key) {
var
source = collector.source,
target = collector.target,
value = source[key],
str
;
if (value && (typeof value == "object")) {
value = Object.keys(value).reduce(sanitizeBooleanStructureRecursively, {
source: value,
target: {}
}).target;
} else if (typeof value == "string") {
str = value.toLowerCase();
value = ((str == "true") && true) || ((str == "false") ? false : value)
}
target[key] = value;
return collector;
}, {
source: myObj,
target: {}
}).target;
console.log(myObj);
source to share
An example of simple javascript recursion:
function mapDeep( obj ) {
for ( var prop in obj ) {
if ( obj[prop] === Object(obj[prop]) ) mapDeep( obj[prop] );
else if ( obj[prop].toLowerCase() === 'false' ) obj[prop] = false;
else if ( obj[prop].toLowerCase() === 'true' ) obj[prop] = true;
}
};
And a MooTools example, expanding the type Object
with a custom function mapDeep()
:
Object.extend( 'mapDeep', function( obj, custom ) {
return Object.map( obj, function( value, key ) {
if ( value === Object( value ) )
return Object.mapDeep( value, custom );
else
return custom( value, key );
});
});
myObj = Object.mapDeep( myObj, function( value, key ) {
var bool = { 'true': true, 'false': false };
return value.toLowerCase() in bool ? bool[ value.toLowerCase() ] : value;
})
source to share