I've been trying to figure out a way of doing this. This is my object:
{
"id":1,
"name":"Blooper Corp.",
"emoji":"π",
"parent_id":null,
"children":[
{
"id":2,
"name":"Food",
"emoji":"π₯©",
"parent_id":1,
"children":[
]
},
{
"id":3,
"name":"Canine Therapy",
"emoji":"π",
"parent_id":1,
"children":[
{
"id":4,
"name":"Massages",
"emoji":"π",
"parent_id":3,
"children":[
]
},
{
"id":5,
"name":"Games",
"emoji":"πΎ",
"parent_id":3,
"children":[
]
}
]
}
]
}
I'm trying to get the last id of the array so I can use it in the new child array and add it as a child to that specific tier, for example:
{
"id":6, // New id
"name":"Music",
"emoji":"",
"parent_id":4, //this will be the parent id it belongs to
"children":[
]
}
This is my javascript button function:
function askUserForTeamDetails( team ) {
const emoji = prompt( 'Enter new teamβs emoji:' );
if ( null === emoji ) {
return;
}
const name = prompt( 'Enter new teamβs name:' );
if ( null === name ) {
return;
}
let tree = getTree();
tree.id = {}; //new child array to push depending on parent id
return { name, emoji };
}
getTree():
const tree = {"id":1,"name":"Rusty Corp.","emoji":"π","parent_id":null,"children":[{"id":2,"name":"Food","emoji":"π₯©","parent_id":1,"children":[]},{"id":3,"name":"Canine Therapy","emoji":"π","parent_id":1,"children":[{"id":4,"name":"Massages","emoji":"π","parent_id":3,"children":[]},{"id":5,"name":"Games","emoji":"πΎ","parent_id":3,"children":[]}]}]};
return tree;
I've tried using Object.keys(tree)[Object.keys(tree).length-1]; But I don't see how this will work since it's multidimensional.
I hope someone can advise a way of doing this.
Thanks
If you want to add new object based on parent_id property that can be on any level, you can create recursive function that will traverse the object with for...in loop and push the object to children if the id matches.
const data = {"id":1,"name":"Blooper Corp.","emoji":"π","parent_id":null,"children":[{"id":2,"name":"Food","emoji":"π₯©","parent_id":1,"children":[]},{"id":3,"name":"Canine Therapy","emoji":"π","parent_id":1,"children":[{"id":4,"name":"Massages","emoji":"π","parent_id":3,"children":[]},{"id":5,"name":"Games","emoji":"πΎ","parent_id":3,"children":[]}]}]}
function insert(data, obj, pid = null) {
if (pid == obj.parent_id) {
data.children.push(obj);
return;
}
if (data.children) {
data.children.forEach(e => insert(e, obj, e.id))
}
}
function getLastId(data) {
let result = null;
function search(data) {
if (!result || data.id > result) result = data.id;
if (data.children) data.children.forEach(search);
}
search(data);
return result;
}
const newObj = {
"id": getLastId(data) + 1,
"name": "Music",
"emoji": "",
"parent_id": 4,
"children": []
}
insert(data, newObj);
console.log(data)
You could write a recursive function that would loop over each of the children until the id was found. One way of doing this could be:
function findTeamById(id, tree) {
if (id === tree.id) return tree //returning tree as it is the current team's object
for (let i = 0; i < tree.length; i++) {
let teamTree = findTeamById(id, tree[i])
if (teamTree !== false) {
// return the found tree
return teamTree
}
}
// if no tree was found return false
return false
}
// EDIT: forgot to show how to add the child
const newTeam = {
"id":6, // New id
"name":"Music",
"emoji":"",
"parent_id":4, //this will be the parent id it belongs to
"children":[
]
}
// this will push the new team to the childrens array of the newTeams parent
findTeamById(newTeam.parent_id, myTree).children.push(newTeam)
It seems like you'll need a recursive function to find the max ID, then get the parent of that maxID object and put the new object in its children.
const tree = {"id":1,"name":"Rusty Corp.","emoji":"π","parent_id":null,"children":[{"id":2,"name":"Food","emoji":"π₯©","parent_id":1,"children":[]},{"id":3,"name":"Canine Therapy","emoji":"π","parent_id":1,"children":[{"id":4,"name":"Massages","emoji":"π","parent_id":3,"children":[]},{"id":5,"name":"Games","emoji":"πΎ","parent_id":3,"children":[]}]}]};
const newObj = {
"id": 6,
"name": "Music",
"emoji": "",
"parent_id": 0,
"children": []
}
var maxID=0;
var parentObj = null;
function findMaxIDArray(obj) {
var tmpid = maxID;
maxID = Math.max(obj.id, maxID);
if (maxID>tmpid) {
parentObj = obj;
}
if (obj.children) {
for(let c=0;c<obj.children.length;c++){
if (obj.children[c]) {
findMaxIDArray(obj.children[c]);
}
}
}
}
findMaxIDArray(tree);
if (parentObj) {
newObj.parent_id=parentObj.id;
parentObj.children.push(newObj);
}
console.log(tree);
Related
I am working on a solution where I have a deep array of parent having child elements
Here is how the array look like
[
{
"id": "1",
"Name": "John Doe",
"children":
[
{
"id": "1.1",
"name": "John doe 1.1"
},
{
"id:": "1.2",
"name:": "John doe 1.2"
},
{
"id": "1.3",
"name": "John doe 1.3",
"children":
[
{
"id": "1.3.1",
"name": "John doe 1.3.1"
}
]
}
]
},
{
"id": "2",
"Name": "Apple",
"children":
[
{
"id": "2.1",
"name": "Apple 2.1"
},
{
"id:": "1.2",
"name:": "Apple 1.2"
}
]
}
]
basically, I have a functionality where I have a table whenever the user clicks on a row I want to add children related to that row,
For example, whenever I click on the row with id 1, I call click function by passing row as an argument, find an index for row and append children under that along with maintaining state, my solution works only for one level nested child, suppose if I want to add children property under children it's not working
Here is the function that I wrote
const expandRow = (row) => {
const index = _(this.state.data)
.thru(function(coll) {
return _.union(coll, _.map(coll, 'children') || []);
})
.flattenDeep()
.findIndex({ id: row.id });
console.log(index)
if (index !== -1) {
let prevState = [...this.state.data];
let el = _(prevState)
.thru(function(coll) {
return _.union(coll, _.map(coll, 'children') || []);
})
.flattenDeep()
.find({ id: row.id });
console.log(el)
el.children = [
{ id: '_' + Math.random().toString(36).substr(2, 5), name: "sfsdfds1", isExpanded:false,parentId:row.id },
{ id: '_' + Math.random().toString(36).substr(2, 5), name: "sfsdfds2",isExpanded:false,parentId:row.id },
];
this.setState({data:[...this.state.data],prevState},()=>{console.log(this.state.data)})
}
updateState(row.id, { isExpanded: true });
};
I also want to maintain state along with it so whenever the user adds a new row my component re-render.
You need recursive function for this.below is the code I write in VueJs for parent child deep array. please take a look hope it's provide you some idea.
and one more thing my data structure is same as your.
let treeData= {
id:1,
type: 0,
status: 0,
parent_id:0,
children: [{
id:1,
type: 0,
status: 0,
parent_id:1,
children:[
{
id:1,
type: 0,
status: 0,
parent_id:1,
}
]
}],
}
ChangeCheckStatus(treedata, item, status) {
for (let i = 0; i < treedata.length; i++) {
if (treedata[i].id === item.id) {
treedata[i].selectAll = status;
return;
}
this.ChangeCheckStatus(treedata[i].children, item, status);
}
}
makeTreeViewThroughCsvData(csvData) {
const data = this.csvToJSON(csvData)
this.rows_new = [...this.rows_new, ...data];
this.rows_new.forEach((_data) => {
let newNode = {}
for (const key in _data) {
if (_data.hasOwnProperty(key)) {
newNode[key.trim()] = _data[key]
}
}
newNode['children'] = []
newNode['status'] = _data.status
/* eslint-disable */
newNode = rest
//variable hold new tree data
this.treeData.push(newNode)
})
this.generateFinalTreeData();
},
generateFinalTreeData() {
const root = []
const nodeIds = []
const mapping = {}
this.treeData.forEach(node => {
// No parentId means Node
if (node.parent_id != undefined) {
//increment NODE ID only when parent_is is not undefined
nodeIds.push(node.id)
}
if (node.parent_id == 0 || node.parent_id == 1) return root.push(node);
// Insert node as child of parent
let parentKey = mapping[node.parent_id];
if (typeof parentKey !== "number") {
parentKey = this.treeData.findIndex(el => el.id === node.parent_id);
mapping[node.parent_id] = parentKey;
}
if (!this.treeData[parentKey].children) {
return this.treeData[parentKey].children = [node];
}
this.treeData[parentKey].children.push(node);
});
this.finalTreeData = root
//vuex commit statement == Redux dispach
this.$store.commit('setTreeViewData', root);
this.$store.commit('setMaxNodeId', Math.max(...nodeIds) + 1);
}
I have an object like the following :
[
{
"uid": "aaa-aaa",
"name": "foo",
"children": []
},
{
"uid": "aaa-bbb",
"name": "bar",
"children": [
{
"uid": "aaa-bbc",
"name": "baz",
"children": []
},
{
"uid": "aaa-ccc",
"name": "fooz",
"children": [
{
"uid": "aaa-bcb",
"name": "Yeah !",
"children": []
}
]
}
]
}
]
I am trying to write a function that would take that object an uid as parameters and would return a path to the element with the uid in that object (or null if it's not found).
Something like this :
> getElementPath(bigObject, 'aaa-bcb')
[1, "children", 1, "children", 0]
or
> getElementPath(bigObject, 'aaa-bcb')
[1, 1, 0]
I know the function has to be recursive since there should be no limit in nesting levels. I have tried this but it always returns null :
function getElementPath (haystack, uid, currentPath = []) {
if (haystack.uid === uid) {
return currentPath
}
if (Array.isArray(haystack.children)) {
for (let i = 0; i < haystack.children.length; i++) {
let newPath = [...currentPath, i]
let path = getElementPath(haystack.children[i], uid, newPath)
if (path !== null) {
return path
}
}
}
return null
}
I'd use flat
Flatten the object and then loop over the Object keys until you find the one that has the appropriate value. Once you find it, the key is the path.
https://www.npmjs.com/package/flat
My (naive and quick) implementation would look like this. But what I don't love about it is that it knows to look at the "children" property, it's fine if you're data structure is well defined and doesn't change very often, the flat idea will work no matter if you change your data structure or not.
getPathForUid = (uid,obj,thisPath = []) => {
if(Array.isArray(obj)) {
return obj.reduce((acc,item,idx) => getPathForUid(uid,item,thisPath.concat(idx)),[]);
}
return obj.uid === uid ? thisPath : getPathForUid(uid,obj.children,thisPath.concat('children'));
}
Try this:
function getObject(listaJson, uid) {
var object = null,
param,
type = null;
if (listaJson.uid === uid) {
return listaJson;
}
for (param in listaJson) {
type = typeof(listaJson[param]);
if (type.toString().toLowerCase() === 'object') {
object = getObject(listaJson[param], uid);
}
if (object) {
return object;
}
}
return object;
}
console.log(getObject(json, 'aaa-aaa'));
console.log(getObject(json, 'aaa-bbb'));
console.log(getObject(json, 'aaa-bbc'));
console.log(getObject(json, 'aaa-ccc'));
console.log(getObject(json, 'aaa-bcb'));
console.log(getObject(json, 'aaa-xxx')); // null
console.log(getObject(json, 'yyy-jjj')); // null
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 the following valid JSON. It describes a tree structure:
{
"items": [
{
"id": "d1"
},
{
"id": "2",
"children": [
{
"id": "3"
},
{
"id": "4"
},
{
"id": "5",
"children": [
{
"id": "6"
},
{
"id": "7",
"children": [
{
"id": "8"
},
{
"id": "9"
}
]
},
{
"id": "10"
}
]
},
{
"id": "11"
},
{
"id": "12"
}
]
},
{
"id": "13"
},
{
"id": "14"
}
]
}
I need to be able to get any of the "items" by id and any of the child items. For example. Initially I tried grep:
var returnedData = $.grep(obj.items, function(element, index){return element.id == "2";
});
This worked great for item with id==2 but fails completely when I try to obtain element.id=="7"
Any assistance would be appreciated. Thanks in advance.
You can make a recursive function to search in the data:
function find(source, id)
{
for (key in source)
{
var item = source[key];
if (item.id == id)
return item;
// Item not returned yet. Search its children by recursive call.
if (item.children)
{
var subresult = find(item.children, id);
// If the item was found in the subchildren, return it.
if (subresult)
return subresult;
}
}
// Nothing found yet? return null.
return null;
}
// In the root object, the array of items is called 'items', so we pass in
// data.items to look into. The root object itself doesn't seem to have an id anyway.
var result = find(data.items, 7);
// Show the name of item 7, if it had one...
alert(result.name);
Demo: http://jsfiddle.net/rj26H/
In this function I just looped over the object, so its a bit more verbose. You could probably also use $.grep to do the searching and make the code a bit smaller. Anyway, the trick is to search all children if the item is not found on the main level. Apparently grep doesn't work in a recursive fashion.
Try this:
var id = 7;
var data = {"items": [{"id": "d1"},{"id": "2","children": [{"id": "3"},{"id": "7"},{"id": "11"},{"id": "12"}]}]};
function search(values) {
$.each(values, function(i, v) {
if (v.id == id) {
console.log('found', v);
return false;
}
if (v.children) {
search(v.children);
}
});
}
search(data.items);
Demo Link
I know this have been already answered, but I wanted to show how you could leverage the new the new JavaScript 1.7 features to solve this. Please note that the same approach could have been used without support for generators, but the code would have been longer.
//Returns an iterator that knows how to walk a tree
function treeIterator(root, childGetter, childCountGetter) {
let stack = [root], node;
while (node = stack.pop()) {
yield node;
for (let i = childCountGetter(node); i--;) stack.push(childGetter(node, i));
}
}
//Our custom search function
function findNodeById(tree, id) {
let it = treeIterator(tree,
function (node, i) { return node.children[i]; },
function (node) { return node.children? node.children.length : 0; }
);
for (let node in it) if (node.id === id) return node;
return null;
}
var tree = {
id: 'root',
children: [
{ id: 'a' },
{
id: 'b',
children: [
{ id: 'b1' },
{ id: 'b2' }
]
},
{ id: 'c' }
]
};
findNodeById(tree, 'b1'); //Object { id="b1"}
Note that you can also set the __iterator__ on the data structure so that functions that needs to iterate over this data structure do not have to know implementation details.
tree.__iterator__ = treeIterator.bind(null, tree,
function (node, i) { return node.children[i]; },
function (node) { return node.children? node.children.length : 0; }
);
Then the findNodeById function can be:
function findNodeById(tree, id) {
for (let node in it) if (node.id === id) return node;
return null;
}
I wish to filter a nested javascript object by the value of the "step" key:
var data = {
"name": "Root",
"step": 1,
"id": "0.0",
"children": [
{
"name": "first level child 1",
"id": "0.1",
"step":2,
"children": [
{
"name": "second level child 1",
"id": "0.1.1",
"step": 3,
"children": [
{
"name": "third level child 1",
"id": "0.1.1.1",
"step": 4,
"children": []},
{
"name": "third level child 2",
"id": "0.1.1.2",
"step": 5,
"children": []}
]},
]}
]
};
var subdata = data.children.filter(function (d) {
return (d.step <= 2)});
This just returns the unmodified nested object, even if I put value of filter to 1.
does .filter work on nested objects or do I need to roll my own function here, advise and correct code appreciated.
cjm
Recursive filter functions are fairly easy to create. This is an example, which strips a JS object of all items defined ["depth","x","x0","y","y0","parent","size"]:
function filter(data) {
for(var i in data){
if(["depth","x","x0","y","y0","parent","size"].indexOf(i) != -1){
delete data[i];
} else if (i === "children") {
for (var j in data.children) {
data.children[j] = filter(data.children[j])
}
}
}
return data;
}
If you would like to filter by something else, just updated the 2nd line with your filter function of choice.
Here's the function to filter nested arrays:
const filter = arr => condition => {
const res = [];
for (const item of arr) {
if (condition(item)) {
if (!item.children) {
res.push({ ...item });
} else {
const children = filter(item.children)(condition);
res.push({ ...item, children })
}
}
}
return res;
}
The only thing you have to do is to wrap your root object into an array to reach self-similarity. In common, your input array should look like this:
data = [
{ <...>, children: [
{ <...>, children: [...] },
...
] },
...
]
where <...> stands for some properties (in your case those are "name", "step" and "id"), and "children" is an optional service property.
Now you can pass your wrapped object into the filter function alongside a condition callback:
filter(data)(item => item.step <= 2)
and you'll get your structure filtered.
Here are a few more functions to deal with such structures I've just coded for fun:
const map = arr => f => {
const res = [];
for (const item of arr) {
if (!item.children) {
res.push({ ...f({ ...item }) });
} else {
res.push({ ...f({ ...item }), children: map(item.children)(f) });
}
}
return res;
}
const reduce = arr => g => init => {
if (!arr) return undefined;
let res = init;
for (const item of arr) {
if (!item.children) {
res = g(res)({ ...item });
} else {
res = g(res)({ ...item });
res = reduce(item.children)(g)(res);
}
}
return res;
}
Usage examples:
map(data)(item => ({ step: item.step }))
reduce(data)($ => item => $ + item.step)(0)
Likely, the code samples aren't ideal but probably could push someone to the right direction.
Yes, filter works on one array (list), like the children of one node. You have got a tree, if you want to search the whole tree you will need to use a tree traversal algorithm or you first put all nodes into an array which you can filter. I'm sure you can write the code yourself.