How to add or update items in this multidimensional JSON? - javascript

Let's say we have some houses represented as JSON. Something like this:
[
{
"id": "1",
"code": "1",
"name": "Smith's",
"children": [
{
"id": "",
"code": "11",
"name": "Kitchen",
"children": [
{
"id": "",
"code": "111",
"name": "Sink",
"children": []
}
]
},
{
"id": "",
"code": "12",
"name": "Living Room",
"children": [
{
"id": "",
"code": "121",
"name": "Television",
"children": [
{
"id": "",
"code": "1211",
"name": "Panel buttons",
"children": [
{
"id": "",
"code": "12111",
"name": "Power button",
"children": []
},
{
"id": "",
"code": "12112",
"name": "Colors adjust button",
"children": []
}
]
},
{
"id": "",
"code": "1221",
"name": "Screen",
"children": []
}
]
}
]
}
]
},
{
"id": "2",
"code": "2",
"name": "Taylor's",
"children": [
// Here goes all house places and items like the example above
]
},
{
"id": "1",
"code": "1",
"name": "Wilson's",
"children": [
// Here goes all house places and items like the example above
]
}
]
Take notice that the "code" property, found in each item, is something to represent the "path" until that item, carrying its parents "code" property concatenated with its own position by incremental order. So the code "11" means house 1 and child 1. And 212 would be house 2, child 1, child 2. Also take notice that all items follow the same type. In other words, every item has a children that follows its own type. So, it could be infinite.
Now, I'd like to maintain these structure. Adding items, updating items and so on. Let's say we want to add a carpet in Smith's living room. We would go deep in the structure 2 levels, which are Smith's house (index 0 of the array) and living room (index 1 of the children array). And then add a carpet.
The problem is it won't be 2 levels in all cases. What if I wanted to add a bathroom? It would be level 1, alongside with kitchen in living room (the first children). What if I'd like to add a microwave in the kitchen and add to it buttons, display, etc?
I think I'm a recursive scenario where I have to visit all items and, if it is the one I'm looking to reach at, add/updated it.
I've tried following this example
I couldn't figure it out how to bring it to my case. though.
I appreciate if your contribution is in JavaScript, but feel free to represent it in other language in case you are better in other language =).

There are indeed some questions, like for instance what happens if you have more than 10 items as child and why do you need it?
And what happens if you remove any item on any level? will you recursively start updating all codes?
Nevertheless I gave it a go. In essence what I do in the code is first search for the parent (example: Kitchen) where you want to add it to and then add the new child item (example: Carpet) to it.
The search is a typical recursive search.
The child addition is a typical addition to an array.
For argument's sake I assumed that the fields code always exist and that children is always an array.
// Actual code is underneath the declaration of this array
let houseList = [
{
"id": "1",
"code": "1",
"name": "Smith's",
"children": [
{
"id": "",
"code": "11",
"name": "Kitchen",
"children": [
{
"id": "",
"code": "111",
"name": "Sink",
"children": []
}
]
},
{
"id": "",
"code": "12",
"name": "Living Room",
"children": [
{
"id": "",
"code": "121",
"name": "Television",
"children": [
{
"id": "",
"code": "1211",
"name": "Panel buttons",
"children": [
{
"id": "",
"code": "12111",
"name": "Power button",
"children": []
},
{
"id": "",
"code": "12112",
"name": "Colors adjust button",
"children": []
}
]
},
{
"id": "",
"code": "1221",
"name": "Screen",
"children": []
}
]
}
]
}
]
},
{
"id": "2",
"code": "2",
"name": "Taylor's",
"children": [
// Here goes all house places and items like the example above
]
},
{
"id": "1",
"code": "1",
"name": "Wilson's",
"children": [
// Here goes all house places and items like the example above
]
}
]
addChild(houseList,"11",{name:"Carpet" });
addChild(houseList,"1211",{name: "Volume Up Button"});
addChild(houseList,"1211",{name: "Volume Down Button"});
console.log('new houselist', houseList);
// child is just what you want to add and the parentCode refers to where you want to add it to
function addChild(houseList, parentCode, child) {
let parent = findInHouseList(houseList,parentCode,child);
let amountOfChildren = parent.children.length;
let newCodeName = parentCode +""+ (amountOfChildren+1);
child = {...{id: "", code: newCodeName, children: []}, ...child};
console.log('adding child ', child);
parent.children = [...parent.children, child];
}
function findInHouseList(houseList,code) {
for (let house of houseList) {
let foundElement = findElement(house,code);
if ( foundElement)
return foundElement;
}
}
function findElement(currentElement, code) {
if ( currentElement.code === code)
return currentElement;
if (currentElement.children?.length > 0)
{
for (let child of currentElement.children) {
let foundElement = findElement(child,code);
if ( foundElement)
return foundElement;
}
}
return null;
}
I decided to let the code manage the code names for new children. It seems the easiest.

What you're trying to do is updating a JSON value at a dynamic path.
This function will append a child to the item which holds the specified code.
You may add conditions to check if the item at the code is defined
function appendChild(houses, code, item) {
let path = code.split('')
let o = houses
for (let i = 0; i < path.length; i++) {
let n = path[i] - 1
o = o[n]["children"]
}
o.push(item)
return houses
}
However, you should start your code indexes at 0 and storing them inside the JSON is useless since they are simply the path to reach the item.

Related

Concatenating child array to parent

Right now I have an array of object with children that also have an array of objects.
[
{
"code": "mock-code",
"name": "mock-name",
"children": [
{
"code": "mock-child-code",
"name": "mock-child-name",
},
{
"code": "mock-child-code",
"name": "mock-child-name",
},
],
},
{
"code": "mock-code",
"name": "mock-name",
"children": [],
},
{
"code": "mock-code",
"name": "mock-name",
"children": [
{
"code": "mock-code",
"name": "mock-name",
}
],
}
]
I want to extract the children array and concat them to the parent array like below.
[
{
"code": "m1",
"name": "mock-name",
"children": [
{
"code": "mc-1",
"name": "mn-1",
},
{
"code": "mc-2",
"name": "mn-2",
},
],
},
{
"code": "m2",
"name": "mock-name",
"children": [],
},
{
"code": "mm3",
"name": "mock-name",
"children": [
{
"code": "mc-3",
"name": "mn-3",
}
],
}
{
"code": "mc-1",
"name": "mn-1",
},
{
"code": "mc-2",
"name": "mn-2",
},
{
"code": "mc-3",
"name": "mn-3",
}
]
What are someways to do this. I'm currently looping though the child array creating a new array checking if it's not empty. It all seems a bit messy. Is there a clean way to do this?
let fullList = New Array()
parentData.forEach(element => {
if (!!element.children.length) {
fullList.push(element.children);
}
});
return parentData.concat(fullList);
This isn't giving me the desired results since it's adding another array to the parent object but this is where I am at.
const newArray = originalArray.flatMap(element => [element, ...element.children])
This should do it, and as a bonus will preserve the order (parent1, parent1's children, parent2, parent2's children etc.)
Of course, this works if you have only one level of nesting. If you have greater depth level, that would be a bit more complex, probably using Array.prototype.reduce().

Remove item from nested array object in Javascript by key value

I have object like this:
{
"id": 1,
"name": "first",
"sections": [
{
"id": 1,
"title": "First section",
"contents": [
{
"id": "123",
"title": "Sample title 1",
"description": "<html>code</html>",
},
{
"id": "124",
"title": "Sample title 2",
"description": "<html>code</html>"
},
{
"id": "125",
"title": "Some other sample",
"description": "<html>code</html>"
}
]
},
{
"id": 2,
"title": "Second section",
"contents": [
{
"id": "126",
"title": "Sample title 126",
"description": "<html>code</html>"
},
{
"id": "127",
"title": "Sample title 127",
"description": "<html>code</html>"
}
]
}
]
}
I want to remove specific object from contents array by its id (all those ids are unique).
I can easily find element I want to remove, but I'm unable to find in which section this element is to splice it later.
obj.sections.forEach(function(section) {
section.contents.forEach((content) => {
if (content.id == 125) {
console.log(content)
console.log(section)
}
})
})
In above code console.log(sections) returns undefined. How can I get position in sections array which contains contents array that has specific id. For example, id: 125 would return sections position 0, so I can use splice to remove that element.
If my approach is completely wrong please point me in right direction, thanks :)
You could use .filter() instead of .splice(). .filter() will keep all items which you return true for and discard of those which you return false for. So, if the current section's content's object has an id equal to the one you want to remove you can return false to remove it, otherwise return true to keep that item. You can use this with .map() to map each section object to a new one with an updated contents array:
const obj = { "id": 1, "name": "first", "sections": [ { "id": 1, "title": "First section", "contents": [ { "id": "123", "title": "Sample title 1", "description": "<html>code</html>", }, { "id": "124", "title": "Sample title 2", "description": "<html>code</html>" }, { "id": "125", "title": "Some other sample", "description": "<html>code</html>" } ] }, { "id": 2, "title": "Second section", "contents": [ { "id": "126", "title": "Sample title 126", "description": "<html>code</html>" }, { "id": "127", "title": "Sample title 127", "description": "<html>code</html>" } ] } ] };
const idToRemove = 125;
obj.sections = obj.sections.map(
sec => ({...sec, contents: sec.contents.filter(({id}) => id != idToRemove)})
);
console.log(obj);
.as-console-wrapper { max-height: 100% !important; top: 0; } /* ignore */
Why not use a simple filter for accomplishing the task. Also, it will be much cleaner and immutable way of doing it.
let newArrWithoutContentWithGivenId = obj.sections.map(section =>
({...section, contents: section.contents.filter(content =>
content.id != 125)}));
Here, we are mapping each section whose content does not contain the ID 125. In short, section > content.id != 125 will be removed from the new array.
Hope it helps :)
Note: Code is not tested, it is just to help you find a way to do it cleanly.
You only need to use the second argument of forEach :)
obj.sections.forEach(function(section, sectionIndex) {
section.contents.forEach((content, contentIndex) => {
if (content.id == 125) {
// use the sectionIndex and contentIndex to remove
}
})
})

javascript reference to other variable

I'm trying to create an array of objects where one of the properties of each object is a list of a few other objects from the same array. Similar to a hierarchy, each object has parent objects and children objects. I'm wondering what is the most efficient way of doing this. The first thought I had was to simply create an array of the unique IDs. So:
var network = [
{
"id": "ABCD",
"description": "some info",
"parents": [""], //no parents
"childern": ["EFG"]
},
{
"id": "EFG",
"description": "more info",
"parents": ["ABCD"],
"children": ["XYZ"]
},
{
"id": "LMNOP",
"description": "something",
"parents": ["ABCD"],
"children": ["XYZ"]
},
{
"id": "XYZ",
"description": "something",
"parents": ["EFG", "LMNOP"],
"children": [] //no child nodes
}
My concern with this approach is that everytime I want to act on a 'parent' or 'child', I only have a string of the name. I would need to search the array to match the string to whatever object has that ID. So while I can get the description of my current object with network[2].description, I can't do something like network[2].children[0].description. If the array contains hundreds or even a couple thousand objects and I'm continually searching it I'm worried about performance.
The second approach I've considered is storing the index of the parent and children nodes instead of their unique IDs. Then I could access a parent or child node with something like network[network[1].children[0]]. But in this case I'm relying on the order of objects to remain static. I plan to pull down the data from a noSQL database at the start of each user session, so I don't think I can rely on the order to remain unchanged.
Is there another approach? As I think about C++ I would probably be able to store a pointer, but I don't think I can do that in JS.
I suggest you store your objects in an object, not an array. You can then use the id as the key (aka pointer) to look up the object:
var network = {
"ABCD": {
"id": "ABCD",
"description": "some info",
"parents": [""], //no parents
"children": ["EFG"]
},
"EFG": {
"id": "EFG",
"description": "more info",
"parents": ["ABCD"],
"children": ["XYZ"]
},
"LMNOP": {
"id": "LMNOP",
"description": "something",
"parents": ["ABCD"],
"children": ["XYZ"]
},
"XYZ": {
"id": "XYZ",
"description": "something",
"parents": ["EFG", "LMNOP"],
"children": [] //no child nodes
}
}
// access a parent like this
let parent0 = network["XYZ"]["parents"][0];
console.log(parent0);
// get an array of your node IDs like this
let nodeIds = Object.keys(network);
console.log(nodeIds);
// can still look up by position then
console.log(network[nodeIds[0]]);
Of course you can do it using references to your objects
var a = {
"id": "ABCD",
"description": "some info",
"parents": [], //no parents
"children": []
};
var b = {
"id": "EFG",
"description": "more info",
"parents": [],
"children": []
};
var c = {
"id": "LMNOP",
"description": "something",
"parents": [],
"children": []
};
var d = {
"id": "XYZ",
"description": "something",
"parents": [],
"children": [] //no child nodes
};
a.children.push(b);
b.parents.push(a);
//and so on.
var network = [a,b,c,d];

Trying to connect D3 Nodes from "flat" JSON to make company heirarchy graph

I am trying to create a company hierarchy graph using D3..using the code from this example:
http://bl.ocks.org/mbostock/2949981
Is there a way to create that graph from this json:
links = [
{
"Source": "1",
"Target": "2",
},
{
"Source": "1",
"Target" : "3"
},
{
"Source": "2",
"Target": "4",
},
{
"Source": "2",
"Target": "5",
},
{
"Source" : "3",
"Target" " "6"
}
]
nodes = [
{
"Id": "1",
"Name": "TEST NODE ONE",
"Url": "http://www.google.com"
},
{
"Id": "2",
"Name": "TEST NODE TWO",
"Url": "http://www.yahoo.com"
},
{
"Id": "3",
"Name": "TEST NODE THREE",
"Url": "http://www.stackoverflow.com"
},
{
"Id": "4",
"Name": "TEST NODE FOUR",
"Url": "http://www.reddit.com"
}
{
"Id": "5",
"Name": "TEST NODE FIVE",
"Url": "http://www.stack.com"
}
{
"Id": "6",
"Name": "TEST NODE SIX",
"Url": "http://www.six.com"
}
]
I want to make each node a rectangle where I can put the employee name, which comes from the "nodes" json. But connect each node with the data in the "link" json. Feel free to alter the link json to have it make more sense to you.
I would also like to make the lines connecting the nodes straight and make it vertical as well but my main concern is displaying the graph on the page via the json above.
Thanks for the help.
You need to convert your links, which associates between ids of nodes, to an array that associates — via reference — between the actual objects in nodes. A simple loop does this:
var links = [ { "Source": "1", "Target": "2" }, ... ]
var nodes = [ { "Id": "1", "Name": "TEST NODE ONE", "Url": "http://www.google.com" }, ... ]
var cachedNodesById = {};
var linksBetweenNodeObjs = links.map(funciont(link) {
return {
source: nodeById(link.Source),
target: nodeById(link.Target)
};
});
function nodeById(id) {
// return the node if it's been found before, otherwise loop over the
// array to find it
if(cachedNodesById[id] != null) { return cachedNodesById[id]; }
else {
for(var i=0; i < nodes.length; i++) {
if(nodes[i].Id == id) { return cachedNodesById[id] = nodes[i]; }
}
}
}

What is wrong with this JSON file?

I put it in a parser and all it gives me is "expecting string on line 19". I have no idea what that means.
{
"name": "Rajeev",
"children": [
{
"name": "Joe",
"children": [
{
"name": "Kevin",
"children": [
{
"name": "George"
}
]
},
{
"name": "John",
"children": [
{
"name": "Barb",
}{
"name": "Michael",
}{
"name": "Charles"
}
]{
"name": "Ravinder"
]
},
Your commas are in the wrong place, e.g.
"children": [
{
"name": "Barb"
},{
"name": "Michael"
},{
"name": "Charles"
}
]
The left one is the right one. see for yourself. you had many extra , and unclosed { and [
http://i.stack.imgur.com/9yKNN.jpg
You have a property / value:
"name": "Barb",
… with a trailing comma so the next thing must be another property / value (the string mentioned in the error message is the property name).
However you have:
}{
Either remove the comma or add more details about Barb.
Then you will need to put a comma between the two objects:
}, {
It seems likely that you intended to place the comma causing teh error between the two objects, so you can just move them.
(You have similar errors throughout the rest of the file)
Sorry for the first answer, I saw a missing comma and automatically assumed that was it, but there were many other errors in there. I think this is what you're trying to do
[
{
"name": "Rajeev",
"children": [
{
"name": "Joe",
"children": [
{
"name": "Kevin",
"children": [
{
"name": "George"
}
]
},
{
"name": "John",
"children": [
{
"name": "Barb"
},
{
"name": "Michael"
},
{
"name": "Charles"
}
]
}
]
}
]
},
{
"name": "Ravinder"
}
]

Categories