Creating deep-nested objects in Javascript - javascript

When loading JSON from a server, I need to create objects. The objects do not always exist beforehand.Therefore I have to check that each level exists before adding a new level. Is there a better way to do this, then the following way:
var talkerId = commentDB.val().to;
var commentId = commentDB.val().id
if (!store.talkers.hasOwnProperty(talkerId)) {
store.talkers[talkerId] = {}
}
if (!store.talkers[talkerId].hasOwnProperty(comments)) {
store.talkers[talkerId] = { comments: {} };
}
if (!store.talkers[talkerId].comments.hasOwnProperty(commentId)) {
store.talkers[talkerId].comments[commentId] = {}
}
store.talkers[talkerId].comments[commentId].author = commentDB.val().author;

You cour reduce the keys by using the object an check if the key exist. if not create a new property with an empty object.
var dbValue = commentDB.val();
[dbValue.to, 'comments', dbValue.id].reduce(
(o, k) => o[k] = o[k] || {},
store.talkers
).author = commentDB.val().author;

Why don't you create a function
function checkAndCreateProperty(arr, baseVar) {
for(var i = 0; i < arr.length; i++) {
if (!baseVar.hasOwnProperty(arr[i])) {
baseVar[arr[i]] = {};
}
baseVar = baseVar[arr[i]];
}
}
checkAndCreateProperty(["talkerId", "comments", commentDB.val().id, commentDB.val().author], store.talkers)

After the suggestion of #31piy I went for the most simple solution:
using lodash _.set
var talkerId = commentDB.val().to;
var commentId = commentDB.val().id;
_.set(store.talkers, '[' + talkerId + '].comments[' + commentId + '].author', commentDB.val().author)

Related

JS: dynamically populate/combine object with nested objects/properties > do we have a simpler way?

The problem: I've a JS global var that I've to check if it as an object with "menus configurations" and populate with the missing configurations.
So this work is based on this AS2:
var result_array = re.result.items;
_global.menu_array = new Array();
var menu_type_num:Number = -1;
var menu_group_num:Number = -1;
var menu_id_num:Number = -1;
for (var i=0; i<result_array.length; i++){
if (menu_type_num != result_array[i].menu_type){
menu_type_num = result_array[i].menu_type;
menu_group_num = -1;
var type_array:Array = new Array();
_global.menu_array[menu_type_num] = type_array;
}
if (menu_group_num != result_array[i].menu_group){
menu_group_num = result_array[i].menu_group;
var group_array:Array = new Array();
_global.menu_array[menu_type_num][menu_group_num] = group_array;
}
var menu_obj:Object = new Object();
menu_id_num = menu_obj.menu_id = Number(result_array[i].menu_id);
menu_obj.menu_text = result_array[i].menu_text;
_global.menu_array[menu_type_num][menu_group_num][menu_id_num] = menu_obj;
}
I've reached to this solution in JS:
result.then(function(res)
{
if(!_MEV2_GLOBAL.hasOwnProperty("menu_array"))
{
_MEV2_GLOBAL.menu_array = {};
}
res.forEach(r =>
{
if(!_MEV2_GLOBAL.menu_array.hasOwnProperty(r.menu_type))
{
_MEV2_GLOBAL.menu_array[r.menu_type] = {};
}
if(!_MEV2_GLOBAL.menu_array[r.menu_type].hasOwnProperty(r.menu_group))
{
_MEV2_GLOBAL.menu_array[r.menu_type][r.menu_group] = {};
}
if(!_MEV2_GLOBAL.menu_array[r.menu_type][r.menu_group].hasOwnProperty(r.menu_id))
{
_MEV2_GLOBAL.menu_array[r.menu_type][r.menu_group][r.menu_id] = {
menu_id: r.menu_id,
menu_text: r.menu_text
};
}
}
}
This works but doing an if for every sublevel... Do I have another way, something with reduce or whatever, more effective and concise?
I know a little about JS but I'm afraid my solution isn't the best aproach. Or am I falling to the "root of all evil" :P
EDIT:
The input comes from an IndexedDB "table", so the result is made of objects ("lines") similar to the one on the picture:
for example, "t_menus_gre" select is the input
This resultSet must be inserted on a GlobalVar, that should follow this structure:
this object is my GlobalVar and should include a subObject['menu_array'] with menus configs by type/group/id
Okay... I honestly don't think I understood the logic behind that menu configuration, but based solely on your code I could make the following improvements:
result.then(res => {
_MEV2_GLOBAL.menu_array = _MEV2_GLOBAL.menu_array || {};
const array = _MEV2_GLOBAL.menu_array;
res.forEach(r => {
array[r.menu_type] = array[r.menu_type] || {};
array[r.menu_type][r.menu_group] = array[r.menu_type][r.menu_group] || {};
array[r.menu_type][r.menu_group][r.menu_id] = array[r.menu_type][r.menu_group][r.menu_id] || { menu_id: r.menu_id, menu_text: r.menu_text };
});
});
If you just want to verify if a value is defined, javascript always evaluates null, undefined, false, "" and 0 as false.
That way, when you execute array[r.menu_type] = array[r.menu_type] || {};, if array[r.menu_type] is undefined, it receives {}.
Hope that helps you in some way.

Javascript object transform

I want to transform this object so that I can call it that way
cars.ox, bikes.ox
var baseValue = [
{
'2014-12-01': {
'cars;ox;2014-12-01':100,
'cars;ot;2014-12-01':150,
'bikes;ox;2014-12-01':50,
'bikes;ot;2014-12-01':80
},
'2014-12-02': {
'cars;ox;2014-12-02':100,
'cars;ot;2014-12-02':150,
'bikes;ox;2014-12-02':50,
'bikes;ot;2014-12-02':80
}
}
]
I try do this in many ways, but at the end i completely lost all hope.
var category = []
var obj = baseValue[0]
Object.keys(obj).forEach(function(key) {
var dane = obj[key]
Object.keys(dane).forEach(function(key) {
splitted = key.split(';')
var category = splitted[0]
var serviceName = splitted[1];
})
})
I would be grateful if anyone help me with this
I think you were close, you just need to create objects if they do not exist for the keys you want. Perhaps something like this.
var obj = baseValue[0]
var result = {};
Object.keys(obj).forEach(function(key) {
var dane = obj[key]
Object.keys(dane).forEach(function(key) {
splitted = key.split(';')
var category = splitted[0]
var serviceName = splitted[1];
if(!result[category]) {
result[category] = {};
}
if(!result[category][serviceName]) {
result[category][serviceName] = [];
}
result[category][serviceName].push(dane[key]);
})
});
http://jsfiddle.net/6c5c3qwy/1/
(The result is logged to the console.)

Build object in JavaScript from PHP form input name

There are a couple of similar questions but none covers the case when a string looks like some-name[][some-key]. I have tried JSON.parse('some-name[][some-key]'); but it doesn't parse it.
Is there a way to convert such string to a JavaScript object that will look like { 'some-name': { 0: { 'some-key': '' } } }?
This is a name of a form field. It's normally parsed by PHP but I'd like to parse it with JavaScript the same way. I basically have <input name="some-name[][some-key]"> and I'd like to convert that to var something = { 'some-name': { 0: { 'some-key': VALUE-OF-THIS-FIELD } } }.
Try this:
JSON.parse('{ "some-name": [ { "some-key": "" } ] }');
I don't know exactly how you're doing this, but assuming they are all that format (name[][key]) and you need to do them one by one - this works for me:
var fieldObj = {};
function parseFieldName(nameStr)
{
var parts = nameStr.match(/[^[\]]+/g);
var name = parts[0];
var key = typeof parts[parts.length-1] != 'undefined' ? parts[parts.length-1] : false;
if(key===false) return false;
else
{
if(!fieldObj.hasOwnProperty(name)) fieldObj[name] = [];
var o = {};
o[key] = 'val';
fieldObj[name].push(o);
}
}
parseFieldName('some-name[][some-key]');
parseFieldName('some-name[][some-key2]');
parseFieldName('some-name2[][some-key]');
console.log(fieldObj); //Firebug shows: Object { some-name=[2], some-name2=[1]} -- stringified: {"some-name":[{"some-key":"val"},{"some-key2":"val"}],"some-name2":[{"some-key":"val"}]}
o[key] = 'val'; could of course be changed to o[key] = $("[name="+nameStr+"]").val() or however you want to deal with it.
Try this:
var input = …,
something = {};
var names = input.name.match(/^[^[\]]*|[^[\]]*(?=\])/g);
for (var o=something, i=0; i<names.length-1; i++) {
if (names[i])
o = o[names[i]] || (o[names[i]] = names[i+1] ? {} : []);
else
o.push(o = names[i+1] ? {} : []);
}
if (names[i])
o[names[i]] = input.value;
else
o.push(input.value);
Edit: according to your updated example, you can make something like this (view below). This will work - but only with the current example.
var convertor = function(element) {
var elementName = element.getAttribute('name');
var inpIndex = elementName.substring(0, elementName.indexOf('[')),
keyIndex = elementName.substring(elementName.lastIndexOf('[') + 1, elementName.lastIndexOf(']'));
var strToObj = "var x = {'" + inpIndex + "': [{'" + keyIndex + "': '" + element.value + "'}]}";
eval(strToObj);
return x;
};
var myObject = convertor(document.getElementById('yourInputID'));
Example here: http://paulrad.com/stackoverflow/string-to-array-object.html
(result is visible in the console.log)
old response
Use eval.. but your string must have a valid javascript syntax
So:
var str = "arr[][123] = 'toto'";
eval(str);
console.log(arr);
Will return a syntax error
Valid syntax will be:
var str = "var arr = []; arr[123] = 'toto'";
var x = eval(str);
console.log(arr);

javascript array - accessing json type of objects

The code:
function getDummyDetails(){
var userDetailsMap = [];
userDetailsMap.push({key:'APPCODE', value:'41'});
userDetailsMap.push({key:'WORKERNUMBER', value:'1234567'});
userDetailsMap.push({key:'ACCOUNTID', value:'DEVELOP'});
userDetailsMap.push({key:'NAMEFIRST', value:'John'});
userDetailsMap.push({key:'NAMELAST', value:'Developer'});
return userDetailsMap;
}
function someOtherFunction () {
var userDetails = getDummyDetails();
document.getElementById("userName").innerHTML = "User Name: " + userDetails[3].value + ", " + userDetails[4].value;
}
Here, it works fine but I can not use the array index here like userDetails[3].value. I was trying to do something like this
userDetails["APPCODE"].value; // just a pseudo code
How can I index this array with that string values but not an integer?
You should create an object instead of an array. That way you'll be able to access it via its key:
function getDummyDetails() {
return {
'APPCODE':'41',
'WORKERNUMBER':'1234567',
'ACCOUNTID':'DEVELOP',
'NAMEFIRST':'John',
'NAMELAST':'Developer'
};
}
function someOtherFunction () {
var userDetails = getDummyDetails();
userDetails["APPCODE"] // 41 - use it however you want...
}
You need to create an object, not an array:
var userDetailsMap = {
APPCODE:41
}
var value = userDetailsMap["APPCODE"];//value now = 41
If you don't want to change your structure, you can iterate over your array:
for (var i = 0, len = userDetailsMap.length; i < len; i++) {
if (userDetailsMap[i].key == 'APPCODE') {
var val = userDetailsMap[i].value;
// do something with the value here
}
}

Serialize JavaScript object into JSON string

I have this JavaScript prototype:
Utils.MyClass1 = function(id, member) {
this.id = id;
this.member = member;
}
and I create a new object:
var myobject = new MyClass1("5678999", "text");
If I do:
console.log(JSON.stringify(myobject));
the result is:
{"id":"5678999", "member":"text"}
but I need for the type of the objects to be included in the JSON string, like this:
"MyClass1": { "id":"5678999", "member":"text"}
Is there a fast way to do this using a framework or something? Or do I need to implement a toJson() method in the class and do it manually?
var myobject = new MyClass1("5678999", "text");
var dto = { MyClass1: myobject };
console.log(JSON.stringify(dto));
EDIT:
JSON.stringify will stringify all 'properties' of your class. If you want to persist only some of them, you can specify them individually like this:
var dto = { MyClass1: {
property1: myobject.property1,
property2: myobject.property2
}};
It's just JSON? You can stringify() JSON:
var obj = {
cons: [[String, 'some', 'somemore']],
func: function(param, param2){
param2.some = 'bla';
}
};
var text = JSON.stringify(obj);
And parse back to JSON again with parse():
var myVar = JSON.parse(text);
If you have functions in the object, use this to serialize:
function objToString(obj, ndeep) {
switch(typeof obj){
case "string": return '"'+obj+'"';
case "function": return obj.name || obj.toString();
case "object":
var indent = Array(ndeep||1).join('\t'), isArray = Array.isArray(obj);
return ('{['[+isArray] + Object.keys(obj).map(function(key){
return '\n\t' + indent +(isArray?'': key + ': ' )+ objToString(obj[key], (ndeep||1)+1);
}).join(',') + '\n' + indent + '}]'[+isArray]).replace(/[\s\t\n]+(?=(?:[^\'"]*[\'"][^\'"]*[\'"])*[^\'"]*$)/g,'');
default: return obj.toString();
}
}
Examples:
Serialize:
var text = objToString(obj); //To Serialize Object
Result:
"{cons:[[String,"some","somemore"]],func:function(param,param2){param2.some='bla';}}"
Deserialize:
Var myObj = eval('('+text+')');//To UnSerialize
Result:
Object {cons: Array[1], func: function, spoof: function}
Well, the type of an element is not standardly serialized, so you should add it manually. For example
var myobject = new MyClass1("5678999", "text");
var toJSONobject = { objectType: myobject.constructor, objectProperties: myobject };
console.log(JSON.stringify(toJSONobject));
Good luck!
edit: changed typeof to the correct .constructor. See https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor for more information on the constructor property for Objects.
This might be useful.
http://nanodeath.github.com/HydrateJS/
https://github.com/nanodeath/HydrateJS
Use hydrate.stringify to serialize the object and hydrate.parse to deserialize.
You can use a named function on the constructor.
MyClass1 = function foo(id, member) {
this.id = id;
this.member = member;
}
var myobject = new MyClass1("5678999", "text");
console.log( myobject.constructor );
//function foo(id, member) {
// this.id = id;
// this.member = member;
//}
You could use a regex to parse out 'foo' from myobject.constructor and use that to get the name.
Below is another way by which we can JSON data with JSON.stringify() function
var Utils = {};
Utils.MyClass1 = function (id, member) {
this.id = id;
this.member = member;
}
var myobject = { MyClass1: new Utils.MyClass1("5678999", "text") };
alert(JSON.stringify(myobject));
function ArrayToObject( arr ) {
var obj = {};
for (var i = 0; i < arr.length; ++i){
var name = arr[i].name;
var value = arr[i].value;
obj[name] = arr[i].value;
}
return obj;
}
var form_data = $('#my_form').serializeArray();
form_data = ArrayToObject( form_data );
form_data.action = event.target.id;
form_data.target = event.target.dataset.event;
console.log( form_data );
$.post("/api/v1/control/", form_data, function( response ){
console.log(response);
}).done(function( response ) {
$('#message_box').html('SUCCESS');
})
.fail(function( ) { $('#message_box').html('FAIL'); })
.always(function( ) { /*$('#message_box').html('SUCCESS');*/ });
I was having some issues using the above solutions with an "associative array" type object. These solutions seem to preserve the values, but they do not preserve the actual names of the objects that those values are associated with, which can cause some issues. So I put together the following functions which I am using instead:
function flattenAssocArr(object) {
if(typeof object == "object") {
var keys = [];
keys[0] = "ASSOCARR";
keys.push(...Object.keys(object));
var outArr = [];
outArr[0] = keys;
for(var i = 1; i < keys.length; i++) {
outArr[i] = flattenAssocArr(object[keys[i]])
}
return outArr;
} else {
return object;
}
}
function expandAssocArr(object) {
if(typeof object !== "object")
return object;
var keys = object[0];
var newObj = new Object();
if(keys[0] === "ASSOCARR") {
for(var i = 1; i < keys.length; i++) {
newObj[keys[i]] = expandAssocArr(object[i])
}
}
return newObj;
}
Note that these can't be used with any arbitrary object -- basically it creates a new array, stores the keys as element 0, with the data following it. So if you try to load an array that isn't created with these functions having element 0 as a key list, the results might be...interesting :)
I'm using it like this:
var objAsString = JSON.stringify(flattenAssocArr(globalDataset));
var strAsObject = expandAssocArr(JSON.parse(objAsString));

Categories

Resources