I have an object like this:
{
"responses": {
"firstKey": {
"items": {
"name": "test name one"
}
},
"anotherKey": {
"items": {
"name": "test name two"
}
},
"oneMoreKey": {
"items": {
"name": "John"
}
}
}
}
I need to find all 'name' keys and replace its value only if it starts with 'test name' then return new JSON object:
{
"responses": {
"firstKey": {
"items": {
"name": "N/A"
}
},
"anotherKey": {
"items": {
"name": "N/A"
}
},
"oneMoreKey": {
"items": {
"name": "John"
}
}
}
}
The problem is that the keys are not consistent through the objects, i.e. 'firstKey', 'secondKey'... I tried ForEach but it seems to be too cumbersome... So I need either lodash or vanila JavaScript to replace the values.
The javascript object should be iterated and then each value of name can be checked and replaced. There are checks such as hasOwnProperty() that can be used to make sure you are not iterating objects that are missing "items" or "name" for better error handling.
var data = {
"responses": {
"firstKey": {
"items": {
"name": "test name one"
}
},
"anotherKey": {
"items": {
"name": "test name two"
}
},
"oneMoreKey": {
"items": {
"name": "John"
}
}
}
};
Given the JSON above you can use a simple for statement to iterate and then check each name for some value and replace.
for(var key in data.responses){
if ((data.responses[key].items.name).match(/test name/)){
data.responses[key].items.name = "N/A";
}
}
To check your replacements you can log data to the console.
console.log(JSON.stringify(data));
It can also be done during parsing :
var json = `{
"responses": {
"firstKey": {
"items": {
"name": "test name one"
}
},
"anotherKey": {
"items": {
"name": "test name two"
}
},
"oneMoreKey": {
"items": {
"name": "John"
}
}
}
}`
var obj = JSON.parse(json, (k, v) => k == 'name' && /^test name/.test(v) ? 'N/A' : v)
console.log( obj )
A javascript object is for all intents and purposes a tree — though it can be, and may well be, a directed graph — that quite possibly may be cyclic meaning a node in the graph points back to own of its own parents. Following a cycle can result in never-ending recursion or loop.
You want to use something like traverse to do what you're talking about. It takes care of all the stuff that makes traversing a graph hassle — dealing with cycles in the graph and the like.
https://www.npmjs.com/package/traverse
https://github.com/substack/js-traverse
const traverse = require('traverse');
. . .
var scrubbed = traverse(obj).map( function(value) {
const isTestName = this.key === 'name'
&& value
&& /^test name/i.test(value)
;
if (isTestName) {
this.update('N/A');
}
});
NOTE: The callback function given to travese can't be an arrow function (() => {...} as that function's this context is the traverse context for the current node being inspected.
That traverse context also gives you access to the entire path from the root down to the current node, along with an upward link to the parent node's traverse context.
Do something like this. Convert to string replace using regex (add key to regex as well) and then convert back.
var data = {
"responses": {
"firstKey": {
"items": {
"name": "test name one"
}
},
"anotherKey": {
"items": {
"name": "test name two"
}
},
"oneMoreKey": {
"items": {
"name": "John"
}
}
}
};
var originalMsg = JSON.stringify(data);
console.log(data)
console.log(originalMsg)
var updatedMsg = originalMsg.replace(/test name [a-z]*/g, "N/A");
console.log(updatedMsg)
var newObj = JSON.parse(updatedMsg);
console.log(newObj);
Related
This question already has answers here:
How can I recursively replace the key name in an object?
(6 answers)
Closed 2 months ago.
I have object like this which contains many keys named label and choices:
const obj = {
"label": "mylabel",
"choices": [
{
"label": "mylabel_11",
"choices": {
"label": "mylabel_12",
"choices": [ /* … */ ]
}
},
{
"label": "mylabel_21",
"choices": {
"label": "mylabel_22",
"choices": [ /* … */ ]
}
},
]
}
I want to change all "label" to "name", and all "choices" to "children".
Is there any recursive way to replace the name?
Currently my idea is this:
const new_keys = {};
for (const key in obj) {
const old_key = key;
key = key.replace("label", "name");
key = key.replace("choices", "children");
new_keys[key] = obj[old_key]
}
How can I make this recursive?
IMHO the easiest way would be to use string.replace. All code in one line
var obj=JSON.parse(JSON.stringify(obj).replaceAll("\"label\":","\"name\":")
.replaceAll("\"choices\":","\"children\":"));
result
{
"name": "mylabel",
"children": [
{
"name": "mylabel_11",
"children": {
"name": "mylabel_12",
"children": []
}
},
{
"name": "mylabel_21",
"children": {
"name": "mylabel_22",
"children": []
}
}
]
}
You could look to use recursion in the following way, noting that the if statement accounts for the fact that your object's "choices" can be either an array or an object.
function replaceObject(yourObj) {
if (yourObj.hasOwnProperty("label")) {
if (Array.isArray(yourObj.choices)) {
return {
name: yourObj.label,
children: yourObj.choices.map((choice) => {
return replaceObject(choice);
}),
};
} else {
return {
name: yourObj.label,
children: replaceObject(yourObj.choices),
};
}
}
}
I think you're asking for a way to make it go arbitrarily deep into your object. I think this could work:
function recursiveKeyRename(obj) {
var new_keys;
for (key in obj){
var old_key = key;
key = key.replace("label","name");
key = key.replace("choices","children");
new_keys[key] = obj[old_key]
};
if (obj.children && obj.children.choices)
{
recursiveKeyRename(obj.children.choices)
};
};
I haven't really tested that, and that only works if all of you "choices" are objects, not arrays like you (maybe?) implied in your example. It can easily be retooled for whichever use case, though.
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));
I'm working on an app in javascript, and I have this json object (this is a simplified example of the actual json object)
{
"data": [
"user": {
"pictures"{
"sizes"[
0: {
"link": "http://www"
},
1: {
"link": "http://"
}
]
}
}
]
}
And I want to get the value of link, so i tried data.user.pictures.sizes[0].link, and it returned an error. How to get the right value?
edit: I'm using a map function to loop around the data object, so i can display values in an html page. it works for most of the other items, except for pictures sizes, i cant seem to get the value of the second item in the sizes array.
From the data, it shows that 'data' contains array and sizes contains array that contains object of properties 0 and 1 , so do this
data[0].user.pictures.sizes[0].0.link
First of all you need to have colons to "pictures" and "sizes" :)
Secondly, it depends what your structure is.
for example:
if you need to use arrays as they appear in your example:
var a = {
"data": [
{
"user": {
"pictures": {
"sizes": [
{
0: {
"link": "http://www"
}
},
{
1: {
"link": "http://"
}
}
]
}
}
}
]
}
console.log(a.data[0].user.pictures.sizes[0][0].link);
or you just need a json without arrays in it:
var b = {
"data": {
"user": {
"pictures": {
"sizes": {
0: {
"link": "http://www"
},
1: {
"link": "http://"
}
}
}
}
}
}
console.log(b.data.user.pictures.sizes[0].link);
or you can even mix them:
var c = {
"data": {
"user": {
"pictures": {
"sizes": [
{
"link": "http://www"
},
{
"link": "http://"
}
]
}
}
}
}
console.log(c.data.user.pictures.sizes[0].link);
notice the subtle difference of "sizes" in b and c and the double array of a. This is because json with index as keys is the same as an array. check this example:
var example = [
{
0: "example00",
1: "example01",
2: "example02"
},
{
0: "example10",
1: "example11",
2: "example12"
},
]
console.log(example[0][1]); // == example01
you can see that you have 2 arrays within the example array
hope that helps :)
Your JSON is wrong, it should be as below :
var js = {
"data": {
"user":{
"pictures":{
"sizes":[
{
"link":"http://www"
},
{
"link":"http://"
}
]
}
}
}
}
To get the value:
js.data.user.pictures.sizes[0].link
Say I wanted to check if Mango_EA existed. How would I do that in Node JS.
{
"selection1": [
{
"name": "ClashOnGan"
},
{
"name": "JoblessGarrett"
},
{
"name": "FemmeFatale"
},
{
"name": "Mango_EA"
}
]
}
obj.selection1.some(x => x.name === "Mango_EA")
In English,
Within the array given by obj.selection1, is there some element x which satisfies the condition that the name property of x is equal to "Mango_EA".
I'm building the JSON object using JavaScript. How would I inset the following data to the bottom of the stack:
"hello": { "label":"Hello", "url":"#hello" }
in to the following variable:
var ListData = {
"main": {
"label":"Main",
"url":"#main"
},
"project": {
"label":"Project",
"url":"#project"
},
"settings": {
"label":"Settings",
"url":"#settings",
"subnav":[
{
"label":"Privacy",
"url":"#privacy"
},
{
"label":"Security",
"url":"#security"
},
{
"label":"Advanced",
"url":"#advanced"
}
]
}
};
So the variable looks like:
var ListData = {
"main": {
"label":"Main",
"url":"#main"
},
"project": {
"label":"Project",
"url":"#project"
},
"settings": {
"label":"Settings",
"url":"#settings",
"subnav":[
{
"label":"Privacy",
"url":"#privacy"
},
{
"label":"Security",
"url":"#security"
},
{
"label":"Advanced",
"url":"#advanced"
}
]
},
"hello": {
"label":"Hello",
"url":"#hello"
}
};
I used the following code but it doesn't seem to work:
var NewData = '"hello": { "label":"Hello", "url":"#hello" }';
ListData.push(NewData);
You can insert it directly with an object literal:
ListData.hello = { label: "Hello", url: "#hello" };
If you are using jQuery, you can use the .extend() jQuery API like:
$.extend(ListData, {"hello": { "label":"Hello", "url":"#hello" }});
I have one more solution using underscore.js module,
var _ = require("underscore");
var src = {
"main": {
"label": "Main",
"url": "#main"
},
"project": {
"label": "Project",
"url": "#project"
},
"settings": {
"label": "Settings",
"url": "#settings",
"subnav": [
{
"label": "Privacy",
"url": "#privacy"
},
{
"label": "Security",
"url": "#security"
},
{
"label": "Advanced",
"url": "#advanced"
}
]
}
};
var dest = {"hello": { "label":"Hello", "url":"#hello" }};
var data = _.extend(src, dest);
console.log(JSON.stringify(data));
Required op :
{"main":{"label":"Main","url":"#main"},"project":{"label":"Project","url":"#project"},"settings":{"label":"Settings","url":"#settings","subnav":[{"label":"Privacy","url":"#privacy"},{"label":"Security","url":"#security"},{"label":"Advanced","url":"#advanced"}]},"hello":{"label":"Hello","url":"#hello"}}
Keeping with you object literal statements just add another object to your ListData object.
ListData.hello = { "label":"Hello", "url":"#hello" };
push is only for Javascript Arrays.
A JavaScript Object Literal is a comma-separated list of name/value pairs wrapped by a pair of curly braces.
To append the property name of encampment name with a value of Valley Forge to the bottom of the stack, simply add the property name after the JSON object with a dot syntax. Then specify the value. (See 'Append data' below)
You can also delete the appended name/value pair from the object literal. (See 'Delete data below')
// Start with some JSON
var myJson = { "name":"George Washington", "rank":"General", "serial":"102" };
// Append data
myJson.encampment = "Valley Forge";
// Delete data
delete myJson.encampment