Expand flat object into hierarchical structure - javascript

I'm trying to find best approach to expand this flat structure
var input = [
{
"path":"/",
"size":1111
},
{
"path":"/test1",
"size":2222
},
{
"path":"/test1/folder2",
"size":3333
},
{
"path":"/test1/folder2",
"size":4444
},
{
"path":"/test7/folder1",
"size":5555
}
]
into this hierarchical structure
var expectedoutput = [{
"path": "/",
"size": 1111
},
{
"path": "/test1",
"size": 2222,
"items": [{
"path": "/test1/folder2",
"size": 3333,
},
{
"path": "/test1/folder2",
"size": 4444
}
]
},
{
"path": "/test7",
"items": [{
"path": "/test7/folder1",
"size": 5555
}]
}
]
Any ideas? please.

Not so bad approach, it work's but there is one scenario which it cannot handle
Scenario when parent node doesn't exists (should be created) i've commented this part.
Updated version with missing intermediate paths support
function expand_object(list) {
var map = {}, node, roots = [], i;
for (i = 0; i < list.length; i += 1) {
map[list[i].path] = i; // map
list[i].items = []; // place for children
}
for (i = 0; i < list.length; i += 1) {
node = list[i];
//find parent, example "path":"test1/folder2" parent= "test1"
var lastSlash = node.path.lastIndexOf('/');
if (lastSlash > 1) {
lastSlash = lastSlash == -1 ? node.path.length : lastSlash;
parentNode = node.path.substring(0, lastSlash);
}
else {
parentNode = "/";
}
if (parentNode !== "/") {
// creat missing nodes
if (!list[map[parentNode]]) {
list.push({ "name": parentNode ,"path": parentNode, "items": [] })
map[list[list.length-1].path] = list.length-1;
}
var c = list[map[parentNode]];
list[map[parentNode]].items.push(node);
} else {
roots.push(node);
}
}
return roots;
}
var input = [
{
"path":"/",
"size":1111
},
{
"path":"/",
"size":2222
},
{
"path":"/test1",
"size":2222
},
{
"path":"/test1/folder2",
"size":3333
},
{
"path":"/test1/folder2",
"size":4444
}
,
{ //Missing node
"path":"/test7/folder1",
"size":5555
}
]
console.log(expand_object(input));

Related

How to get an JSON Object based in key using jquery

I'm using jsTree and have tree an structured JSON object.
[{
"id": 1,
"text": "TEXT_ONE",
"children": [
{
"id": 2,
"text": "TEXT_TWO",
"children": [
{
"id": 3,
"text": "TEXT_THREE",
"children": [
]
},
{
"id": 4,
"text": "TEXT_FOUR",
"children": [
]
}
]
},
{
"id": 5,
"text": "TEXT_FIVE",
"children": [
]
}
]
},
{
"id": 6,
"text": "TEXT_SIX",
"children": [ ]
}]
I want to get the the object based on the "id" of the object.
For example if i have a function getIdFromTree(3) it will return me the JSON object as following:
{
"id": 3,
"text": "TEXT_THREE",
"children": []
},
How I do that in Javascript/JQuery?
Try this
function getObjById (tree, id) {
if(tree.id === id) {
return tree;
}
if(tree.children) {
for(var i = 0, l = tree.children.length; i < l; i++) {
var returned = getObjById(tree.children[i], id);
if(returned) {
// so that the loop doesn't keep running even after you find the obj
return returned;
}
}
}
}
Call this as follows
getObjById({children: tree}, 3); // tree is the array object above.
function findById (tree, id) {
var result, i;
if (tree.id && tree.id === id) {
result = tree;
// Revalidate array list
} else if (tree.length) {
for (i = 0; i < tree.length; i++) {
result = findById(tree[i], id);
if (result) {
break;
}
}
// Check childrens
} else if (tree.children) {
result = findById(tree.children, id);
}
return result;
}
Use filter Methode off Array
data.filter(function (obj){ obj.id== 3});
try this.... Es6
function *getObjectById(data, id) {
if (!data) return;
for (let i = 0; i< data.length; i++){
let val = data[i];
if (val.id === id) yield val;
if (val.children) yield *getObjectById(val.children , id);
}
}
now
getObjectById(arrayOfObjects, id).next().value;
try this with most effective and efficient way..
function getObjById (tree, id) {
for(var i= 0;i<tree.length;i++)
{
if(tree[i].id===id)
{
return tree[i];
}
if(tree[i].children)
{
var returned = getObjById(tree[i].children,id);
if(returned!= undefined)
return returned;
}
}
};
link:
https://jsfiddle.net/aa7zyyof/14/

Ordering recursive function results in arrays of arrays

I am currently dealing with in issue in writing a recrusive function to order some json data. I have several nested arrays of objects which i need to order into single slides. The structure is similar to the following :
[
{
"title": "a",
"children": [
{
"title": "a-a",
"children": [
{
"title": "a-a-a"
},
{
"title": "a-a-b"
}
]
},
{
"title": "a-b",
"children": [
{
"title": "a-b-a"
},
{
"title": "a-b-b"
}
]
}
]
},
{
"title": "b",
"children": [
{
"title": "b-a",
"children": [
{
"title": "b-a-a"
},
{
"title": "b-a-b"
}
]
},
{
"title": "b-b",
"children": [
{
"title": "b-b-a"
},
{
"title": "b-b-b"
}
]
}
]
}
]
I have written a recursive function :
var catalog = {
init: function() {
var _this = this;
$.getJSON("catalog.json", function(data) {
_this.slides = [];
_this.parseCategories(data.catalog.category,-1,0);
});
},
parseCategories: function(array, depth, prevParent) {
++depth;
if (!this.slides[depth]) this.slides[depth] = [];
if (!this.slides[depth][prevParent]) this.slides[depth][prevParent] = [];
this.slides[depth][prevParent].push(array);
for (var i = 0; i < array.length; i++) {
if (array[i].category) {
this.parseCategories(array[i].category, depth, i);
}
}
}
}
catalog.init();
This outputs :
However instead of retrieving the data for my third slide under format :
a-a-a
a-b-a
a-c-a
I would like to get
a-a-[a,b,c]
I was wondering if that was possible since I'm not very good at handling recursive processes. I hope I was clear and thank you for reading this.
I basically need to keep my original data structure but remove the first depth level for each iteration (slide in a slider that represent increasing depths in my data structure).
I recently wrote a algorithm to recursively handle data like this. Here is a jsfiddle and the main function
console.log('starting');
// data in tree format.
var output = {};
// data in slide format ["a-a-a", "a-a-b", "b-b-a", "b-b-b"]
var outputStrs = [];
parseData(data, output);
console.log(output);
console.log(outputStrs);
function parseData(data, store) {
// go through each element
for (var i = 0; i < data.length; i++) {
var element = data[i];
// used to keep track of where we are in the tree.
var splitElement = element.title.split('-');
var titleStart = splitElement[0];
// console.log(element);
if (_.has(element, 'children') && _.isArray(element.children)) {
// if there is a children, then recursively handle it.
store[titleStart] = {};
parseData(element.children, store[titleStart]);
} else {
// if we are at the end, then add in the data differently.
var titleEnd = splitElement[splitElement.length-1];
store[titleEnd] = titleEnd;
// create the slides
var slide = [];
for (var j = 0; j < splitElement.length; j++) {
if (j !== splitElement.length - 1) {
slide.push(titleStart);
} else {
slide.push(titleEnd);
}
}
slide = slide.join('-');
if (!_.contains(outputStrs, slide)) outputStrs.push(slide);
}
}
}
With this data the output should resemble
a
a
a
b
b
b
a
b
And outputStrs will resemble a-a-[a,b,c]
Hope this helps!!!

Get the unique items - Handlebars

My JSON looks like this:
{
"features": [
{
"id": "belly",
"scenarios": [
{
"id": "belly;a-few-cukes",
"tags": [
{
"name": "#tag1"
}
],
"steps": [
{
"name": "I have 42 cukes in my belly"
},
{
"name": "I wait 1 hour"
},
{
"name": "my belly should growls"
}
]
},
{
"id": "belly;a-few-cukes-with-new-test",
"tags": [
{
"name": "#tag2"
}
],
"steps": [
{
"name": "I have 42 cukes in my belly"
},
{
"name": "I wait 1 hour"
},
{
"name": "my belly should growl"
}
]
}
]
},
{
"id": "newbelly",
"scenarios": [
{
"id": "newbelly;a-few-cukes-with-new-feature",
"tags": [
{
"name": "#tag1"
}
],
"steps": [
{
"name": "I have 42 cukes in my belly"
},
{
"name": "I wait 1 hour"
},
{
"name": "my belly should growls"
}
]
}
]
}
]
}
I would like to retrieve all the unique tag names: i.e., #tag1, #tag2. If you notice, the #tag1 is repeated twice.
My template:
{{#getTags features}}
{{#scenarios}}
{{#tags}}
<p>{{name}}</p>
{{/tags}}
{{/scenarios}}
{{/getTags}}
Custom Helper that I created so far:
Handlebars.registerHelper('getTags', function(context, block) {
var ret = "";
for (var i = 0; i < context.length; i++) {
ret += block.fn(context[i]);
};
return ret;
});
The above custom helper returns all the tags, but I want unique ones.
Something along these lines may work:
Handlebars.registerHelper('getTags', function(context, block) {
var ret = "";
var got = [];
function contains(obj, a) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}
for (var i = 0; i < context.length; i++) {
if (!this.contains(context[i],got)) {
got.addObject(context[i]);
ret += block.fn(context[i]);
}
}
return ret;
});
Code used for testing, all javascript:
var ret = "";
var got = [];
var data = ["tag1", "tag1", "tag2", "tag3"]
function contains(obj, a) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}
for (var i = 0; i < data.length; i++) {
if (!contains(data[i],got)) {
got.push(data[i]);
ret += data[i];
}
}
console.log( ret);

Removing jquery objects from an object based on duplication

I'm trying to remove obects from an object if they appear in other objects. Really hard to exaplin! Here's an example. I have 2 Objects containing DOM image objects and I would like the DOM image objects removed from the first object if they appear in the second object.
First Object
{
"241": [{
"img": image_object_1
},
{
"img": image_object_2
},
{
"img": image_object_3
},
{
"img": image_object_4
}]
}
Second Object
{
"241": [{
"img": image_object_1
},
{
"img": image_object_3
},
{
"img": image_object_4
}]
}
Expected result of object 1
{
"241": [{
"img": image_object_2
}]
}
I have everything in a single object like so but I'm happy to change the format if needs be
{
"0": {
},
"1": {
"241.14999389648438": [{
"img": {
image_object_1
},
},
{
"img": {
image_object_2
},
},
{
"img": {
image_object_3
},
},
{
"img": {
image_object_4
},
}]
},
"2": {
"241.14999389648438": [{
"img": {
image_object_2
},
},
{
"img": {
image_object_3
},
},
{
"img": {
image_object_4
},
}]
}
}
My working code is here
jQuery.fn.reverse = [].reverse;
function same_height(){
var imob = {};
var groups = [];
var heights = [];
var tp = 0;
var img = false;
$("#ez-container .row").each(function(gi){
imob = {};
groups[gi] = {};
heights[gi] = {};
tp = 0;
img = false;
$(this).find(".ez-image img").each(function(){
img = $(this);
tp = img.offset().top;
imob = {
"img":img,
"padding":img.outerHeight(true) - (parseInt(img.css('borderBottomWidth'))+parseInt(img.css('borderTopWidth'))) - img.innerHeight()
};
if(typeof(groups[gi][tp])=="undefined"){
groups[gi][tp] = [];
heights[gi][tp] = [];
}
groups[gi][tp].push(imob);
heights[gi][tp].push(img.height());
});
});
heights.reverse();
var max_group_height = 0;
$.each(groups.reverse(),function(gix,grp){
$.each(grp,function(t,im){
if(im.length>1){
$.each(im,function(i,v){
max_group_height = Math.max.apply(Math, heights[gix][t]);
if(typeof(v.img.attr("data-fixed"))=="undefined"){
v.img.css({"height":max_group_height+(v.padding)+"px"}).attr("data-height",0).attr("data-width",0).attr("data-fixed",1);
}
});
}
});
});
do_swap_images();
}
if you checking dom image node, you need isSameNode functions. I am not sure about your requirements, hope below code will helps
//suppose a, b are your objects
var key = 241
var diff = a[key].filter( function( v ){
var firstImgNode = v.img;
return !b[key].some( function( v ){
return v.img.isSameNode( firstImgNode );
});
});
or if you checking other data type, then simply do v.img == firstImgNode

JSON tree to parent-link structure

I have a JSON tree structure:
nodes =
[
{
"name": "user1",
"children": [
{
"name": "user2"
},
{
"name": "user3",
"children": [
{
"name": "user4"
}
]
},
{
"name": "user5"
}
]
}
]
that I would like to convert to a parent-link structure:
[{"name": "user1","parent": "null"},
{"name": "user2","parent": "user1"},
{"name": "user3","parent": "user1"},
{"name": "user4","parent": "user3"},
{"name": "user5","parent": "user1"}]
I have tried to traverse the tree recursively but without success accessing the parent object:
rebuild(nodes,parentLink);
function parentlink(key,value) {
var obj = { name: value , parent: ??? };
if (key == "name"){
nodes.push(obj);
}
}
function rebuild(o,func) {
for (i in o) {
func.apply(this,[i,o[i]])
if (typeof(o[i])=="object") {
traverse(o[i],func,nodes);
}
}
}
In developer tools I can see the parent objects for each child, but I don't know how to access them. What should I do to add the parent to each user?
I'm not gonna lie, I didn't bother looking at your code - this is how I would do it:
http://jsfiddle.net/J6G2W/1/
function processChildren(item, ret, parent) {
for (var i = 0; i < item.length; i++) {
var cur = item[i];
var cur_name = cur.name;
ret.push({"user": cur_name, "parent": parent});
if ("children" in cur && cur.children.length > 0) {
processChildren(cur.children, ret, cur_name);
}
}
}
var all = [];
processChildren(nodes, all, null);
console.log(JSON.stringify(all));
Output is:
[{"user":"user1","parent":null},{"user":"user2","parent":"user1"},{"user":"user3","parent":"user1"},{"user":"user4","parent":"user3"},{"user":"user5","parent":"user1"}]
Which seems to be what you're looking for. You're welcome to modify what of my code to work more like yours, I just thought I'd share what I'd do :)
UPDATE:
If, for some reason, you want to make it more extendable, you can customize which keys are the "name" and which are the "children"...for example:
http://jsfiddle.net/J6G2W/2/
function startProcess(item, ret, key_look, children_look, parent) {
function processChildren(item2, ret2, parent2) {
for (var i = 0; i < item2.length; i++) {
var cur = item2[i];
var cur_name = key_look in cur ? cur[key_look] : null;
ret.push({"user": cur_name, "parent": parent2});
if (children_look in cur && cur[children_look].length > 0) {
processChildren(cur[children_look], ret, cur_name);
}
}
}
processChildren(item, ret, parent);
}
var all = [];
startProcess(nodes, all, "name", "children", null);
console.log(JSON.stringify(all));
Notice how you only have to specify the key_look, children_look arguments once. The inner function can access those parameters while only passing the important things each recursion. This probably isn't important, I just wanted to figure it out :)

Categories