I have a this type of a json object.
{
"id": "001",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001"],
"children": [
{
"id": "002",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001", "002"],
"children": []
},
{
"id": "003",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001", "003"],
"children": [
{
"id": "00001",
"type": "B",
"children": []
}
]
},
{
"id": "004",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001", "004"],
"children": [
{
"id": "005",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001", "004", "005"],
"children": []
},{
"id": "005",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001", "004", "005"],
"children": [
{
"id": "00002",
"type": "B",
"children": []
}
]
}
]
},
{
"id": "00003",
"type": "B",
"children": []
}
]
}
I need to replace all the object which is type: "B" , with this (which is mentioned below) type of object which I can get from an object with ids as keys of typed B. This typed B objects can be nested anywhere either as a first child or fifth child of a nested arrays of children
{
"id": "002",
"type": "A",
"value": "aaaaa",
"data:": {},
"children": []
},
How can I do that? This can be deeply nested and there's no specific place where we should replace the objects, beforehand. So, I need to go through the entire object and do that. How should I get it done?
EDIT
I updated the code in the question slightly. There's a path property of the nested in each object, except for typed B objects. So, when replacing the typed B properties with the other object, I need to add the paths in there as well.
eg: path for id: "00001", typed B object should be : ["001", "003", "00001"]
EDIT :
Expected result
{
"id": "001",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001"],
"children": [
{
"id": "002",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001", "002"],
"children": []
},
{
"id": "003",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001", "003"],
"children": [
{
"id": "002",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001", "003", "002"],
"children": []
},
]
},
{
"id": "004",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001", "004"],
"children": [
{
"id": "005",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001", "004", "005"],
"children": []
},{
"id": "005",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001", "004", "005"],
"children": [
{
"id": "002",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001", "004", "005", "002"],
"children": []
}
]
}
]
},
{
"id": "002",
"type": "A",
"value": "aaaaa",
"data:": {},
"path": ["001", "002"],
"children": []
}
]
}
lodash if you don't mind.
Use cloneDeepWith to clone the entire tree and replace a specific value.
const data = {"id":"001","type":"A","value":"aaaaa","data:":{},"children":[{"id":"002","type":"A","value":"aaaaa","data:":{},"children":[]},{"id":"003","type":"A","value":"aaaaa","data:":{},"children":[{"id":"00001","type":"B","children":[]}]},{"id":"004","type":"A","value":"aaaaa","data:":{},"children":[{"id":"005","type":"A","value":"aaaaa","data:":{},"children":[]},{"id":"005","type":"A","value":"aaaaa","data:":{},"children":[{"id":"00002","type":"B","children":[]}]}]},{"id":"00003","type":"B","children":[]}]};
const result = _.cloneDeepWith(data, (value) => {
const newObj = {"id": "002", "type": "A", "value": "---NEW VALUE FOR 'B' TYPE---", "data:": {} };
return (value.type === 'B') ? { ...value, ...newObj} : _.noop();
});
console.dir(result, { depth: null } );
.as-console-wrapper{min-height: 100%!important; top: 0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.js" integrity="sha512-2iwCHjuj+PmdCyvb88rMOch0UcKQxVHi/gsAml1fN3eg82IDaO/cdzzeXX4iF2VzIIes7pODE1/G0ts3QBwslA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
--- Update 2--- (Without lodash)
Use local variable to store and combine current path.
const data = { "id": "001", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001"], "children": [{ "id": "002", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "002"], "children": [] }, { "id": "003", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "003"], "children": [{ "id": "00001", "type": "B", "children": [] }] }, { "id": "004", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "004"], "children": [{ "id": "005", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "004", "005"], "children": [] }, { "id": "005", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "004", "005"], "children": [{ "id": "00002", "type": "B", "children": [] }] }] }, { "id": "00003", "type": "B", "children": [] }] }
const deepReplace = (obj, prevPath = []) => {
if (obj.type === 'A') {
if (obj.children.length) {
obj.children = obj.children.map((childObj) => deepReplace(childObj, obj.path))
}
return obj;
};
if (obj.type === 'B') {
const id = '002';
return { id, type: "A", value: "aaaaa", path: [...prevPath, id], data: {}, children: []};
};
};
console.dir(deepReplace(data), { depth: null });
.as-console-wrapper{min-height: 100%!important; top: 0}
This looks like a tree traversal problem. Here's a way to handle that with depth-first search without recursion.
As mentioned in this answer, recursion should be avoided whenever possible, as it requires more memory and is more difficult to debug than an iterative implementation.
Updated per adjustment in question
const data = {"id":"001","type":"A","value":"aaaaa","data:":{},"path":["001"],"children":[{"id":"002","type":"A","value":"aaaaa","data:":{},"path":["001","002"],"children":[]},{"id":"003","type":"A","value":"aaaaa","data:":{},"path":["001","003"],"children":[{"id":"00001","type":"B","children":[]}]},{"id":"004","type":"A","value":"aaaaa","data:":{},"path":["001","004"],"children":[{"id":"005","type":"A","value":"aaaaa","data:":{},"path":["001","004","005"],"children":[]},{"id":"005","type":"A","value":"aaaaa","data:":{},"path":["001","004","005"],"children":[{"id":"00002","type":"B","children":[]}]}]},{"id":"00003","type":"B","children":[]}]};
const dfs = () => {
const stack = [[data, null]];
while(stack.length) {
const [curr, parent] = stack.pop();
// check for match on type
if (curr.type === "B") {
curr.type = "A";
curr.id = "002";
curr.value = "aaaaa";
curr.data = {};
curr.path = [...parent?.path.slice() ?? [], "002"];
}
curr.children.forEach(child => stack.push([child, curr]));
}
};
dfs();
const output = document.getElementById("output");
output.innerText = JSON.stringify(data, null, 2);
<pre id="output" />
I played around with this in the console and it did what you wanted (based on the json array provided, setting all the "B" to "A" types). It's a recursive function so on any nested child it meets in the "children" array it would call the function again on each item in the array.
function fixObjects (obj) {
if (obj["type"] === "B") {
obj["type"] = "A";
obj["id"] = "002";
obj["value"] = "aaaaa";
obj["data"] = {};
}
if (obj["children"].length > 0) {
obj["children"].forEach (child => fixObjects (child));
}
}
fixObjects (_yourArray)
Related
I have the following array of objects with nested elements in the children property. I need to get objects by their id if the id matches.
[
{
"id": 10,
"name": "Scenarios",
"value": null,
"children": [
{
"id": 12,
"name": "Scenario status",
"value": null,
"children": []
}
]
},
{
"id": 11,
"name": "Forecast source",
"value": null,
"children": []
},
{
"id": 16787217,
"name": "Item#Cust",
"value": null,
"children": [
{
"id": 16787230,
"name": "Customer",
"value": null,
"children": [
{
"id": 16787265,
"name": "Site",
"value": null,
"children": []
},
{
"id": 16787291,
"name": "Commercial Network",
"value": null,
"children": []
},
{
"id": 16787296,
"name": "Distribution Site",
"value": null,
"children": []
}
]
},
{
"id": 16787245,
"name": "Item#Site",
"value": null,
"children": [
{
"id": 16787266,
"name": "Family#Warehouse",
"value": null,
"children": []
}
]
},
{
"id": 16787254,
"name": "Item",
"value": null,
"children": [
{
"id": 16787260,
"name": "Family",
"value": null,
"children": [
{
"id": 16787264,
"name": "Product line",
"value": null,
"children": []
}
]
},
{
"id": 16787261,
"name": "Group 1",
"value": null,
"children": []
}
]
}
]
},
{
"id": 16787267,
"name": "Supplier",
"value": null,
"children": []
},
{
"id": 16787297,
"name": "SKU",
"value": null,
"children": []
}
]
If no match on root element is found, the function should lookup its children to see if there is a match, and the first children match should be pushed on the root level.
For exemple, I have a list of ids: [12, 16787217, 16787245, 16787266]
The function should return only objects where ids matches while keeping hierarchy if matching parent id have matching children id, so it should return this:
[
{
"id": 12,
"name": "Scenario status",
"value": null,
"children": []
},
{
"id": 16787217,
"name": "Item#Cust",
"value": null,
"children": [
{
"id": 16787245,
"name": "Item#Site",
"value": null,
"children": [
{
"id": 16787266,
"name": "Family#Warehouse",
"value": null,
"children": []
}
]
}
]
}
]
As for now, I can get only the first level elements if they are found with this function:
filterArray(array: Array<any>, ids: Array<number>) {
array = array.filter(el => ids.includes(el.id));
for (let i = 0; i < array.length; i++) {
this.filterArray(array[i].children, ids);
}
console.log(array);
}
Anyone have an idea on how to achieve this?
You could reduce the array and take the nodes with found id and children or only the children at the base level.
const
find = (r, { children = [], ...o }) => {
children = children.reduce(find, []);
if (ids.includes(o.id)) r.push({ ...o, children });
else if (children.length) r.push(...children);
return r;
},
data = [{ id: 10, name: "Scenarios", value: null, children: [{ id: 12, name: "Scenario status", value: null, children: [] }] }, { id: 11, name: "Forecast source", value: null, children: [] }, { id: 16787217, name: "Item#Cust", value: null, children: [{ id: 16787230, name: "Customer", value: null, children: [{ id: 16787265, name: "Site", value: null, children: [] }, { id: 16787291, name: "Commercial Network", value: null, children: [] }, { id: 16787296, name: "Distribution Site", value: null, children: [] }] }, { id: 16787245, name: "Item#Site", value: null, children: [{ id: 16787266, name: "Family#Warehouse", value: null, children: [] }] }, { id: 16787254, name: "Item", value: null, children: [{ id: 16787260, name: "Family", value: null, children: [{ id: 16787264, name: "Product line", value: null, children: [] }] }, { id: 16787261, name: "Group 1", value: null, children: [] }] }] }, { id: 16787267, name: "Supplier", value: null, children: [] }, { id: 16787297, name: "SKU", value: null, children: [] }],
ids = [12, 16787217, 16787245, 16787266],
result = data.reduce(find, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could also try this
let data = [
{
"id": 10,
"name": "Scenarios",
"value": null,
"children": [
{
"id": 12,
"name": "Scenario status",
"value": null,
"children": []
}
]
},
{
"id": 11,
"name": "Forecast source",
"value": null,
"children": []
},
{
"id": 16787217,
"name": "Item#Cust",
"value": null,
"children": [
{
"id": 16787230,
"name": "Customer",
"value": null,
"children": [
{
"id": 16787265,
"name": "Site",
"value": null,
"children": []
},
{
"id": 16787291,
"name": "Commercial Network",
"value": null,
"children": []
},
{
"id": 16787296,
"name": "Distribution Site",
"value": null,
"children": []
}
]
},
{
"id": 16787245,
"name": "Item#Site",
"value": null,
"children": [
{
"id": 16787266,
"name": "Family#Warehouse",
"value": null,
"children": []
}
]
},
{
"id": 16787254,
"name": "Item",
"value": null,
"children": [
{
"id": 16787260,
"name": "Family",
"value": null,
"children": [
{
"id": 16787264,
"name": "Product line",
"value": null,
"children": []
}
]
},
{
"id": 16787261,
"name": "Group 1",
"value": null,
"children": []
}
]
}
]
},
{
"id": 16787267,
"name": "Supplier",
"value": null,
"children": []
},
{
"id": 16787297,
"name": "SKU",
"value": null,
"children": []
}
]
let ids = [12, 16787217, 16787245, 16787266]
let filterArray = (mArray, ids) => {
let result = []
for (const x of mArray) {
if (ids.includes(x.id)) {
let element = {...x, children: []}
let childrenFounds = []
if (x.children.length) {
childrenFounds = filterArray(x.children, ids)
if (childrenFounds.length) element.children.push(...childrenFounds)
}else {
delete element.children
}
result.push(element)
} else if (x.children.length) {
let found = filterArray(x.children, ids)
if (found.length) result.push(...found)
}
}
return result
}
let res = filterArray(data, ids)
console.log('res', res)
I need to rename "text" attribute with "name" in all of objects (parent and child)
the API return the objects as below:
"content": [
[{
"id": 1,
"text": "hierarchy 1",
"icon": "h",
"children": [{
"id": 2,
"text": "hierarchy 11",
"icon": "h",
"children": []
},
{
"id": 3,
"text": "hierarchy 110",
"icon": "h",
"children": []
}
]
}]
]
My API function should return the same result but after rename "text" with "name":
async getTree() {
const res = await axios.get("/api/HierarchyTree");
return res.data.content["0"].map(item => {
return {
...item,
name: item.text
};
});
}
The result should be:
"content": [
{
"id": 1,
"name": "hierarchy 1",
"icon": "h",
"children": [{
"id": 2,
"name": "hierarchy 11",
"icon": "h",
"children": []
},
{
"id": 3,
"name": "hierarchy 110",
"icon": "h",
"children": []
}
]
}
]
Perhaps this?
let content = [
[{
"id": 1,
"text": "hierarchy 1",
"icon": "h",
"children": [{
"id": 2,
"text": "hierarchy 11",
"icon": "h",
"children": []
},
{
"id": 3,
"text": "hierarchy 110",
"icon": "h",
"children": []
}
]
}]
]
content = JSON.parse(JSON.stringify(content[0]).replace(/"text"/g,'"name"'))
console.log(content)
Have an object that needs to be pass inside the array of source using javascript,throwing an error spec is undefined when using push function. will push function work for this scenario
var a = [
"name": "ben",
"type": "male",
"appType": "human",
"spec": {
"view": "instanceview",
"sink": {
"source": [{
"data": {
"path": "google/path",
"name": "test",
"Id": "11234",
},
"ref": "www.xyz.com",
"id": "isdfjsbfjsfb",
"resourceType": "app"
}
],
},
},
}]
var b = {
"data": {
"path": "google/path",
"name": "goldengate",
"Id": "11234vndslknvlsmnv",
},
"ref": "www.xyz.com",
"id": "6452367e5375",
"resourceType": "app"
}
a.spec.sink.source.push(b);
would expect b to be pushed to source
An array with string keys is not a valid structure, you need to convert a to an object
var a = { // <-- here
"name": "ben",
"type": "male",
"appType": "human",
"spec": {
"view": "instanceview",
"sink": {
"source": [
{
"data": {
"path": "google/path",
"name": "test",
"Id": "11234",
},
"ref": "www.xyz.com",
"id": "isdfjsbfjsfb",
"resourceType": "app"
}
],
},
},
} // <-- here
I have hundreds of objects inside each other
and under each object there is a repeated object structure with an object inside that contains hundred of keys that are the same and in most of the keys the values are the same also.
What I would like to do is to get from this repeated object the key and value only if its the same across all over the objects and put it aside in an new object that contains all the same repeated pairs of key/value while also remove it from that repeated object inside of every one of the objects.
json objects for example:
[{
"name": "A",
"values": {
"ab": "abc",
"ac": "1",
"ad": "none"
},
"childs": []
},
{
"name": "A",
"values": {
"ab": "abc",
"ac": "1",
"ad": "none"
},
"childs": [{
"name": "B",
"values": {
"ab": "abc",
"ac": "1",
"ad": "none"
},
"childs": [{
"name": "C",
"values": {
"ab": "abc",
"ac": "1",
"ad": "none"
},
"childs": []
},
{
"name": "C",
"values": {
"ab": "def",
"ac": "1",
"ad": "none"
},
"childs": []
}
]
}]
},
{
"name": "A",
"values": {
"ab": "abc",
"ac": "1",
"ad": "none"
},
"childs": [{
"name": "D",
"values": {
"ab": "abc",
"ac": "1",
"ad": "none"
},
"childs": []
}]
}]
desired output:
[{
"name": "A",
"values": {
"ab": "abc"
},
"childs": []
},
{
"name": "A",
"values": {
"ab": "abc"
},
"childs": [{
"name": "B",
"values": {
"ab": "abc"
},
"childs": [{
"name": "C",
"values": {
"ab": "abc"
},
"childs": []
},
{
"name": "C",
"values": {
"ab": "def"
},
"childs": []
}
]
}]
},
{
"name": "A",
"values": {
"ab": "abc"
},
"childs": [{
"name": "D",
"values": {
"ab": "abc"
},
"childs": []
}]
}]
and a new object contains the key/value pairs that were removed because they are the same:
[{
"ac": "1",
"ad": "none"
}]
We could take the first object as a starting point for key value pairs:
let pairs = Object.entries( objects[0].values );
Then for all the values we remove non dupes:
function remove(obj){
pairs = pairs.filter(([key,value]) => obj.values[key] === value);
//the same for all childs:
if(obj.childs) obj.childs.forEach(remove);
}
objects.forEach(remove);
So now weve got a list of key values every object shares, so now we can build an object again:
const result = {};
for(let [key,value] of pairs) result[key] = value;
And we can remove duplicates:
function dupes(obj){
pairs.forEach(([key]) => {
if( key in obj.values) delete obj.values[key];
});
if(obj.childs) obj.childs.forEach(dupes);
}
objects.forEach(dupes)
Try it
This is the JSON Object i am working on :
var r={
"id": "A",
"name": "Analysis",
"url": "A.html",
"root": true,
"children": [
{
"id": "B",
"name": "Introduction",
"url": "B.html",
"root": true,
"children": [{
"id": "C",
"name": "Creating",
"url": "c.html#I1"
}, {
"id": "D",
"name": "Running",
"url": "d.html#I2"
}]
},
{
"id": "E",
"name": "Transient Analysis",
"url": "E.html",
"root": true,
"children": [{
"id": "F",
"name": "RC",
"url": "F.html#T1"
}, {
"id": "G",
"name": "RLC",
"url": "G.html#T2"
}]
}
]
}
I want this as another key-value
path="home.html"
and it should be appended not only to the root node but to all its children node.
So it should look like.
var r= {
"id": "A",
"name": "Analysis",
"url": "A.html",
"path":"home.html",
"root": true,
"children": [
{
"id": "B",
"name": "Introduction",
"url": "B.html",
"root": true,
"path":"home.html",
"children": [{
"id": "C",
"name": "Creating",
"url": "c.html#I1",
"path":"home.html"
}, {
"id": "D",
"name": "Running",
"url": "d.html#I2",
"path":"home.html"
}]
},
{
"id": "E",
"name": "Transient Analysis",
"url": "E.html",
"root": true,
"path":"home.html",
"children": [{
"id": "F",
"name": "RC",
"url": "F.html#T1",
"path":"home.html"
}, {
"id": "G",
"name": "RLC",
"url": "G.html#T2",
"path":"home.html"
} ]
}
]
}
so far I've tried is this :
r.path={"home.html"}
and
var to_concatjson = JSON.parse(r);
to_concatjson["path"] = {"home.html"};
but both are not working for me.
Further Modifications in the above question :
Lets say i am loading my json content from my File 1
File 1 looks like
[
{
"id":"a", "name":"a","children":[
{ "id":"b", "name":"b","children":[
{ "id":"b", "name":"b"},
{ "id":"c", "name":"c"},
{ "id":"d", "name":"d"}
]
},
{ "id":"e", "name":"e","children":[
{ "id":"f", "name":"f"},
{ "id":"g", "name":"g"},
{ "id":"h", "name":"h"}
]
},
]
}
]
the content of my json file is loaded in result[0]. and when i am calling addPath like :
result[0]=addPath(result[0]);
it shows me an error that "forEach" will not work as it is not the property of object.
Do it with this code :-
function addPath(obj){
if(!obj.hasOwnProperty('path')){
obj.path = 'home.html';
}
if(obj.hasOwnProperty('children')){
obj.children.forEach(function(obj1){
obj1 = addPath(obj1);
});
}
return obj;
}
r = addPath(r);
I know that my way is not optimum, but it is working:
I append new item through .replace(...) (without loop)
var r = JSON.parse(JSON.stringify(r).replace(new RegExp("\"url\"", 'g'), '"path": "home.html", "url"'));
console.log(r);
var r={
"id": "A",
"name": "Analysis",
"url": "A.html",
"root": true,
"children": [
{
"id": "B",
"name": "Introduction",
"url": "B.html",
"root": true,
"children": [{
"id": "C",
"name": "Creating",
"url": "c.html#I1"
}, {
"id": "D",
"name": "Running",
"url": "d.html#I2"
}]
},
{
"id": "E",
"name": "Transient Analysis",
"url": "E.html",
"root": true,
"children": [{
"id": "F",
"name": "RC",
"url": "F.html#T1"
}, {
"id": "G",
"name": "RLC",
"url": "G.html#T2"
}]
}
]
}
var r = JSON.parse(JSON.stringify(r).replace(new RegExp("\"url\"", 'g'), '"path": "home.html", "url"'));
console.log(r);