Reputation: 670
I need to transform a string
1. "user.member.staffAddress"
2. "user.something"
to object:
1. { user: { member: { staffAddress: {} } } }
2. { user: { something: {} } }
Does anyone has an elegant way how to do it? It should always be an object in object. The last property should be an empty one.
Upvotes: 0
Views: 90
Reputation: 12079
A string conversion approach:
var str = 'user.member.staffAddress'
var str_arr = str.split('.')
var obj = JSON.parse(
'{ "' + str_arr.join('": { "') + '": {}'
+ Array(str_arr.length+1).join(' }')
)
console.log(obj)
// { "user": { "member": { "staffAddress": {} } } }
": { "
.{ "
and ": {}
followed by length
number of closing curly braces.Upvotes: 0
Reputation: 318182
Iterate and add the properties etc ...
function stringToObject(str) {
var obj = {}, arr = str.split('.');
(function it(o) {
var key = arr.shift();
o[key] = {};
if (arr.length) it(o[key]);
}(obj));
return obj;
}
var obj = stringToObject("user.member.staffAddress");
document.body.innerHTML = JSON.stringify(obj, null, 4);
Upvotes: 0
Reputation: 13487
I wrote a utility that I think you will find helpful for this: https://github.com/forms-js/forms-js/blob/master/source/utils/flatten.ts
Here's the relevant bits. It's written in TypeScript but if you remove the :type annotations, it's valid JavaScript.
/**
* Writes a value to the location specified by a flattened key and creates nested structure along the way as needed.
*
* <p>For example, writing "baz" to the key 'foo.bar' would result in an object <code>{foo: {bar: "baz"}}</code>.
* Writing 3 to the key 'foo[0].bar' would result in an object <code>{foo: [{bar: 3}]}</code>.
*/
function write(value:any, flattenedKey:string, object:any):void {
var currentKey:any;
var keyIndexStart = 0;
for (var charIndex = 0, length = flattenedKey.length; charIndex < length; charIndex++) {
var character = flattenedKey.charAt(charIndex);
switch(character) {
case '[':
currentKey = flattenedKey.substring(keyIndexStart, charIndex);
createPropertyIfMissing_(currentKey, object, Array);
break;
case ']':
currentKey = flattenedKey.substring(keyIndexStart, charIndex);
currentKey = parseInt(currentKey); // Convert index from string to int
// Special case where we're targeting this object in the array
if (charIndex === length - 1) {
object[currentKey] = value;
} else {
// If this is the first time we're accessing this Array key we may need to initialize it.
if (!object[currentKey] && charIndex < length - 1) {
switch(flattenedKey.charAt(charIndex + 1)) {
case '[':
object[currentKey] = [];
break;
case '.':
object[currentKey] = {};
break;
}
}
object = object[currentKey];
}
break;
case '.':
currentKey = flattenedKey.substring(keyIndexStart, charIndex);
// Don't do anything with empty keys that follow Array indices (e.g. anArray[0].aProp)
if (currentKey) {
createPropertyIfMissing_(currentKey, object, Object);
}
break;
default:
continue; // Continue to iterate...
break;
}
keyIndexStart = charIndex + 1;
if (currentKey) {
object = object[currentKey];
}
}
if (keyIndexStart < flattenedKey.length) {
currentKey = flattenedKey.substring(keyIndexStart, flattenedKey.length);
object[currentKey] = value;
}
}
/**
* Helper method for initializing a missing property.
*
* @throws Error if unrecognized property specified
* @throws Error if property already exists of an incorrect type
*/
function createPropertyIfMissing_(key:string, object:any, propertyType:any):void {
switch(propertyType) {
case Array:
if (!object.hasOwnProperty(key)) {
object[key] = [];
} else if (!(object[key] instanceof Array)) {
throw Error('Property already exists but is not an Array');
}
break;
case Object:
if (!object.hasOwnProperty(key)) {
object[key] = {};
} else if (typeof object[key] !== 'object') {
throw Error('Property already exists but is not an Object');
}
break;
default:
throw Error('Unsupported property type');
break;
}
}
To be fair, you could also consider a project written specifically for doing this - rather than mine, in which it's only a small portion - which is to say, https://github.com/hughsk/flat
Upvotes: 1