Where is the bug in my recursive JSON parser? - javascript

I'd like to create a recursive function to parse json-like data as below. When key is xtype, a new class will be created. In particular, when xtype = gridpanel/treepanel, all the properties have to be its constructor argument, otherwise, properties will be added after class has been created.
My recursive function as below, I got an error 'too much recursion' at line 21 in ext-all.js.
Please take a look, how am I able to solve this problem?
codes in main program:
me.recursiveParser(null, data.json);
Ext.apply(me.root, me.parent);
me.desktopCfg = me.root;
recursiveParser function:
recursiveParser: function(nodeName, jsonData) {
var properties = {};
var isSpecial = false;
var isLeaf = true;
var parent, child, special;
//Base factor
for (var key in jsonData) {
var value = jsonData[key];
//To collect all the properties that is only initialized with '#'.
if (key.toString().indexOf("#") === 0) {
key = key.replace("#", "");
if(typeof(value) === "string"){
properties[key] = "'"+value+"'";
}else{
//Later, should have to deal with the empty value or array with no keys and only elements.
properties[key] = value;
}
if(key === "xtype"){
//To initialize the root
if(nodeName === null){
this.root = this.createNewObject(value, null);
}
if(value === "gridpanel" || value === "treepanel"){
isSpecial = true;
special = value;
}else{
child = this.createNewObject(value, null);
}
}
}else {
isLeaf = false;
}
}
if(isSpecial){
child = this.createNewObject(special, properties);
}
//To add the subnode and its properties to its parent object.
if (nodeName !== null && typeof(nodeName) === "string") {
if(child === null){
Ext.apply(parent, properties);
}else{
Ext.apply(parent, child);
}
}
if(isLeaf){
return;
}
for (var key in jsonData) {
var value = jsonData[key];
if (key.toString().indexOf("#") === 0) {
continue;
}else{
if(value === "[object Object]"){
for(var index in value){
this.recursiveParser(key, value[index]);
}
}else{
this.recursiveParser(key, value);
}
Ext.apply(this.root, parent);
}
}
}
createNewObject function:
createNewObject: function(objType, properties){
if(objType){
switch (objType){
case "gridpanel":
return new MyProg.base.GridPanel(properties);
break;
case "treepanel":
return new MyProg.base.TreePanel(properties);
break;
case "tabpanel":
return new MyProg.base.TabPanel();
break;
case "tab":
return new MyProg.base.Tabs();
break;
case "formpanel":
return new MyProg.base.Accordion();
break;
case "fieldset":
return new MyProg.base.FieldSet();
break;
case "textfield":
return new MyProg.base.Fields();
break;
case "panel":
return new MyProg.base.Accordion();
break;
default:
return new MyProg.base.Accordion();
};
};
}
data.json:
var data = {
"json": {
"#title": "BusinessIntelligence",
"#xtype": "tab",
"#layout": "accordion",
"items": [
{
"#title": "SalesReport",
"#ctitle": "SalesReport",
"#layout": "column",
"items": [
{}
]
},
{
"#title": "ContentPlayingReport",
"#ctitle": "ContentPlayingReport",
"#layout": "column",
"items": [
{}
]
},
{
"#title": "BusinessIntelligence",
"#ctitle": "BusinessIntelligence",
"#layout": "column",
"items": [
{}
]
}
]
}
}

I modified the recursion part, it looks more elegant now. All the xtype works just fine, except gridpanel, I've check DOM, everything is in there, but still got error message:
TypeError: c is undefined
...+g.extraBaseCls);delete g.autoScroll;if(!g.hasView){if(c.buffered&&!c.remoteSort...
ext-all.js (line 21, col 1184416)
I suspect it's an ExtJS bug. I'll try to find another way out.
recursion program:
recursiveParser: function (jsonData) {
var me = this;
var properties = {};
for ( var key in jsonData ){
var value = jsonData[key];
var items = (value.constructor === Array) ? [] : {};
if (value instanceof Object) {
if (isNaN(key)){
if (items.constructor === Array) {
for (var node in value){
items.push(me.recursiveParser(value[node]));
}
properties[key] = items;
} else {
properties[key] = me.recursiveParser(value);
}
} else {
return me.recursiveParser(value);
}
} else {
if (key.toString().indexOf('#') === 0){
key = key.replace('#', '');
properties[key] = value;
}
}
}
return properties;
}

Related

JavaScript Promise Method not returning any data

I am creating a react native application.
I have a back button that fires the function findItem. findItem the uses async method searchJson. searchJson searches recursive json to find parent object based on id. However it never returns any results.
findItem:
findItem() {
//Pass null so top level json will be pulled
let result = this.searchJson(null).done();
let abv = 2;
// this.setState(previousState => {
// return {
// data: result,
// parentID: result.parentid
// };
// });
}
searchJson:
async searchJson(object) {
return new Promise(resolve => {
//use object or pull from porp - all data
let theObject = object == null ? this.props.data : object;
var result = null;
if (theObject instanceof Array) {
for (var i = 0; i < theObject.length; i++) {
result = this.searchJson(theObject[i]);
if (result) {
break;
}
}
}
else {
for (var prop in theObject) {
console.log(prop + ': ' + theObject[prop]);
if (prop == 'id') {
if (theObject[prop] == this.state.parentID) {
return theObject;
}
}
if (theObject[prop] instanceof Object || theObject[prop] instanceof Array) {
result = this.searchJson(theObject[prop]);
if (result) {
break;
}
}
}
}
if(result != null)
resolve(result);
});
}
Any help will be greatly appreciated.
Ok so I never got this to work but my workaround was this.
I Modified the findItem method:
findItem() {
let FinNode = null;
for (var node in this.props.data) {
FinNode = this.searchJson(this.state.parentID, this.props.data, this.props.data[node].book);
if (FinNode != null) {
this.setState(previousState => {
return {
data: FinNode[0].book.parentid == "" ? null : FinNode,
parentID: FinNode[0].book.parentid
};
});
break;
}
}
}
And then the searchJson:
searchJson(id, parentArray, currentNode) {
if (id == currentNode.id) {
return parentArray;
} else {
var result;
for (var index in currentNode.books) {
var node = currentNode.books[index].book;
if (node.id == id)
return currentNode.books;
this.searchJson(id, currentNode.books, node);
}
return null;
}
}
This allowed for all my nodes to be searched and the for loop made so that there is no need for async. This does have some drawbacks but seems to work decently without any massive performance issues.

Convert a jQuery object into JSON

I have a jQuery object with inside value and method also. I need to convert it into JSON.
Look at the following object:
I have self.pgrid.config.columnFields as above screenshot.
When I have converted this object into JSON and get that JSON back to object, it is not returning aggregateFunc and formatFunc.
Look at following screenshot of JSON.parse(JSON.stringify(self.pgrid.config.columnFields)):
Here I want to get those two methods also. How can I get them?
Code of generating object is as follows:
this.columnFields = (config.columns || []).map(function (fieldconfig) {
fieldconfig = ensureFieldConfig(fieldconfig);
return createfield(self, axe.Type.COLUMNS, fieldconfig, getfield(self.allFields, fieldconfig.name));
});
function ensureFieldConfig(obj) {
if (typeof obj === 'string') {
return {
name: self.captionToName(obj)
};
}
return obj;
}
function createfield(rootconfig, axetype, fieldconfig, defaultfieldconfig) {
var axeconfig;
var fieldAxeconfig;
if (defaultfieldconfig) {
switch (axetype) {
case axe.Type.ROWS:
axeconfig = rootconfig.rowSettings;
fieldAxeconfig = defaultfieldconfig.rowSettings;
break;
case axe.Type.COLUMNS:
axeconfig = rootconfig.columnSettings;
fieldAxeconfig = defaultfieldconfig.columnSettings;
break;
case axe.Type.DATA:
axeconfig = rootconfig.dataSettings;
fieldAxeconfig = defaultfieldconfig.dataSettings;
break;
default:
axeconfig = null;
fieldAxeconfig = null;
break;
}
} else {
axeconfig = null;
fieldAxeconfig = null;
}
var merged = mergefieldconfigs(fieldconfig, fieldAxeconfig, axeconfig, defaultfieldconfig, rootconfig);
return new Field({
name: getpropertyvalue('name', merged.configs, ''),
caption: getpropertyvalue('caption', merged.configs, ''),
sort: {
order: getpropertyvalue('order', merged.sorts, null),
customfunc: getpropertyvalue('customfunc', merged.sorts, null)
},
subTotal: {
visible: getpropertyvalue('visible', merged.subtotals, true),
collapsible: getpropertyvalue('collapsible', merged.subtotals, true),
collapsed: getpropertyvalue('collapsed', merged.subtotals, false) && getpropertyvalue('collapsible', merged.subtotals, true)
},
aggregateFuncName: getpropertyvalue('aggregateFuncName', merged.functions, 'sum'),
aggregateFunc: getpropertyvalue('aggregateFunc', merged.functions, aggregation.sum),
formatFunc: getpropertyvalue('formatFunc', merged.functions, null)
}, false);
}
function getpropertyvalue(property, configs, defaultvalue) {
for (var i = 0; i < configs.length; i++) {
if (configs[i][property] != null) {
return configs[i][property];
}
}
return defaultvalue;
}

Deep changing values in a JavaScript object

I have an object which contains an unknown number of other objects. Each (sub-)object may contain boolean values as strings and I want to change them to real boolean values. Here's an example object:
var myObj = {
my1stLevelKey1: "true",
my1stLevelKey2: "a normal string",
my1stLevelKey3: {
my2ndLevelKey1: {
my3rdLevelKey1: {
my4thLevelKey1: "true",
my4thLevelKey2: "false"
}
},
my2ndLevelKey2: {
my3rdLevelKey2: "FALSE"
}
}
}
What I want in the end is this:
var myObj = {
my1stLevelKey1: true,
my1stLevelKey2: "a normal string",
my1stLevelKey3: {
my2ndLevelKey1: {
my3rdLevelKey1: {
my4thLevelKey1: true,
my4thLevelKey2: false
}
},
my2ndLevelKey2: {
my3rdLevelKey2: false
}
}
}
Important is that the number sub-objects/levels is unknown. How can I do this effectively by either using classic JavaScript or Mootools?
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));
The obj[k].toLowerCase() is to make it case-insensitive
Walk each level of the object and replace boolean string values with the appropriate booleans. If you find an object, recurse in and replace again.
You can use Object.keys to grab all the members of each object, without worrying about getting inherited properties that you shouldn't.
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>
JavaScript data structures elegantly can be sanitized by recursive functional reduce approaches.
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);
Plain javascript recursion example:
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 MooTools example, by extending the Object type with custom mapDeep() function:
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;
})

Alter value in object

Writing Javascript, I have an object/class with the following attributes:
this.option1Active = null;
this.option2Active = null;
this.option3Active = null;
this.option4Active = null;
I would like to set one of those attributes to true based on the parameter genre
function selectGenre (genre) {
if (genre === 'option1') {
this.option1Active = true;
}
else if (genre === 'option2') {
this.option2Active = true;
}
else if (genre === 'option3') {
this.option3Active = true;
}
else if (genre === 'option4') {
this.option4Active = true;
}
}
Though writing if statements is not a sustainable solution.
I'd like to do something like this:
function selectGenre (genre) {
var options = {
'option1': this.option1Active,
'option2': this.option2Active,
'option3': this.option3Active,
'option4': this.option4Active
};
options[genre] = true;
}
But that only set options[index] to true, not e.g. this.option1Active.
Is there a way to change the reference a key of an object points to?
If not, other ways of refactoring the if statements is greatly appreciated.
You can use a string for the property name to set on this.
var genreOptions = {
'option1': 'option1Active',
'option2': 'option2Active',
'option3': 'option3Active',
'option4': 'option4Active'
};
function selectGenre (genre) {
this[genreOptions[genre]] = true;
}
It seems you can just append "Active" to genre to get the property itself:
function selectGenre (genre)
{
var prop = genre + 'Active';
if (typeof this[prop] != 'undefined') {
this[prop] = true;
}
}
Though, it would be easier if you could use an array as your property instead, i.e. this.optionActive[3] vs. this.option3Active.
Is this what you want ?
var obj = {};
obj.option1Active = null;
obj.option2Active = null;
obj.option3Active = null;
obj.option4Active = null;
var options = {
option1: 'option1Active',
option2: 'option2Active',
option3: 'option3Active',
option4: 'option4Active'
};
function selectGenre(genre) {
obj[options[genre]] = true;
}
console.log(obj);
selectGenre('option2');
console.log(obj);

How does Javascript evaluate the right parenthesis?

I'm working on a calculator that takes an expression such as (5+4) and evaluates it by passing the buttons pressed to an array, and then building a parse tree from the data in the array.
What's interesting/strange, is that my code won't push the value of the right parenthesis to the array. Here is my code, could someone help me out?
The console.log activeButton shows that is the value of the button being pressed, but even when I placed calcArray.push() outside the if statements it would not push ) to an array.
$(document).ready(function(){
var calcArray = new Array();
$("input").click(function(){
var activeButton = this.value;
console.log(activeButton);
if(!isNaN(activeButton))
{
calcArray.push(parseInt(activeButton));
console.log(calcArray);
}
else if(activeButton === "=")
{
evaluate(buildTree(calcArray));
calcArray = [];
}
else
{
calcArray.push(activeButton);
}
});
});
The BuildTree code:
function BinaryTree(root) {
this.root = root;
this.activeNode = root;
}
function Node(element){
this.element = element;
this.parent;
this.rightChild;
this.leftChild;
this.setLeft = function(node){
this.leftChild = node;
node.parent = this;
};
this.setRight = function(node){
this.rightChild = node;
node.parent = this;
};
}
//methods
var buildTree = function(array)
{
var tree = new BinaryTree(new Node(null));
for(var i = 0; i < array.length; i++)
{
var newNode = new Node(array[i]);
if(array[i] == "(")
{
newNode.element = null;
tree.activeNode.setLeft(newNode);
tree.activeNode = newNode;
}
else if(array[i] == "+" || array[i] == "-" || array[i] == "/" || array[i] == "*")
{
tree.activeNode.element = newNode.element;
tree.activeNode.setRight(new Node(null));
tree.activeNode = tree.activeNode.rightChild;
}
else if(array[i] == ")")
{
if(tree.activeNode.parent == null)
{
;
}
else
{
tree.activeNode = tree.activeNode.parent;
tree.root = tree.activeNode;
}
}
else
{
tree.activeNode.element = newNode.element;
tree.activeNode = tree.activeNode.parent;
}
}
return tree.activeNode;
}
var evaluate = function(node){
var newNode1, newNode2;
newNode1 = new Node(null);
newNode1.parent = node;
newNode2 = new Node(null);
newNode2.parent = node;
if(node.leftChild == null && node.rightChild == null)
return node.element;
else{
newNode1.element = evaluate(node.leftChild);
newNode2.element = evaluate(node.rightChild);
if(newNode1.parent.element == "+")
{
return Number(newNode1.element) + Number(newNode2.element);
}
if(newNode1.parent.element == "-")
{
return newNode1.element - newNode2.element;
}
if(newNode1.parent.element == "*")
{
return newNode1.element * newNode2.element;
}
else
{
return newNode1.element / newNode2.element;
}
}
};
I just tried this out using your code and it worked fine passing the value as a string:
function pushButton (value) {
var activeButton = value;
console.log(activeButton);
if(!isNaN(activeButton))
{
calcArray.push(parseInt(activeButton));
console.log(calcArray);
}
else if(activeButton === "=")
{
evaluate(buildTree(calcArray));
calcArray = [];
}
else
{
calcArray.push(activeButton);
}
};
You aren't ever printing out the array in the last case (which is where the right paren would go), so are you sure it's not on the array and you just aren't seeing the visual feedback?
If so, we need to see more of your code. Try and setup a jsfiddle.

Categories

Resources