Rename json keys iterative - javascript

I got a very simple json but in each block I got something like this.
var json = {
"name": "blabla"
"Children": [{
"name": "something"
"Children": [{ ..... }]
}
And so on. I don't know how many children there are inside each children recursively.
var keys = Object.keys(json);
for (var j = 0; j < keys.length; j++) {
var key = keys[j];
var value = json[key];
delete json[key];
key = key.replace("Children", "children");
json[key] = value;
}
And now I want to replace all "Children" keys with lowercase "children". The following code only works for the first depth. How can I do this recursively?

It looks the input structure is pretty well-defined, so you could simply create a recursive function like this:
function transform(node) {
return {
name: node.name,
children: node.Children.map(transform)
};
}
var json = {
"name": "a",
"Children": [{
"name": "b",
"Children": [{
"name": "c",
"Children": []
}, {
"name": "d",
"Children": []
}]
}, {
"name": "e",
"Children": []
}]
};
console.log(transform(json));

A possible solution:
var s = JSON.stringify(json);
var t = s.replace(/"Children"/g, '"children"');
var newJson = JSON.parse(t);
Pros: This solution is very simple, being just three lines.
Cons: There is a potential unwanted side-effect, consider:
var json = {
"name": "blabla",
"Children": [{
"name": "something",
"Children": [{ ..... }]
}],
"favouriteWords": ["Children","Pets","Cakes"]
}
The solution replaces all instances of "Children", so the entry in the favouriteWords array would also be replaced, despite not being a property name. If there is no chance of the word appearing anywhere else other than as the property name, then this is not an issue, but worth raising just in case.

Here is a function that can do it recursivly:
function convertKey(obj) {
for (objKey in obj)
{
if (Array.isArray(obj[objKey])) {
convertKey[objKey].forEach(x => {
convertKey(x);
});
}
if (objKey === "Children") {
obj.children = obj.Children;
delete obj.Children;
}
}
}
And here is a more generic way for doing this:
function convertKey(obj, oldKey, newKey) {
for (objKey in obj)
{
if (Array.isArray(obj[objKey])) {
obj[objKey].forEach(objInArr => {
convertKey(objInArr);
});
}
if (objKey === oldKey) {
obj[newKey] = obj[oldKey];
delete obj[oldKey];
}
}
}
convertKey(json, "Children", "children");

Both the accepted answer, and #Tamas answer have slight issues.
With #Bardy's answer like he points out, there is the issue if any of your values's had the word Children it would cause problems.
With #Tamas, one issue is that any other properties apart from name & children get dropped. Also it assumes a Children property. And what if the children property is already children and not Children.
Using a slightly modified version of #Tamas, this should avoid the pitfalls.
function transform(node) {
if (node.Children) node.children = node.Children;
if (node.children) node.children = node.children.map(transform);
delete node.Children;
return node;
}
var json = {
"name": "a",
"Children": [{
"age": 13,
"name": "b",
"Children": [{
"name": "Mr Bob Chilren",
"Children": []
}, {
"name": "d",
"age": 33, //other props keep
"children": [{
"name": "already lowecased",
"age": 44,
"Children": [{
"name": "now back to upercased",
"age": 99
}]
}] //what if were alrady lowercased?
}]
}, {
"name": "e",
//"Children": [] //what if we have no children
}]
};
console.log(transform(json));

Related

Find branches in nested Object in Javascript

I have tried to find a solution for my problem in the last two hours, which included trying myself, scanning lodash docs as well as SO for any suitable answer, but did not come up with anything remotely working or practical. I would be very grateful for help.
I have an object that can be any depth.
e.g.
{
"name": "alpha",
"children": [
{
"name": "beta",
"children": [
{
"name": "gamma",
"children": [
{
"name": "delta",
"children": []
}
]
}
]
},
{
"name": "epsilon",
"children": [
{
"name": "zeta",
"children": [
{
"name": "eta",
"children": []
}
]
}
]
}
]
}
I am looking for a function which will return the whole branch of this object where there is a matching name (if possible without lodash but if really needed its ok).
Given an input of 'gamma' I expect it to return
{
"name": "alpha",
"children": [
{
"name": "beta",
"children": [
{
"name": "gamma",
"children": [
{
"name": "delta",
"children": []
}
]
}
]
}
]
}
Given an input 't' I expect it to return the whole object, since it is included in the names of children in both branches.
You can separate this problem into two parts, first let's test if the name is present in the tree:
function hasStr(item, str) {
return item.name.includes(str) || item.children.some(x => hasStr(x, str));
}
hasStr(item, 'gamma'); // true
You also asked to have a function that returns the passed object and return only a filtered version of the children:
function possibleAnswer(item, str) {
return {
name: item.name,
children: item.children.filter(x => hasStr(x, str)),
};
}
possibleAnswer(item, 'gamma'); // will return desired object
This problem can be solved with recursion as the depth is not know.
getNames = (nameChild, match) => {
if (nameChild.length < 1) return false;
if (nameChild.name.includes(match)) return true;
k = nameChild.children
.filter((child) =>
getNames(child, match))
return (k.length > 0) ? k : false;
}
suppose the object is assign to nameObj variable then
nameObj.children = getNames(nameObj, 'zeta');
this will do the work!

How to update a specific node of a tree structured global json object in javascript?

I passed a global tree structured JSON string in jquery fancy tree. I can add a new node data to the global object. I am not able to update a specific node of the global JSON string although I can find that specific node.
I have tried the following way of doing what I wanted but at the end the global variable is not changed. I also tried recursive way but not worked too. Also this is my first question at stackoverflow. I simplified the data because real data is so big.
// global json object array
var treeViewData = [
{
"nodeData": {
"physicalid": "A",
"rootToNode": "A-0",
"level": "0",
"attribute": "English-english"
},
"children": [
{
"nodeData": {
"physicalid": "B",
"rootToNode": "A-0:B-1",
"level": "1",
"attribute": "Finish-finish"
},
"children": []
},
{
"nodeData": {
"physicalid": "C",
"rootToNode": "A-0:C-1",
"level": "1",
"attribute": "Arabic-arabic"
},
"children": [
{
"nodeData": {
"physicalid": "D",
"rootToNode": "A-0:C-1:D-2",
"level": "2",
"attribute": "Spanish-spanished"
},
"children": []
}
]
}
]
}
]
Here is the update method. rootToNode is unique.
findAndUpdateNodeIterative: function(selfNodeRootPath, updatedNode) {
console.log("+++++++++ findAndUpdateNodeIterative ++++++++");
var thisContext = this;
var currentNode = thisContext.treeViewData[0];
var i, currentChild, result;
var flag = true;
while (flag) {
if (currentNode.nodeData.rootToNode === selfNodeRootPath) {
currentNode.nodeData.attribute = updatedNode.nodeData.attribute;
flag = false;
} else {
let it = true;
for (i = 0; i < currentNode.children.length && it; i += 1) {
currentChild = currentNode.children[i];
var currentNodeLevel = parseInt(currentChild.nodeData.level);
var targetPhysicalId = selfNodeRootPath.split(':')[currentNodeLevel].split("-")[0];
if (targetPhysicalId === currentChild.nodeData.physicalid) {
// Search in the current child
currentNode = currentChild;
it = false;
}
}
}
}
console.log("---------- findAndUpdateNodeIterative ---------");
},
usage
var updatedNode = {
"nodeData": {
"physicalid": "D",
"rootToNode": "A-0:C-1:D-2",
"level": "2",
"attribute": "Spanish-spanish"
},
"children": []
};
thisContext.findAndUpdateNodeIterative(updatedNode.nodeData.rootToNode, updatedNode);
console.log(thisContext.treeViewData); // **not updated**
RequireJS Code structure,
// about 4000 lines are present in this module
define("CustomModule", ["fewModules"], function(fewModules) {
var widget = {
init: function() {
this.treeViewData = '';
},
// many methods are here
update: function() {
var thisContext = this;
// rest api call brings the data nodesMap
for (let selectedObject of nodesMap.values()) {
// many lines
thisContext.findAndUpdateNodeIterative(updatedNode.nodeData.rootToNode, updatedNode);
console.log(thisContext.treeViewData); // not updated treeViewData
}
}
};
return widget;
}

Reverse Traverse a hierarchy

I have a hierarchy of objects that contain the parent ID on them. I am adding the parentId to the child object as I parse the json object like this.
public static fromJson(json: any): Ancestry | Ancestry[] {
if (Array.isArray(json)) {
return json.map(Ancestry.fromJson) as Ancestry[];
}
const result = new Ancestry();
const { parents } = json;
parents.forEach(parent => {
parent.parentId = json.id;
});
json.parents = Parent.fromJson(parents);
Object.assign(result, json);
return result;
}
Any thoughts on how to pull out the ancestors if I have a grandchild.id?
The data is on mockaroo curl (Ancestries.json)
As an example, with the following json and a grandchild.id = 5, I would create and array with the follow IDs
['5', '0723', '133', '1']
[{
"id": "1",
"name": "Deer, spotted",
"parents": [
{
"id": "133",
"name": "Jaime Coldrick",
"children": [
{
"id": "0723",
"name": "Ardys Kurten",
"grandchildren": [
{
"id": "384",
"name": "Madelle Bauman"
},
{
"id": "0576",
"name": "Pincas Maas"
},
{
"id": "5",
"name": "Corrie Beacock"
}
]
},
There is perhaps very many ways to solve this, but in my opinion the easiest way is to simply do a search in the data structure and store the IDs in inverse order of when you find them. This way the output is what you are after.
You could also just reverse the ordering of a different approach.
I would like to note that the json-structure is a bit weird. I would have expected it to simply have nested children arrays, and not have them renamed parent, children, and grandchildren.
let data = [{
"id": "1",
"name": "Deer, spotted",
"parents": [
{
"id": "133",
"name": "Jaime Coldrick",
"children": [
{
"id": "0723",
"name": "Ardys Kurten",
"grandchildren": [
{
"id": "384",
"name": "Madelle Bauman"
},
{
"id": "0576",
"name": "Pincas Maas"
},
{
"id": "5",
"name": "Corrie Beacock"
}
]
}]
}]
}]
const expectedResults = ['5', '0723', '133', '1']
function traverseInverseResults(inputId, childArray) {
if(!childArray){ return }
for (const parent of childArray) {
if(parent.id === inputId){
return [parent.id]
} else {
let res = traverseInverseResults(inputId, parent.parents || parent.children || parent.grandchildren) // This part is a bit hacky, simply to accommodate the strange JSON structure.
if(res) {
res.push(parent.id)
return res
}
}
}
return
}
let result = traverseInverseResults('5', data)
console.log('results', result)
console.log('Got expected results?', expectedResults.length === result.length && expectedResults.every(function(value, index) { return value === result[index]}))

How to delete object from an array of objects having relations with each arrays?

This Object have relationship as: childOne > childTwo > childThree > childFour > childFive > childSix.
{
"parentObj": {
"childOne": [
{
"name": "A",
"id": "1"
},
{
"name": "B",
"id": "2"
}
],
"childTwo": [
{
"name": "AB",
"parent_id": "1",
"id": "11"
},
{
"name": "DE",
"parent_id": "2",
"id": "22"
}
],
"childThree": [
{
"name": "ABC",
"parent_id": "22",
"id": "111"
},
{
"name": "DEF",
"parent_id": "11",
"id": "222"
}
],
"childFour": [
{
"name": "ABCD",
"parent_id": "111",
"id": "1111"
},
{
"name": "PQRS",
"parent_id": "111",
"id": "2222"
}
],
"childFive": [
{
"name": "FGRGF",
"parent_id": "1111",
"id": "11111"
},
{
"name": "ASLNJ",
"parent_id": "1111",
"id": "22222"
},
{
"name": "ASKJA",
"parent_id": "1111",
"id": "33333"
}
],
"childSix": [
{
"name": "SDKJBS",
"parent_id": "11111",
"id": "111111"
},
{
"name": "ASKLJB",
"parent_id": "11111",
"id": "222222"
}
]
}
}
Is there any way to delete an item by ID and the objects which are associated with that particular ID should get deleted(i.e., If I do delete parentObj.childTwo[1], then all the related object beneath it should also gets deleted).
Looping manually is too bad code, and generate bugs. There must be better ways of dealing with this kind of problems like recursion, or other.
The data structure does not allow for efficient manipulation:
By nature objects have an non-ordered set of properties, so there is no guarantee that iterating the properties of parentObj will give you the order childOne, childTwo, childThree, ... In practice this order is determined by the order in which these properties were created, but there is no documented guarantee for that. So one might find children before parents and vice versa.
Although the id values within one such child array are supposed to be unique, this object structure does not guarantee that. Moreover, given a certain id value, it is not possible to find the corresponding object in constant time.
Given this structure, it seems best to first add a hash to solve the above mentioned disadvantages. An object for knowing a node's group (by id) and an object to know which is the next level's group name, can help out for that.
The above two tasks can be executed in O(n) time, where n is the number of nodes.
Here is the ES5-compatible code (since you mentioned in comments not to have ES6 support). It provides one example call where node with id "1111" is removed from your example data, and prints the resulting object.
function removeSubTree(data, id) {
var groupOf = {}, groupAfter = {}, group, parents, keep = { false: [], true: [] };
// Provide link to group per node ID
for (group in data) {
data[group].forEach(function (node) {
groupOf[node.id] = group;
});
}
// Create ordered sequence of groups, since object properties are not ordered
for (group in data) {
if (!data[group].length || !data[group][0].parent_id) continue;
groupAfter[groupOf[data[group][0].parent_id]] = group;
}
// Check if given id exists:
group = groupOf[id];
if (!group) return; // Nothing to do
// Maintain list of nodes to keep and not to keep within the group
data[group].forEach(function (node) {
keep[node.id !== id].push(node);
});
while (keep.false.length) { // While there is something to delete
data[group] = keep.true; // Delete the nodes from the group
if (!keep.true.length) delete data[group]; // Delete the group if empty
// Collect the ids of the removed nodes
parents = {};
keep.false.forEach(function (node) {
parents[node.id] = true;
});
group = groupAfter[group]; // Go to next group
if (!group) break; // No more groups
// Determine what to keep/remove in that group
keep = { false: [], true: [] };
data[group].forEach(function (node) {
keep[!parents[node.parent_id]].push(node);
});
}
}
var tree = {"parentObj": {"childOne": [{"name": "A","id": "1"},{"name": "B","id": "2"}],"childTwo": [{"name": "AB","parent_id": "1","id": "11"},{"name": "DE","parent_id": "2","id": "22"}],"childThree": [{"name": "ABC","parent_id": "22","id": "111"},{"name": "DEF","parent_id": "11","id": "222"}],"childFour": [{"name": "ABCD","parent_id": "111","id": "1111"},{"name": "PQRS","parent_id": "111","id": "2222"}],"childFive": [{"name": "FGRGF","parent_id": "1111","id": "11111"},{"name": "ASLNJ","parent_id": "1111","id": "22222"},{"name": "ASKJA","parent_id": "1111","id": "33333"}],"childSix": [{"name": "SDKJBS","parent_id": "11111","id": "111111"},{"name": "ASKLJB","parent_id": "11111","id": "222222"}]}}
removeSubTree(tree.parentObj, "1111");
console.log(tree.parentObj);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Sure, the function you use to delete an entry should FIRST recurse, which means run itself on the linked entry, unless there is none. So, in psuedocode
function del(name, index)
{
if parent[name][index] has reference
Then del(reference name, reference ID)
Now del parent[name][index]
}
No loop needed.
And since we stop if there is no reference, we do not recurse forever.
Not sure what it is you want but maybe this will work:
const someObject = {
"parentObj": {
"childOne": [
{
"name": "A",
"id": "1"
},
{
"name": "B",
"id": "2"
}
],
"childTwo": [
{
"name": "AB",
"childOne": "1",
"id": "11"
},
{
"name": "DE",
"childOne": "2",
"id": "22"
}
]
}
};
const removeByID = (key,id,parent) =>
Object.keys(parent).reduce(
(o,k)=>{
o[k]=parent[k].filter(
item=>
!(Object.keys(item).includes(key)&&item[key]===id)
);
return o;
},
{}
);
const withoutID = Object.assign(
{},
someObject,
{ parentObj : removeByID("childOne","1",someObject.parentObj) }
);
console.log(`notice that childTwo item with childOne:"1" is gone`);
console.log("without key:",JSON.stringify(withoutID,undefined,2));
const otherExample = Object.assign(
{},
someObject,
{ parentObj : removeByID("childOne","2",someObject.parentObj) }
);
console.log(`notice that childTwo item with childOne:"2" is gone`);
console.log("without key:",JSON.stringify(otherExample,undefined,2));
const both = Object.assign(
{},
someObject,
{ parentObj : removeByID("childOne","1",otherExample.parentObj) }
);
console.log(`notice that childTwo items with childOne are both gone`);
console.log("without key:",JSON.stringify(both,undefined,2));

In JSON Object parent key name remove and Include its inner content

This is my JSON Object in this i want to remove "item" key from json and want to keep its inner object include with "children" and its tree like JOSN. How can I do this?
[
{
"item": {
"id": 11865,
"parentid": null,
"levelid": 63,
"name": "Total"
},
"children": [
{
"item": {
"id": 10143,
"parentid": 11865,
"levelid": 19,
"name": "Productive"
}
}
]
}
]
If I'm understanding what you want your object to look like after correctly, then this should do the trick:
var arrayOfObjects = [
{
"item": {
"id": 11865,
"parentid": null,
"levelid": 63,
"name": "Total"
},
"children": [
{
"item": {
"id": 10143,
"parentid": 11865,
"levelid": 19,
"name": "Productive"
}
}
]
}
]
arrayOfObjects.forEach(function(obj) {
obj.id = obj.item.id;
obj.parentid = obj.item.parentid;
obj.levelid = obj.item.levelid;
obj.name = obj.item.name;
delete obj.item;
});
All this is doing is manually moving the data from obj.item to obj and then deleting obj.item entirely.
I would do this:
//your original array of objects
var array = [{
"item": {
"id": 11865,
"parentid": null,
"levelid": 63,
"name": "Total"
},
"children": [
{
"item": {
"id": 10143,
"parentid": 11865,
"levelid": 19,
"name": "Productive"
}
}
]
}, ...];
array.forEach(function(parent) {
flattenKey(parent, 'item');
});
function flattenKey(parent, keyName) {
var section = parent[keyName];
var child = section ? section : {};
var keys = Object.keys(child);
keys.forEach(function(key) {
parent[key] = child[key];
})
delete parent[keyName];
}
basically, the function flattenKey would flatten any key for a given object (given its key).
logic is similar to other solutions here: iterate through child keys and assign their values to the parent object (flattening).
then it deletes the child key after step 1.
try
objArray = objArray.map( function(value){
var item = value.item;
for( var key in item )
{
value[key] = item[key];
}
delete value.item;
return value;
});
DEMO
Explanation
1) Use map to iterate on each item (value) of this given array objArray.
2) Get the item property of value, assign them to value directly
3) Finally delete the item property of the value.
Faster option
objArray = objArray.map( function(value){
var item = value.item;
var itemKeys = Object.keys(item);
for( var counter = 0; counter < itemKeys.length; counter++ )
{
value[itemKeys[counter]] = item[itemKeys[counter]];
}
delete value.item;
return value;
});
You can use a recursion, which keeps the content of item and adds the children property as well.
function delItem(a, i, aa) {
var children = a.children;
if (a.item) {
aa[i] = a.item;
aa[i].children = children;
delete a.item;
}
Array.isArray(children) && children.forEach(delItem);
}
var array = [{ "item": { "id": 11865, "parentid": null, "levelid": 63, "name": "Total" }, "children": [{ "item": { "id": 10143, "parentid": 11865, "levelid": 19, "name": "Productive" } }] }];
array.forEach(delItem);
document.write('<pre>' + JSON.stringify(array, 0, 4) + '</pre>');

Categories

Resources