var json;
function DamnDemo() {
json = DemoJSON();
var it = new JNode(json), it2 = it;
var levelKeys = []; /* A bit crazy structure:
[
levelN:{
keyA:[JNode, JNode,...],
keyB:[JNode, JNode,...],
...
},
levelM:...
]
*/
do {
var el = levelKeys[it.level]; // array of level say LevelN or undefined
el = levelKeys[it.level] = el || {}; // set 2 empty it if does not exist
el = el[it.key] = el[it.key] || []; // key array in say levelN
el.push(it); // save current node indexing by level, key -> array
} while (it = it.DepthFirst()) // traverse all nodes
for(var l1 in levelKeys) { // let start simply by iterating levels
l2(levelKeys[l1]);
}
console.log(JSON.stringify(json, null, 2));
}
function l2(arr) { // fun starts here...
var len = 0, items = []; // size of arr, his items to simple Array
for(var ln in arr) { // It's a kind of magic here ;-) Hate recursion, but who want to rewrite it ;-)
if (arr[ln] instanceof JNode) return 1; // End of chain - our JNode for traverse of length 1
len += l2(arr[ln]);
items.push(arr[ln]);
}
if (len == 2) { // we care only about 2 items to move (getting even 3-5)
//console.log(JSON.stringify(json));
if (!isNaN(items[0][0].key) || (items[0][0].key == items[1][0].key)) { // key is number -> ignore || string -> must be same
console.log("Keys 2B moved:", items[0][0].key, items[1][0].key, "/ level:", items[0][0].level);
var src = items[1][0]; // 2nd similar JNode
moveMissing(items[0][0].obj, src.obj); // move to 1st
//console.log(JSON.stringify(json));
if (src.level == 1) { // top level cleaning
delete src.obj;
delete json[src.key]; // remove array element
if (!json[json.length-1]) json.length--; // fix length - hope it was last one (there are other options, but do not want to overcomplicate logic)
} else {
var parent = src.parent;
var end = 0;
for(var i in parent.obj) {
end++;
if (parent.obj[i] == src.obj) { // we found removed in parent's array
delete src.obj; // delete this empty object
delete parent.obj[i]; // and link on
end = 1; // stupid marker
}
}
if (end == 1 && parent.obj instanceof Array) parent.obj.length--; // fix length - now only when we are on last element
}
} else console.log("Keys left:", items[0][0].key, items[1][0].key, "/ level:", items[0][0].level); // keys did not match - do not screw it up, but report it
}
return len;
}
function moveMissing(dest, src) {
for(var i in src) {
if (src[i] instanceof Object) {
if (!dest[i]) { // uff object, but not in dest
dest[i] = src[i];
} else { // copy object over object - let it bubble down...
moveMissing(dest[i], src[i]);
}
delete src[i];
} else { // we have value here, check if it does not exist, move and delete source
if (!dest[i]) {
dest[i] = src[i];
delete src[i];
}
}
}
}
// JSON_Node_Iterator_IIFE.js
'use strict';
var JNode = (function (jsNode) {
function JNode(json, parent, pred, key, obj, fill) {
var node, pred = null;
if (parent === undefined) {
parent = null;
} else if (fill) {
this.parent = parent;
this.pred = pred;
this.node = null;
this.next = null;
this.key = key;
this.obj = obj;
return this;
}
var current;
var parse = (json instanceof Array);
for (var child in json) {
if (parse) child = parseInt(child);
var sub = json[child];
node = new JNode(null, parent, pred, child, sub, true);
if (pred) {
pred.next = node;
node.pred = pred;
}
if (!current) current = node;
pred = node;
}
return current;
}
JNode.prototype = {
get hasNode() {
if (this.node) return this.node;
return (this.obj instanceof Object);
},
get hasOwnKey() { return this.key && (typeof this.key != "number"); },
get level() {
var level = 1, i = this;
while(i = i.parent) level++;
return level;
},
Down: function() {
if (!this.node && this.obj instanceof Object) {
this.node = new JNode(this.obj, this);
}
return this.node;
},
Stringify: function() { // Raw test stringify - #s are taken same as strings
var res;
if (typeof this.key == "number") {
res = '[';
var i = this;
do {
if (i.node) res += i.node.Stringify();
else res += "undefined";
i = i.next;
if (i) res += ','
} while(i);
res += ']';
} else {
res = '{' + '"' + this.key + '":';
res += (this.node?this.node.Stringify():this.hasNode?"undefined":'"'+this.obj+'"');
var i = this;
while (i = i.next) {
res += ',' + '"' + i.key + '":';
if (i.node) res += i.node.Stringify();
else {
if (i.obj instanceof Object) res += "undefined";
else res += '"' + i.obj + '"';
}
};
res += '}';
}
return res;
},
DepthFirst: function () {
if (this == null) return 0; // exit sign
if (this.node != null || this.obj instanceof Object) {
return this.Down(); // moved down
} else if (this.next != null) {
return this.next;// moved right
} else {
var i = this;
while (i != null) {
if (i.next != null) {
return i.next; // returned up & moved next
}
i = i.parent;
}
}
return 0; // exit sign
}
}
return JNode;
})();
// Fire test
DamnDemo();
function DemoJSON() {
return [
{
"dog": "lmn",
"tiger": [
{
"bengoltiger": {
"height": {
"x": 4
}
},
"indiantiger": {
"paw": "a",
"foor": "b"
}
},
{
"bengoltiger": {
"width": {
"a": 8
}
},
"indiantiger": {
"b": 3
}
}
]
},
{
"dog": "pqr",
"tiger": [
{
"bengoltiger": {
"width": {
"m": 3
}
},
"indiantiger": {
"paw": "a",
"foor": "b"
}
},
{
"bengoltiger": {
"height": {
"n": 8
}
},
"indiantiger": {
"b": 3
}
}
],
"lion": 90
}
]
;}