Finding child object using one of its properties in JSON - javascript

I am trying to find a child object in JSON by one of its properties and add more properties to that object. I am not sure how to do this using JQuery (or regular javascript). For example: From the following JSON, I would like to find a category with id 123-1 and then add another category object as a child object. Thanks for your help.
JSON:
{
"result": {
"category":
{
"id": 123,
"name": "cat1",
"rules": [
{
"rulename": "r1",
"regex": ""
},
{
"rulename": "r2",
"regex": ""
}
],
"category":
{
"id": "123-1",
"name": "cat1-1",
"rules": [
{
"rulename": "r1-1",
"regex": ""
}
]
}
}
}
}
Javascript:
function addSubCategory(catId, anotherCatObj) {
//Step1: Find category object with catID in the existing json
//Step3: add the supplied object as a child.
}

function appendCategoryTo(categories, destinationCategoryId, newCategoryToAdd){
var success = false;
for (var i = 0; i < categories.length && !success; i++){
var category = categories[i];
if (category.id == destinationCategoryId){
category.category = category.category || [];
success = !!category.category.push(newCategoryToAdd);
} else if (category.category) {
success = appendCategoryTo(category.category, destinationCategoryId, newCategoryToAdd);
}
}
return success;
}
you have to start at the obj.result.category node in order to take advantage of the recursive ability, but you can easily wrap that method in another that makes it more polite.
but, as-is, here's an example usage:
appendCategoryTo(o.result.category, '123-1', {
id: '123-1-1',
name: 'cat-1-1-1',
rules: []
});
console.log(JSON.stringify(o));
Which adds a new category property to the nested category as an array (i assume this follows the nomenclature) then adds the element to that new array--thus giving you:
{
"result": {
"category": [
{
"id": 123,
"name": "cat1",
"rules": [
{
"rulename": "r1",
"regex": ""
},
{
"rulename": "r2",
"regex": ""
}
],
"category": [
{
"id": "123-1",
"name": "cat1-1",
"rules": [
{
"rulename": "r1-1",
"regex": ""
}
],
"category": [ // BEGIN new addition
{
"id": "123-1-1",
"name": "cat-1-1-1",
"rules": [
]
}
] // END new addition
}
]
}
]
}
}
Example to play with on jsfiddle, btw: http://jsfiddle.net/cqRzX/

Related

Convert JSON array with nested arrays (tree) to flat JSON array [duplicate]

This question already has answers here:
Find all values by specific key in a deep nested object
(11 answers)
Closed 10 months ago.
I have this JSON array tree that can include any number of nested arrays:
const namesArrayTree = [
{
"name": "Peter"
},
{
"name": "folder1",
"isArray": true,
"namesArray": [
{
"name": "Paul"
},
{
"name": "folder2",
"isArray": true,
"namesArray": [
{
"name": "Mary"
},
{
"name": "John"
}
]
}
]
},
{
"name": "Mark"
}
]
I need to transform it to a flat array including only the names:
const namesArrayFlat = [ "Peter", "Paul", "Mary", "John", "Mark" ]
So I'm using this code to do the transformation:
const namesArrayTree = [
{
"name": "Peter"
},
{
"name": "folder1",
"isArray": true,
"namesArray": [
{
"name": "Paul"
},
{
"name": "folder2",
"isArray": true,
"namesArray": [
{
"name": "Mary"
},
{
"name": "John"
}
]
}
]
},
{
"name": "Mark"
}
] ;
function getNamesList(item) {
let name = item.name;
let isArray = item.isArray;
if (isArray) {
name = item.namesArray.map(getNamesList).join("\r\n");
}
return name;
}
const namesList = namesArrayTree.map(getNamesList).join("\r\n");
const namesArrayFlat = namesList.split("\r\n");
console.log(namesArrayFlat)
The code works well, but I would like to get rid of the extra steps to create a list with the names using join.("\r\n") and then convert to array using split("\r\n").
That is, I would like to reduce the code by removing the following:
function getNamesList(item) {
let name = item.name;
let isArray = item.isArray;
if (isArray) {
/* remove code to join by "\r\n" */
name = item.namesArray.map(getNamesList)
}
return name;
}
/* remove code to create "namesList" constant and remove code to join by "\r\n") */
const namesArrayFlat = namesArrayTree.map(getNamesList)
console.log(namesArrayFlat)
(The above code still returns a tree nested arrays structure)
Any ideas about how to get rid of the extra code? also any suggestions about how to improve the code would be great, thanks!
function getNamesList(item) {
return item.isArray ? item.namesArray.map(getNamesList) : item.name
}
const names = namesArrayTree.map(getNamesList).flat(Infinity)
console.log(names)
You can achieve this with an array reducer as follows:
const namesArray = [
{
"name": "Peter"
},
{
"name": "folder1",
"isArray": true,
"namesArray": [
{
"name": "Paul"
},
{
"name": "folder2",
"isArray": true,
"namesArray": [
{
"name": "Mary"
},
{
"name": "John"
}
]
}
]
},
{
"name": "Mark"
}
] ;
function reduceNamesList(list, item) {
if (item.isArray) {
return item.namesArray.reduce(reduceNamesList, list);
}
list.push(item.name)
return list
}
const namesList = namesArray.reduce(reduceNamesList, [])
console.log(namesList)

search inside object of objects and replace value

I want to loop through all objects values inside object and replace password value with * Restricted * . I want to use recursive way tp loop over all this items
before i was targeting objects that include password value but was thinking that it can appear somewhere else inside the object entry .
this is what i tried
if (entry.requestBody) {
for (const key of Object.keys(entry.requestBody)) {
if (key.toLowerCase().includes("password")) {
entry.requestBody[key] = "***Restricted***"
}
}
if (entry.config._object) {
for (const key of Object.keys(entry.config._object)) {
if (key.toLowerCase().includes("password")) {
entry.config._object[key] = "***Restricted***"
}
}
}
}
{
"requestBody": {
"email": "ror#ror.com",
"password": "asdasdsad"
},
"code": "VALIDATION_ERROR",
"config": {
"isJoi": true,
"name": "ValidationError",
"details": [
{
"message": "\"email\" must be a valid email",
"path": [
"email"
],
"type": "string.email",
"context": {
"value": "ror#ror.com",
"key": "email",
"label": "email"
}
}
],
"_object": {
"email": "ror#ror.com",
"password": "asdasdsad"
},
"_meta": {
"source": "body"
}
}
}
}
"requestBody": {
"email": "ror#ror.com",
"password": "***Restricted***"
}
Instead of recursion, how about using regex for a quick and dirty solution:
var bodyString = JSON.stringify(entry.requestBody);
var fixedBodyString = bodyString.replace(/"password":".*"/g, '"password":"***Restricted***"');
var body = JSON.parse(fixedBodyString );
Working example:
https://playcode.io/353673?tabs=script.js,preview,console

How to denormalize array in JS

I have a data set of the following form
let data = [
{
"id": {
"primary": "A1"
},
"msg": 1
}, {
"id": {
"primary": "A1"
},
"msg": 2
}, {
"id": {
"primary": "B2"
},
"msg": 3
}
]
I would like to transform it to
newData = [
{
"id": {
"primary": "A1"
},
"items": [
{ "msg": 1 },
{ "msg": 2 }
]
},
{
"id": {
"primary": "B2"
},
"items": [
{ "msg": 3 }
]
}
]
I think the method is something like the following, but am not sure how to check against undefined values in this case.
let newData = [];
for (let i = 0; i < data.length; i++) {
if (newData[i]['id']['primary'] === data[i]['id']) newData.push(data[i]['id'])
else newData[i]['items'].push(data[i]['msg'])
}
How can I transform the original data set to merge entries with a matching primary id?
One option would be to use .reduce() to create a new array from the existing.
I've added comments to clarify.
let data = [ { "id": { "primary": "A1" }, "msg": 1 }, { "id": { "primary": "A1" }, "msg": 2 }, { "id": { "primary": "B2" }, "msg": 3 } ];
let result = data.reduce((out,item) => {
let {id, ...items} = item; //Separate the "id" and "everything else"
let existing = out.find(({id}) => id.primary == item.id.primary);
existing //have we seen this ID already?
? existing.items.push(items) //yes - add the items to it
: out.push({ id: {...id}, items: [items]}); //no - create it
return out;
}, []);
console.log(result);
A couple notes:
You may notice that I've set the ID using id: {...id}, despite the id already being an object. This is because using the existing id object would create a reference, whereas {...id} creates a shallow copy.
I haven't specified the msg property anywhere. Instead, any properties that aren't id will be added to the items list (example below).
let data = [ { "id": { "primary": "A1" }, "msg": 1, "otherStuff": "Hello World!" }, { "id": { "primary": "A1" }, "msg": 2, "AnotherThing": true }, { "id": { "primary": "B2" }, "msg": 3, "someOtherProperty": false } ];
let result = data.reduce((out,item) => {
let {id, ...items} = item;
let existing = out.find(({id}) => id.primary == item.id.primary);
existing
? existing.items.push(items)
: out.push({ id: {...id}, items: [items]});
return out;
}, []);
console.log(result);
That said, if you start to nest objects (other than ID), they will likely be included as references; ...items is only a shallow copy.
If such a case, consider something like JSON.parse(JSON.stringify(...)) for a deep copy. Be sure to read the link though; there are caveats.
You could also solve this in a concise way via the Array.reduce and ES6 destructuring:
let data = [ { "id": { "primary": "A1" }, "msg": 1 }, { "id": { "primary": "A1" }, "msg": 2 }, { "id": { "primary": "B2" }, "msg": 3 } ]
let result = data.reduce((r, {id, msg}) =>
((r[id.primary] = r[id.primary] || { id, items: [] }).items.push({msg}), r), {})
console.log(Object.values(result))
In more readable format it is:
let data = [ { "id": { "primary": "A1" }, "msg": 1 }, { "id": { "primary": "A1" }, "msg": 2 }, { "id": { "primary": "B2" }, "msg": 3 } ]
let result = data.reduce((r, {id, msg}) => {
r[id.primary] = (r[id.primary] || { id, items: [] })
r[id.primary].items.push({msg})
return r
}, {})
console.log(Object.values(result))
The idea is to group by the id.primary and then once the grouping is done simply get the values via Object.values
Notice that this is one pass solution where you do not have to per each iteration do an Array.find against the current accumulator.

Remove duplicate array from response comparing attribute value

I want to remove a duplicate array from the response on the basis of the attribute value. If the attribute_value data match with other array attribute value then other should be removed.
The logic is very simple. check duplicate attribute_value in each array and remove duplicate array and return
In response. now you can see the attribute value = 1 is thrice
and attribute value = 2 is twice
How do i compare and remove whole array if I see attribute value duplicate?
I tried with filter method which seems not working. Please help.
for(var j=0; j<social_post_link.length; j++){
newFilterarray = social_post_link[j].activity_attributes[0].attribute_value.filter(function(item, index) {
if (social_post_link[j].activity_attributes[0].attribute_value.indexOf(item) == index){
return social_post_link;
}
});
}
Response
[
{
"id": "484822",
"activity_attributes": [
{
"id": "868117",
"activity_id": "484822",
"attribute_name": "position",
"attribute_value": "1",
}
]
},
{
"id": "484884",
"activity_attributes": [
{
"id": "868175",
"activity_id": "484884",
"attribute_name": "position",
"attribute_value": "1",
}
]
},
{
"id": "484888",
"activity_attributes": [
{
"id": "868182",
"activity_id": "484888",
"attribute_name": "position",
"attribute_value": "1",
}
]
},
{
"id": "484823",
"activity_attributes": [
{
"id": "868120",
"activity_id": "484823",
"attribute_name": "position",
"attribute_value": "2",
}
]
},
{
"id": "484975",
"activity_attributes": [
{
"id": "868344",
"attribute_name": "position",
"attribute_value": "2",
}
]
},
{
"id": "484891",
"activity_attributes": [
{
"id": "868189",
"attribute_name": "position",
"attribute_value": "3",
}
]
},
{
"id": "484903",
"activity_attributes": [
{
"id": "868200",
"attribute_name": "position",
"attribute_value": "4",
},
]
}
]
Desired output
[
{
"id": "484822",
"activity_attributes": [
{
"id": "868117",
"activity_id": "484822",
"attribute_name": "position",
"attribute_value": "1",
}
]
},
{
"id": "484823",
"activity_attributes": [
{
"id": "868120",
"activity_id": "484823",
"attribute_name": "position",
"attribute_value": "2",
}
]
},
{
"id": "484891",
"activity_attributes": [
{
"id": "868189",
"attribute_name": "position",
"attribute_value": "3",
}
]
},
{
"id": "484903",
"activity_attributes": [
{
"id": "868200",
"attribute_name": "position",
"attribute_value": "4",
},
]
}
]
You can probably use the lodash utility uniqBy,
where iteratee is a function that returns the value you want to compare against.
In your case, it would probably look like the following:
const uniqueLinks = _.uniqBy(social_post_link, item =>
item.activity_attributes[0].attribute_value
)
Edit:
Here is a vanilla JS function that will accomplish the same.
const filterByIteratee = (array, iteratee) => {
// Empty object to store attributes as we encounter them
const previousAttributeNames = {
}
return array.filter(item => {
// Get the right value
const itemValue = iteratee(item)
// Check if we have already stored this item
if (previousAttributeNames.hasOwnProperty(itemValue)) return false
else {
// Store the item so next time we encounter it we filter it out
previousAttributeNames[itemValue] = true
return true
}
})
}
It will loop through an array, store its identifier by some function, and return only the first instance of each item.
Use it the same way:
const uniqueLinks = filterByIteratee(social_post_link, item =>
item.activity_attributes[0].attribute_value
)
This is probably not the best performing solution. but it works for your requirements.
var resultArray = [];
for (var i = 0; i < social_post_link.length; i++) {
var currentSocialLink = social_post_link[i];
for (var j = 0; j < currentSocialLink.activity_attributes.length; j++) {
if (!resultArray.some(val =>
val.activity_attributes.some(activity =>
activity.attribute_value === currentSocialLink.activity_attributes[j].attribute_value))) {
resultArray.push(currentSocialLink);
}
}
}
function removeDuplicates(myArr, prop) { // removes duplicate objects from array
return myArr.filter((obj, pos, arr) => {
return arr.map(mapObj => mapObj[prop]).indexOf(obj[prop]) === pos;
});
};
I found this function not too long ago which removes duplicate objects from an array. Pass it the array and the property you wish to not be duplicated.

How to iterate through deeply nested objects inside of a JSON?

I know there are plenty of questions about iterating through JSON objects but I haven't found one that quite relates to my exact problem. This is the JSON that I'm trying to iterate through:
psinsights = {
"kind": "pagespeedonline#result",
"id": "/speed/pagespeed",
"responseCode": 200,
"title": "PageSpeed Home",
"score": 90,
"pageStats": {
"numberResources": 22,
"numberHosts": 7,
"totalRequestBytes": "2761",
"numberStaticResources": 16,
"htmlResponseBytes": "91981",
"cssResponseBytes": "37728",
"imageResponseBytes": "13909",
"javascriptResponseBytes": "247214",
"otherResponseBytes": "8804",
"numberJsResources": 6,
"numberCssResources": 2
},
"formattedResults": {
"locale": "en_US",
"ruleResults": {
"AvoidBadRequests": {
"localizedRuleName": "Avoid bad requests",
"ruleImpact": 0.0
},
"MinifyJavaScript": {
"localizedRuleName": "Minify JavaScript",
"ruleImpact": 0.1417,
"urlBlocks": [
{
"header": {
"format": "Minifying the following JavaScript resources could reduce their size by $1 ($2% reduction).",
"args": [
{
"type": "BYTES",
"value": "1.3KiB"
},
{
"type": "INT_LITERAL",
"value": "0"
}
]
},
"urls": [
{
"result": {
"format": "Minifying $1 could save $2 ($3% reduction).",
"args": [
{
"type": "URL",
"value": "http://code.google.com/js/codesite_tail.pack.04102009.js"
},
{
"type": "BYTES",
"value": "717B"
},
{
"type": "INT_LITERAL",
"value": "1"
}
]
}
},
{
"result": {
"format": "Minifying $1 could save $2 ($3% reduction).",
"args": [
{
"type": "URL",
"value": "http://www.gmodules.com/ig/proxy?url\u003dhttp%3A%2F%2Fjqueryjs.googlecode.com%2Ffiles%2Fjquery-1.2.6.min.js"
},
{
"type": "BYTES",
"value": "258B"
},
{
"type": "INT_LITERAL",
"value": "0"
}
]
}
}
]
}
]
},
"SpriteImages": {
"localizedRuleName": "Combine images into CSS sprites",
"ruleImpact": 0.0
}
}
},
"version": {
"major": 1,
"minor": 11
}
};
Now, I'm trying to write a function that iterates through all of the ruleResults objects and returns an array of the localizedRuleName properties. According to the JSON, ruleResults has three member objects (AvoidBadRequests, MinifyJavaScript, and SpriteImages). Each of these has a localizedRuleName property I'm trying to access, but when I print out my array, it's blank. Here's how I've written my function:
function ruleList(results) {
var ruleArray = [];
for(var ruleName in results.formattedResults.ruleResults){
ruleArray[counter] = results.formattedResults.ruleResults[ruleName].localizedRuleName;
}
return ruleArray;
}
console.log(ruleList(psinsights));
Can you guys help me get on the right track? I used basically this same method to iterate through the pageStats of the JSON and it worked perfectly. I'm not sure why I can't get it to work with these deeper nested objects and properties.
your problem is not your iteration, but your undefined variable "counter".
Instead of using a counter can use the "push" function:
function ruleList(results) {
var ruleArray = [];
for(var ruleName in results.formattedResults.ruleResults){
ruleArray.push(results.formattedResults.ruleResults[ruleName].localizedRuleName);
}
return ruleArray;
}
fiddle: https://jsfiddle.net/fo9h56gh/
Hope this helps.
you're probably getting a javascript error since counter is not defined. you can try this:
function ruleList(results) {
var ruleArray = [];
var counter = 0;
for(var ruleName in results.formattedResults.ruleResults){
ruleArray[counter] = results.formattedResults.ruleResults[ruleName].localizedRuleName;
counter++;
}
return ruleArray;
}

Categories

Resources