Need help iterating through a complex Json (no jquery) - javascript

I need to create a symfony2 bundle that generates a sidebar from a YAML file
I created this YAML structure
Sidebar:
- Frontpage:
- Dashboard:
_icon: 'icon-home'
_route: 'link'
- Actions:
- My_Likes:
_icon: 'icon-dislike'
_route: 'link'
- My_Dislikes:
_icon: 'icon-home'
_route: 'link'
- Interests:
- Add_Interest:
_icon: 'icon-home'
_route: 'link'
which returns this JSON as a response.
{
"Sidebar": [
{
"Frontpage": [
{
"Dashboard": {
"_icon": "icon-home",
"_route": "link"
}
}
]
},
{
"Actions": [
{
"My_Likes": {
"_icon": "icon-dislike",
"_route": "link"
}
},
{
"My_Dislikes": {
"_icon": "icon-home",
"_route": "link"
}
}
]
},
{
"Interests": [
{
"Add_Interest": {
"_icon": "icon-home",
"_route": "link"
}
}
]
}
]
}
Using ajax, the json is returned on the 'data' variable on the client side
Sidebar.model.request(function(data)
{
for(var a=0; a< data.Sidebar.length; a++ )
{
console.log(data.Sidebar[a]);
}
});
I need to find a way to iterate through the parents and find the corresponding children.
I only need help creating the for loop, so a solution using console.log(data[stuff]); would be enough
EDIT:
here is the adjusted snippet of Daniel Rosano's code
Sidebar.model.request(function(data)
{
//Get Sidebar items
var SidebarItems = data.Sidebar;
//Find Categories in Sidebar Items
for(var a=0; a< SidebarItems.length; a++ )
{
var category = SidebarItems[a];
//Get Category name and append it to sidebar
var category_name = getSubitemName(category);
Sidebar.view.renderCategory(category_name);
//find subitems in categories
for(var b=0; b < category[category_name].length; b++)
{
var button = category[category_name][b];
var button_name = getSubitemName(button);
var button_attributes = button[button_name];
console.log(button_attributes['_icon']);
Sidebar.view.renderButton(button_name);
}
}
function getSubitemName(parent)
{
for(child in parent)
{
return child.toString();
}
}
});
this is the result, thanks Daniel

I know you've already accepted an answer, but I had already written this and then got distracted before posting so I thought I'd share it anyway.
It's a recursive iterator that walks through any arrays or objects it finds in whatever you pass in. It also keeps track of the "path" down to any particular item and the level (mostly for illustrative purposes, but it could be otherwise useful too). A general purpose iterator that would work for any data passed in, pretty much has to be recursive to handle arbitrary depth.
function iterate(item, path, level) {
level = level || 0;
path = path || "root";
if (typeof item === "object") {
if (Array.isArray(item)) {
out("iterating array: " + path, level);
for (var i = 0; i < item.length; i++) {
iterate(item[i], path + "[" + i + "]", level + 1);
}
} else {
out("iterating object: " + path, level);
for (var prop in item) {
// skip any properties on the prototype
if (item.hasOwnProperty(prop)) {
iterate(item[prop], path + "." + prop, level + 1);
}
}
}
} else {
// leaf level property
out(path + " = " + item, level);
}
}
Working demo to see how the path and level work: http://jsfiddle.net/jfriend00/k8aosv59/

Not sure if this is what you need
for (var a = 0; a < t.Sidebar.length; a++) {
var children = t.Sidebar[a];
for (k in children) {
var subchild = children[k];
for (m in subchild) {
var sschild = subchild[m];
for (n in sschild) {
// menu variable has the inner childs (having "_icon" and "_route")
var menu = sschild[n];
console.log(menu._icon+ " "+menu._route);
}
}
}
}
Hope it helps
Dan

You can do it recursively:
function iterate(obj) {
console.log(obj);
for (var key in obj) {
var items = obj[key];
for(var i=0,l=items.length;i<l;i++) {
iterate(items[i]);
}
}
}
iterate(data);
Fiddle

Related

JavaScript: How to re-index multidimensional object after deleting a nested object?

I need to delete a nested object based on index and i and then re-index the object. I am using Vuejs, so this is in the methods. However, this code doesn't work the way I intended it to. The main issue is deleting this.realTagFilters[0][0] will also delete this.realTagFilters[1][0] and this.realTagFilters[2][0] and so on. It will also occasionally not correctly index the index or i but I have not found the cause of that issue yet. Overall, this code is pretty buggy and I could use some outside perspective on how to properly create this function.
Edit: I am using an npm package for searchable multi-select selection lists that only allow objects to be passed to the options attribute.
Here is the script:
export default {
props: {
tag_filters: Object
},
data() {
return {
tagfilters: {
0: {
0: {
code: {
code: 'Thank You Codes'
},
condition: {
condition: 'AND'
}
},
}
}
}
}
computed: {
realTagFilters() {
if (Object.keys(this.tag_filters).length > 0 && typeof Object.keys(this.tag_filters) !== 'undefined') {
return this.tag_filters['filter'];
} else {
return this.tagfilters;
}
}
},
method: {
deleteFilter(index, i){
delete this.realTagFilters[index][i];
for(var property in this.realTagFilters) {
for (var p in this.realTagFilters[property]) {
if(property > index && p > i) {
property -= 1;
this.realTagFilters[property] = this.realTagFilters[property + 1];
delete this.realTagFilters[property + 1];
if(p > i) {
p -= 1;
this.realTagFilters[property][p] = this.realTagFilters[property][p + 1];
delete this.realTagFilters[property][p + 1];
}
}
else if (property == index && p > i) {
p -= 1;
this.realTagFilters[property][p] = this.realTagFilters[property][p + 1];
delete this.realTagFilters[property][p + 1];
}
}
}
if(Object.keys(this.realTagFilters[index]).length == 0) {
delete this.realTagFilters[index];
}
this.$forceUpdate();
},
}
}
You could try Vue's delete function in favor of javascript's delete keyword, so instead of doing delete this.realTagFilters[index][i] you'd do:
this.$delete(this.realTagFilters[index], i);
Another trick to let Vue know data has changed is to replace your object with a new object, like so:
// after you've done your operations:
this.realTagFilters = {...this.realTagFilters};
The last option is this.$forceUpdate() which you are already doing.

Building an AST in JavaScript

I am attempting to build a parser for BBCode in JavaScript that will allow me to transpile a string with BBCode in it to a string with HTML. I have in my head how it is all supposed to work and I even have two of the parser steps built.
Right now the entire process of the parser can be described as
Get input
Break input into tokens (tokenize)
Add information about the tokens (lex)
Build the AST from the tokens (parse)
Clean up the AST based on grammar rules (clean)
Evaluate AST and transform to HTML (evaluate)
Return the HTML string
I have a general idea of how to do all of this in my head except for step four.
When I reached step four I ran into a problem when building the AST. The problem was how would I go about recursively building this tree. I have in the past recursively built two dimensional arrays but a variable depth tree is way out of my scope of abilities.
In my head I think that the tree should look something like this:
// Hello, [b]World![/b]
{
"text": "Hello, ",
"tag": {
"type": "b",
"text": "World!"
}
}
But when trying to generate this I have an issue with recursively building this down.
A more complex example would be as follows:
// [c=red]Hello Tom, [/c][b][c=green]how are you?[/c][/b]
{
"tag": {
type: "c",
"parameters": "red",
"text": "Hello Tom, "
"tag": {
"type": "b",
"tag": {
"type": "c",
"parameters": "green",
"text": "how are you?"
}
}
}
}
The main issue I run across is keeping my place while building down without accidentally overwriting the entire tree.
Currently the code I am using is:
var bbcode = {};
bbcode._tokens = {
'TO_DEL': '[',
'TC_DEL': ']',
'TE_DEL': '/',
'EQ_DEL': '='
};
bbcode._tags = ['c', 'b'];
bbcode.parse = function(bbcode) {
var tokens = this._tokenize(bbcode);
tokens = this._lex(tokens);
var ast = this._parse(tokens);
console.log(JSON.stringify(ast, null, 4));
//return tokens;
};
bbcode._isToken = function(token) {
for (var k in this._tokens) {
if (this._tokens[k] === token) {
return true;
}
}
return false;
};
bbcode._isTag = function(token) {
return (this._tags.indexOf(token) > -1) ? true : false;
};
bbcode._getType = function(token) {
for (var k in this._tokens) {
if (this._tokens[k] === token) {
return k;
}
}
};
bbcode._next = function(tokens, curr) {
return tokens[curr + 1][0];
};
bbcode._previous = function(tokens, curr) {
return tokens[curr - 1][0];
};
bbcode._tokenize = function(bbcode) {
var tree = [];
var temp = '';
for (var i = 0; i < bbcode.length; i++) {
if (this._isToken(bbcode[i])) {
if (temp.length > 0) {
tree.push(temp);
temp = '';
}
tree.push(bbcode[i]);
} else {
temp += bbcode[i];
}
}
return tree;
};
bbcode._lex = function(tokens) {
var tree = [];
for (var i = 0; i < tokens.length; i++) {
if (this._isToken(tokens[i])) {
tree.push([this._getType(tokens[i]), tokens[i]]);
} else if (this._isTag(tokens[i])) {
tree.push(['BB_TAG', tokens[i]]);
} else {
tree.push(['BB_STRING', tokens[i]]);
}
}
return tree;
};
/*****************************************************************************/
/* I need help with the block below */
/*****************************************************************************/
bbcode._parse = function(tokens) {
var tree = {};
for (var i = 0; i < tokens.length; i++) {
if (tokens[i][0] === 'BB_STRING') {
if (tree['text']) {
tree['text'] += tokens[i][1];
} else {
tree['text'] = tokens[i][1];
}
} else if (tokens[i][0] === 'TO_DEL') {
if (this._next(tokens, i) === 'BB_TAG') {
tree['tag'] = {};
} else {
if (tree['text']) {
tree['text'] += tokens[i][1];
} else {
tree['text'] = tokens[i][1];
}
}
}
}
return tree;
};
/*****************************************************************************/

Get the level of a hierarchy

I have an array of objects, Where each object has an id and a ParentId property (so they can be arranged in trees). They are in no particular order.
Please note that the id's and parentId's will not be integers, they will be strings (just wanted to have the sample code cleaner..)
There is only one root: lets say its id:1
The data looks like so:
data = [
{
id:"id-2",
parentId:"id-3"
},
{
id:"id-4",
parentId:"2"
},
{
id:"id-3",
parentId:"id-4"
},
{
id:"id-5",
parentId:"id-4"
},
{
id:"id-6",
parentId:"id-1"
},
{
id:"id-7",
parentId:"id-1"
}
// and so on...
]
I am looking for a efficient way to give each object a level property which should specify the nested level it is...
They should then look like this:
data = [
{
id:"id-2",
parentId:"id-1",
level:2
},
{
id:"id-3",
parentId:"id-4",
level:5
},
{
id:"id-4",
parentId:"id-2",
level:3
},
{
id:"id-5",
parentId:"id-4",
level:5
},
{
id:"id-6",
parentId:"id-1",
level:2
},
{
id:"id-7",
parentId:"id-3",
level:4
}
// and so on...
]
In short:
I want that level to be added dynamically via looping thru the array and figuring out the hierarchy..
Additionally, (if posible) they should then be sorted according to there order, like for instance all objects level:3's from the same parent should be next to each other, not that there should be siblings of the same parent next to each other rather then two cousins of level 3 next to each other.
A working example of the below code is on jsFiddle.
Index the tree by id and traverse it upwards, from each node, and count until you hit the root. By indexing first, we approach O(n) time complexity (depending on tree density). ****Updated to satisfy the sorting requirement, and allow exclusion of root node***:
function levelAndSort(data, startingLevel) {
// indexes
var indexed = {}; // the original values
var nodeIndex = {}; // tree nodes
var i;
for (i = 0; i < data.length; i++) {
var id = data[i].id;
var node = {
id: id,
level: startingLevel,
children: [],
sorted: false
};
indexed[id] = data[i];
nodeIndex[id] = node;
}
// populate tree
for (i = 0; i < data.length; i++) {
var node = nodeIndex[data[i].id];
var pNode = node;
var j;
var nextId = indexed[pNode.id].parentId;
for (j = 0; nextId in nodeIndex; j++) {
pNode = nodeIndex[nextId];
if (j == 0) {
pNode.children.push(node.id);
}
node.level++;
nextId = indexed[pNode.id].parentId;
}
}
// extract nodes and sort-by-level
var nodes = [];
for (var key in nodeIndex) {
nodes.push(nodeIndex[key]);
}
nodes.sort(function(a, b) {
return a.level - b.level;
});
// refine the sort: group-by-siblings
var retval = [];
for (i = 0; i < nodes.length; i++) {
var node = nodes[i];
var parentId = indexed[node.id].parentId;
if (parentId in indexed) {
var pNode = nodeIndex[parentId];
var j;
for (j = 0; j < pNode.children.length; j++) {
var child = nodeIndex[pNode.children[j]];
if (!child.sorted) {
indexed[child.id].level = child.level;
retval.push(indexed[child.id]);
child.sorted = true;
}
}
}
else if (!node.sorted) {
indexed[node.id].level = node.level;
retval.push(indexed[node.id]);
node.sorted = true;
}
}
return retval;
}
Example:
// level 0 (root) excluded
var startingLevel = 1;
var someData = [
{id : "id-1", parentId : "id-0"},
{id : "id-2", parentId : "id-0"},
{id : "id-3", parentId : "id-2"},
{id : "id-4", parentId : "id-3"},
{id : "id-5", parentId : "id-4"},
{id : "id-6", parentId : "id-4"},
{id : "id-7", parentId : "id-0"},
{id : "id-8", parentId : "id-1"},
{id : "id-9", parentId : "id-7"},
{id : "id-10", parentId : "id-1"},
{id : "id-11", parentId : "id-1"},
{id : "id-12", parentId : "id-1"}
];
var outputArray = levelAndSort(someData, startingLevel);
Output:
Note
If you change the input order, the sort comes out a little different, but it's still correct (i.e., in level-order, grouped by sibling).
I'm not sure where you get the value for level so I'll assume that its just an integer. BTW, you can add the level property to each of your array's item by looping through it.
for (var i = 0, l = data.length; i < l; i++) {
data[i].level = i
}
which will give
data = [{id:"1", parentId:"3", level:0 }, {id:"2", parentId:"1", level:1 } ...]
Here is your working code. Level starts at 2.
ALERT: If a level cannot be calculated, the application may go into an infinite loop. So, make sure the parentId is valid for all objects and at least one of them have parentId="id-1".
<script type="text/javascript">
data = [
{
id:"id-2",
parentId:"id-3"
},
{
id:"id-4",
parentId:"id-2"
},
{
id:"id-3",
parentId:"id-5"
},
{
id:"id-5",
parentId:"id-1"
}
];
function processData() {
flag = true;
while(flag) {
flag = false;
for(var i = 0; i < data.length; i++) {
if(data[i].parentId == "id-1") {
data[i].level = 2;
} else {
l = getLevel(data[i].parentId);
if(l > 0) {
data[i].level = l + 1;
} else {
flag = true;
}
}
}
}
}
function getLevel(id) {
for(var i = 0; i < data.length; i++) {
if(data[i].id == id) {
if(data[i].level) {
return data[i].level;
} else {
return 0;
}
}
}
return 0;
}
processData();
console.log(data);
</script>
One way to address this without the need for recursion is to create a DOM hierarchy. For each item in the array, create a div and attach it to its parent. then walk the DOM and assign the levels (top is level 1, then add 1 for each child).
I have set up a rough demo here:
http://jsfiddle.net/4AqgM/
An excerpt from the code:
top.dataset.level="1";
var topChildren=top.getElementsByTagName("div");
for (var i=0;i<topChildren.length;i++) {
topChildren[i].dataset.level=parseInt(topChildren[i].parentNode.dataset.level)+1;
}

How to loop through JSON array?

I have some JSON-code which has multiple objects in it:
[
{
"MNGR_NAME": "Mark",
"MGR_ID": "M44",
"EMP_ID": "1849"
},
{
"MNGR_NAME": "Steve",
"PROJ_ID": "88421",
"PROJ_NAME": "ABC",
"PROJ_ALLOC_NO": "49"
}
]
My JSON loop snippet is:
function ServiceSucceeded(result)
{
for(var x=0; x<result.length; x++)
{
}
}
Could you please let me know how to check there is no occurence of "MNGR_NAME" in the array. (It appears twice in my case.)
You need to access the result object on iteration.
for (var key in result)
{
if (result.hasOwnProperty(key))
{
// here you have access to
var MNGR_NAME = result[key].MNGR_NAME;
var MGR_ID = result[key].MGR_ID;
}
}
You could use jQuery's $.each:
var exists = false;
$.each(arr, function(index, obj){
if(typeof(obj.MNGR_NAME) !== 'undefined'){
exists = true;
return false;
}
});
alert('Does a manager exists? ' + exists);
Returning false will break the each, so when one manager is encountered, the iteration will stop.
Note that your object is an array of JavaScript objects.
Could you use something like this?
var array = [{
"MNGR_NAME": "Mark",
"MGR_ID": "M44",
"EMP_ID": "1849"
},
{
"MNGR_NAME": "Steve",
"PROJ_ID": "88421",
"PROJ_NAME": "ABC",
"PROJ_ALLOC_NO": "49"
}];
var numberOfMngrName = 0;
for(var i=0;i<array.length;i++){
if(array[i].MNGR_NAME != null){
numberOfMngrName++;
}
}
console.log(numberOfMngrName);
This will find the number of occurrences of the MNGR_NAME key in your Object Array:
var numMngrName = 0;
$.each(json, function () {
// 'this' is the Object you are iterating over
if (this.MNGR_NAME !== undefined) {
numMngrName++;
}
});
Within the loop result[x] is the object, so if you wanted to count a member that may or may not be present;
function ServiceSucceeded(result)
{
var managers = 0
for(var x=0; x<result.length; x++)
{
if (typeof result[x].MNGR_NAME !== "undefined")
managers++;
}
alert(managers);
}
You can iterate over the collection and check each object if it contains the property:
var count = 0;
var i;
for(i = 0; i < jsonObj.length; i += 1) {
if(jsonObj[i]["MNGR_NAME"]) {
count++;
}
}
Working example: http://jsfiddle.net/j3fbQ/
You could use $.each or $.grep, if you also want to get the elements that contain the attribute.
filtered = $.grep(result, function(value) {
return (value["MNGR_NAME"] !== undefined)
});
count = filtered.length
Use ES6...
myobj1.map(items =>
{
if(items.MNGR_NAME) {
return items.MNGR_NAME;
}else {
//do want you want.
}
})
Thanks.

Javascript building tree hierarchy

var array = [{"grandpa","father"}, {"father"}, {"grandpa","father","me"}];
Given the above array, I want to product a java-script object(JSON) like below, that has the parent-child like structure.
{"id":"grandpa",
"children":[
{"id":"father",
"children":[
{"id":"me",
"children":[]
}]
}]
}
If you're asking how you would take a list of hierarchy paths and create a tree structure, here's how you could do it in JavaScript:
function convertToHierarchy(arry /* array of array of strings */)
{
var item, path;
// Discard duplicates and set up parent/child relationships
var children = {};
var hasParent = {};
for (var i = 0; i < arry.length; i++)
{
var path = arry[i];
var parent = null;
for (var j = 0; j < path.length; j++)
{
var item = path[j];
if (!children[item]) {
children[item] = {};
}
if (parent) {
children[parent][item] = true; /* dummy value */
hasParent[item] = true;
}
parent = item;
}
}
// Now build the hierarchy
var result = [];
for (item in children) {
if (!hasParent[item]) {
result.push(buildNodeRecursive(item, children));
}
}
return result;
}
function buildNodeRecursive(item, children)
{
var node = {id:item, children:[]};
for (var child in children[item]) {
node.children.push(buildNodeRecursive(child, children));
}
return node;
}
convertToHierarchy([["1","2"], ["1"], ["1","2","3"]]);
Edit:
Your question is still ambiguous. My previous version assumed these two things:
Each node ID uniquely identifies a node
A specified hierarchy path can start at other than the root node
In this sample, I'll assume the following:
Node IDs are not unique, but they are unique within the children of a particular node
All hierarchy paths start at the root node of the tree
Here's the code:
function convertToHierarchy(arry /* array of array of strings */)
{
// Build the node structure
var rootNode = {id:"root", children:{}}
for (var i = 0; i < arry.length; i++)
{
var path = arry[i];
buildNodeRecursive(rootNode, path, 0);
}
return rootNode;
}
function buildNodeRecursive(node, path, idx)
{
if (idx < path.length)
{
item = path[idx];
if (!node.children[item])
{
node.children[item] = {id:item, children:{}};
}
buildNodeRecursive(node.children[item], path, idx + 1);
}
}
The hierarchy structure is returned, but the format's a bit different. However, you should get the picture.
I think this should work. I'm using firebug to track the structure of the output.
var el = {"name": "Level 1", "paths" : ["fruits"]};
var el2 = {"name": "Level 3", "paths" : ["fruits", "apples", "fuji"]};
var el3 = {"name": "Level 4", "paths" : ["fruits", "apples", "fuji", "red"]};
var el4 = {"name": "Level 2", "paths" : ["fruits", "apples"]};
var allEl = [el, el2, el3, el4];
/* Define recursive function for setting the child */
function setChild(parent, pos, arr, name)
{
if(pos < arr.length)
{
if(pos == arr.length-1) //last element of the paths
parent.name = name;
if(!parent.children){
parent.children = [];
parent.children[0] = new Object();
}
setChild(parent.children[0], pos + 1, arr, name);
}
}
/* Test starts here */
var root = new Object();
for(var i=0; i<allEl.length; i++)
{
var el = allEl[i];
setChild(root, 0, el.paths, el.name);
}
//Firefox debugging ...getfirebug.com
console.debug(root);
If you want to encode JSON, just use a JSON library.
Don't try and roll your own.

Categories

Resources