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 :)
Related
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));
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!!!
I have this json file:
var data = [{
"id": 0,
"parentId": null,
"name": "Comapny",
"children": [
{
"id": 1235,
"parentId": 0,
"name": "Experiences",
"children": [
{
"id": 3333,
"parentId": 154,
"name": "Lifestyle",
"children": []
},
{
"id": 319291392,
"parentId": 318767104,
"name": "Other Experiences",
"children": []
}
]
}
]
}];
I need to find object by id. For example if need to find an object with id:319291392, I have to get:
{"id": 319291392,"parentId": 318767104,"name": "Other Experiences","children": []}
How can I do that?
I tried to use this function:
function findId(obj, id) {
if (obj.id == id) {
return obj;
}
if (obj.children) {
for (var i = 0; i < obj.children.length; i++) {
var found = findId(obj.children[i], id);
if (found) {
return found;
}
}
}
return false;
}
But it doesn't work as it's an array of objects.
If your starting point is an array, you want to invert your logic a bit, starting with the array rather than with the object:
function findId(array, id) {
var i, found, obj;
for (i = 0; i < array.length; ++i) {
obj = array[i];
if (obj.id == id) {
return obj;
}
if (obj.children) {
found = findId(obj.children, id);
if (found) {
return found;
}
}
}
return false; // <= You might consider null or undefined here
}
Then
var result = findId(data, 319291392);
...finds the object with id 319291392.
Live Example
This should work for you:-
var serachById = function (id,data) {
for (var i = 0; i < data.length; i++) {
if(id==data[i].id)
return data[i];
if(data[i].children.length>0)
return serachById(id,data[i].children);
};
return null;
}
console.log(serachById(0,data));
Here is another simple solution using object notation.
This solution will work even if you decide to get rid of teh array and use object notation later on. so the code will remain the same.
It will also support the case when you have element with no children.
function findId(obj, id) {
var current, index, reply;
// Use the object notation instead of index.
for (index in obj) {
current = obj[index];
if (current.id === id) {
return current;
}
reply = findId(current.children, id);
if (reply) {
return reply;
}
// If you reached this point nothing was found.
console.log('No match found');
}
}
console.log(findId(data, 319291392));
do it so:
for (var obj in arr) {
if(arr[obj].id== id) {
console.log(arr[obj]);
}
}
I have a JSON data structure as shown below:
{
"name": "World",
"children": [
{ "name": "US",
"children": [
{ "name": "CA" },
{ "name": "NJ" }
]
},
{ "name": "INDIA",
"children": [
{ "name": "OR" },
{ "name": "TN" },
{ "name": "AP" }
]
}
]
};
I need to change the key names from "name" & "children" to say "key" & "value". Any suggestion on how to do that for each key name in this nested structure?
I don't know why you have a semicolon at the end of your JSON markup (assuming that's what you've represented in the question), but if that's removed, then you can use a reviver function to make modifications while parsing the data.
var parsed = JSON.parse(myJSONData, function(k, v) {
if (k === "name")
this.key = v;
else if (k === "children")
this.value = v;
else
return v;
});
DEMO: http://jsfiddle.net/BeSad/
Try this:
function convert(data){
return {
key: data.name,
value: data.children.map(convert);
};
}
Or if you need to support older browsers without map:
function convert(data){
var children = [];
for (var i = 0, len = data.children.length; i < len; i++){
children.push(convert(data.children[i]));
}
return {
key: data.name,
value: children
};
}
You could use a function like this :
function clonerename(source) {
if (Object.prototype.toString.call(source) === '[object Array]') {
var clone = [];
for (var i=0; i<source.length; i++) {
clone[i] = goclone(source[i]);
}
return clone;
} else if (typeof(source)=="object") {
var clone = {};
for (var prop in source) {
if (source.hasOwnProperty(prop)) {
var newPropName = prop;
if (prop=='name') newPropName='key';
else if (prop=='children') newPropName='value';
clone[newPropName] = clonerename(source[prop]);
}
}
return clone;
} else {
return source;
}
}
var B = clonerename(A);
Note that what you have isn't a JSON data structure (this doesn't exist as JSON is a data-exchange format) but probably an object you got from a JSON string.
I have a nested array like this:
array = [
{
"id": "67",
"sub": [
{
"id": "663",
},
{
"id": "435",
}
]
},
{
"id": "546",
"sub": [
{
"id": "23",
"sub": [
{
"id": "4",
}
]
},
{
"id": "71"
}
]
}
]
I need to find 1 nested object by its id and get all its parents, producing an array of ids.
find.array("71")
=> ["546", "71"]
find.array("4")
=> ["546", "23", "4"]
What's the cleanest way to do this? Thanks.
Recursively:
function find(array, id) {
if (typeof array != 'undefined') {
for (var i = 0; i < array.length; i++) {
if (array[i].id == id) return [id];
var a = find(array[i].sub, id);
if (a != null) {
a.unshift(array[i].id);
return a;
}
}
}
return null;
}
Usage:
var result = find(array, 4);
Demo: http://jsfiddle.net/Guffa/VBJqf/
Perhaps this - jsonselect.org.
EDIT: I've just had a play with JSONSelect and I don't think it's appropriate for your needs, as JSON does not have an intrinsic 'parent' property like xml.
It can find the object with the matching id, but you can't navigate upwards from that. E.g.
JSONSelect.match(':has(:root > .id:val("4"))', array)
returns me:
[Object { id="4"}]
which is good, it's just that I can't go anywhere from there!