I have this nested object (json):
const json = {
"application": {
"App1": {
"cats": [
1
]
},
"App2": {
"cats": [
3
]
},
"App3": {
"cats": [
1,
2
]
}
},
"categories": {
"1": {
"name": "FirstCategory"
},
"2": {
"name": "SecondCategory"
},
"3": {
"name": "ThirdCategory"
}
}
};
This object has two main properties: application and categories.
I want to map over application's cats array and get name property of each element of cats array.
So the final result should look like:
{
"App1": "FirstCategory",
"App2": "ThirdCategory",
"App3": "FirstCategory, ThirdCategory"
}
I have tried to use map function, but the main difficulty is that inside applicaiton property cats is array (can have multiple values). So the code below didn't work:
Object.values(json.application).map(val => {
Object.keys(json.categories).map(key => {
//print something
});
});
You can use Array.reduce for an elegant solution.
const json = {
"application": {
"App1": {
"cats": [
1
]
},
"App2": {
"cats": [
3
]
},
"App3": {
"cats": [
1,
2
]
}
},
"categories": {
"1": {
"name": "FirstCategory"
},
"2": {
"name": "SecondCategory"
},
"3": {
"name": "ThirdCategory"
}
}
};
//Getting Application object
const application = json.application
//Getting Categories object
const categories = json.categories
//initializing reduce with a blank object and pushing all the keys of the application object
//Looping over keys of application object
const requiredOutput = Object.keys(application).reduce((out, appKey) => {
//Setting value based on categories name
out[appKey] = application[appKey].cats.map(id => categories[id].name)
return out
}, {})
console.log(requiredOutput)
PS: You can refer this gist for safe reading from a nested object.
Try it with this.
for(let val in json.application){
json.application[val] = json.application[val].cats.map(cat => json.categories[cat].name).join(",")
}
const result = Object.keys(json.application).reduce((a,key) => {
a[key] = json.application[key].cats
.map(cat => json.categories[cat].name)
.join(", ")
return a;
}, {})
loop over keys of application
for each key loop over car, and for each cat return string value from category
join list of cat strings
Related
Given the following sample JSON (stringified from the corresponding JavaScript object), I need to extract this information:
Find the object in persons which has the reference = 2.
If a person with this reference was found, get the name of the person's parent element (here: "B").
In the end, I need to build a new object looking similar to this. This won't be problematic but I'm struggling with how to extract these objects from the source. I tried different approaches with find(), map(), flatMap() and filter() but none of them really worked.
{
companyName: "B",
person: {
"reference": 2,
"name": "Bob"
}
}
Source
{
"root": [
{
"companies": [
{
"name": "A",
"persons": [
{
"reference": 1,
"name": "Alex"
}
]
}
]
},
{
"companies": [
{
"name": "B",
"persons": [
{
"reference": 2,
"name": "Bob"
},
{
"reference": 3,
"name": "Charles"
}
]
}
]
}
]
}
If you're just interested in the name of the company you can find it using:
const reference = 2;
const company = data.root.flatMap(item => item.companies)
.find(company => company.persons.some(person => person.reference === reference));
const companyName = company?.name;
// or if you cannot use optional chaining
const companyName = (company || {}).name;
In data.root.flatMap(item => item.companies) we iterate through all items in root, for each item we select its companies property. Since we don't want a nested array we use flatMap() to flatten the result by 1 level. This leaves us with an array of companies.
After that we'll call find() on the companies array, since we are looking for a specific company name. The criteria of the company is that some() (1 or more) of the persons should match the provided reference. If no match is found null will be returned (from find()).
We then take the find() result (company) and navigate to the name via optional chaining ?.. This will return the name of the company if present, or undefined if company is null
You can use array.reduce here
let data = JSON.parse(`{
"root": [
{
"companies": [
{
"name": "A",
"persons": [
{
"reference": 1,
"name": "Alex"
}
]
}
]
},
{
"companies": [
{
"name": "B",
"persons": [
{
"reference": 2,
"name": "Bob"
},
{
"reference": 3,
"name": "Charles"
}
]
}
]
}
]
}`)
// GET ALL THE COMPANIES DATA
let companies = data.root.reduce(
(prevValue, currValue) => {
prevValue.push(...currValue.companies)
return prevValue
},
[]
)
// FIND AND CREATE EXPECTED RESULT SET
let results = companies.reduce(
(prevValue, currValue) => {
// loop inside a loop, mind it
let refPerson = currValue.persons.find((item)=> item.reference == 2)
if(refPerson){
prevValue.push({
companyName: currValue.name,
person: refPerson
})
}
return prevValue
},
[]
)
console.log(results)
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)
I have a problem on an object inside of an array and I wanted to display only that as an array.
data1
const data1 = [
{
"id": "01",
"info": "fefef",
"sub": "hieei",
"details": {
"data": "fruits"
}
},
{
"id": "02",
"info": "fefef",
"sub": "hieei",
"details": {
"data": "things"
}
}
]
expected output
const final= [
{
"data": "fruits"
},
{
"data": "things"
}
]
Code
const final = data.map((data) => { ...data})
map over the array and return a new object using the details property. If you don't return a new object, your new array will still carry references to the objects in the original array. So if you change a value of a property in that original array, that change will be reflected in the new array too, and you probably don't want that to happen.
const data1=[{id:"01",info:"fefef",sub:"hieei",details:{data:"fruits"}},{id:"02",info:"fefef",sub:"hieei",details:{data:"things"}}];
// Make sure you return a copy of the
// details object otherwise if you change the details
// of the original objects in the array
// the new mapped array will carry those object changes
// because the array objects will simply references to the old objects
const out = data1.map(obj => {
return { ...obj.details };
});
console.log(out);
Map through the array and extract its details property:
const data1 = [
{
"id": "01",
"info": "fefef",
"sub": "hieei",
"details": {
"data": "fruits"
}
},
{
"id": "02",
"info": "fefef",
"sub": "hieei",
"details": {
"data": "things"
}
}
]
const res = data1.map(e => e.details)
console.log(res)
Using map and destructuring will simplify.
const data1 = [
{
id: "01",
info: "fefef",
sub: "hieei",
details: {
data: "fruits",
},
},
{
id: "02",
info: "fefef",
sub: "hieei",
details: {
data: "things",
},
},
];
const res = data1.map(({ details: { data } }) => ({ data }));
console.log(res);
// if you just need the details object
const res2 = data1.map(({ details }) => details);
console.log(res2);
I have an array of objects. You'll be able to find a sample of complete data here on JSONBlob.com.
Here's a simplified preview:
[
{
"name": "Unbetitelt",
"organizationMap": "60a55ed4e3a8973a02f910f1",
"childrenLayout": [{
"i": "60a64930cf1db1710b97bf7a",
}],
"id": "60a6472ecf1db1710b97bf4c"
},
{
"name": "middleparent",
"parentCubo": "60a6472ecf1db1710b97bf4c",
"organizationMap": "60a55ed4e3a8973a02f910f1",
"childrenLayout": [{
"i": "60a64936cf1db1710b97bf7d",
},
{
"i": "60a649afcf1db1710b97bfa6",
}
],
"id": "60a64930cf1db1710b97bf7a"
},
{
"name": "Unbetitelt",
"parentCubo": "60a64930cf1db1710b97bf7a",
"organizationMap": "60a55ed4e3a8973a02f910f1",
"childrenLayout": [
{
"i": "60a6494acf1db1710b97bf8f",
},
{
"i": "60a64976cf1db1710b97bf9a",
}
],
"id": "60a64936cf1db1710b97bf7d"
},
{
"name": "Unbetitelt",
"parentCubo": "60a64930cf1db1710b97bf7a",
"organizationMap": "60a55ed4e3a8973a02f910f1",
"childrenLayout": [],
"id": "60a649afcf1db1710b97bfa6"
},
{
"name": "Unbetitelt",
"parentCubo": "60a649c5cf1db1710b97bfb1",
"organizationMap": "60a55ed4e3a8973a02f910f1",
"childrenLayout": [],
"id": "60a649efcf1db1710b97bfc4"
}
]
Each object in this array(the root array) has an id and childrenLayout property.
The childrenLayout property is an array(the child array) of Objects which will have an i property but won't have another childrenLayout. So in order to find children of each item in the childrenLayout array, we'll have to use this i field and find objects in the root array.
Once we find those objects in the root array, if the childrenLayout property on those objects is not an empty array, then we have to repeat the same process and find objects in this object's childrenLayout, thereby creating a list of all ids that are linked to one specific object with id x.
So, for instance, if we try to find all the items linked with id 60a6472ecf1db1710b97bf4c, we'll get:
60a64930cf1db1710b97bf7a
60a64936cf1db1710b97bf7d
60a649afcf1db1710b97bfa6
60a6494acf1db1710b97bf8f
60a64976cf1db1710b97bf9a
This is what I have so far and it's obviously not working as expcted and only returns the last three items from the expected items list mentioned above:
const findChildFor = item => {
if (item.childrenLayout.length > 0) {
const detailsOfItems = item.childrenLayout.map(({ i }) =>
data.find(datum => datum.id === i)
);
return detailsOfItems.map(itemDetails => findChildFor(itemDetails));
} else {
return item.id;
}
};
const [firstItem] = data;
// TODO: Work on a way to make it return children without having to flatten it.
const childrenRelatedToFirstItem = findChildFor(firstItem);
console.log(
'childrenRelatedToFirstItem: ',
childrenRelatedToFirstItem.flat(10)
);
To help speed up the solution, here's a Sample Stackblitz with the code so far.
Any help is greatly appreciated.
I found a solution. Code isn't so complex. Hoping this would be helpful.
const findChildFor = (arr, id, output) => {
const item = arr.find(obj => obj.id === id);
if (item) {
const childs = item.childrenLayout;
for (let child of childs) {
output.push(child.i);
findChildFor(arr, child.i, output);
}
}
}
output = [];
findChildFor(data, '60a6472ecf1db1710b97bf4c', output);
console.log(output);
This data looks like a graph description. Here is my solution for the problem with optimized time complexity and taking care of cyclic connections.
const findAllChildren = (arr, id) => {
// to find each child with O(1) access time
const familyMap = new Map(arr.map(it => [it.id, it.childrenLayout]));
// to check if a child has been collected with O(1) access time
const resultSet = new Set();
const findChildrenFor = (parentId) => {
familyMap.get(parentId).forEach(({i}) => {
// in a case of a cyclic graph
if (!resultSet.has(i)) {
resultSet.add(i);
findChildrenFor(i);
}
});
};
return Array.from(findChildrenFor(id));
};
I through in a solution in a different way but with the result that you want.
dataArray.reduce((acc,cur)=>{
acc[cur.id] = [];
cur.childrenLayout.map(child=>acc[cur.id].push(child.i))
Object.entries(acc).map(obj=>{
if(obj[1].includes(cur.id)){
cur.childrenLayout.map(child=>acc[obj[0]].push(child.i))
}
})
return acc;
},{})
Any question leave it in the comments section.
const dataArray = [
{
"name": "Unbetitelt",
"organizationMap": "60a55ed4e3a8973a02f910f1",
"childrenLayout": [{
"i": "60a64930cf1db1710b97bf7a",
}],
"id": "60a6472ecf1db1710b97bf4c"
},
{
"name": "middleparent",
"parentCubo": "60a6472ecf1db1710b97bf4c",
"organizationMap": "60a55ed4e3a8973a02f910f1",
"childrenLayout": [{
"i": "60a64936cf1db1710b97bf7d",
},
{
"i": "60a649afcf1db1710b97bfa6",
}
],
"id": "60a64930cf1db1710b97bf7a"
},
{
"name": "Unbetitelt",
"parentCubo": "60a64930cf1db1710b97bf7a",
"organizationMap": "60a55ed4e3a8973a02f910f1",
"childrenLayout": [
{
"i": "60a6494acf1db1710b97bf8f",
},
{
"i": "60a64976cf1db1710b97bf9a",
}
],
"id": "60a64936cf1db1710b97bf7d"
},
{
"name": "Unbetitelt",
"parentCubo": "60a64930cf1db1710b97bf7a",
"organizationMap": "60a55ed4e3a8973a02f910f1",
"childrenLayout": [],
"id": "60a649afcf1db1710b97bfa6"
},
{
"name": "Unbetitelt",
"parentCubo": "60a649c5cf1db1710b97bfb1",
"organizationMap": "60a55ed4e3a8973a02f910f1",
"childrenLayout": [],
"id": "60a649efcf1db1710b97bfc4"
}
]
const childFinder = arr => {
return arr.reduce((acc,cur)=>{
acc[cur.id] = [];
cur.childrenLayout.map(child=>acc[cur.id].push(child.i))
Object.entries(acc).map(obj=>{
if(obj[1].includes(cur.id)){
cur.childrenLayout.map(child=>acc[obj[0]].push(child.i))
}
})
return acc;
},{})
}
console.log(childFinder(dataArray))
My target is if the id from digital_assets and products matches then get the value of URL fro digital_assets and ProductName from products object. I'm able to traverse through the object and get the values of digital_assets and products but need some help to compare these two objects based on IDs to get the value of URL and ProductName. Below is what I've done so far.
var data = [{
"digital_assets": [{
"id": "AA001",
"url": "https://via.placeholder.com/150"
},{
"id": "AA002",
"url": "https://via.placeholder.com/150"
}]
}, {
"products": [{
"id": ["BB001", "AA001"],
"ProductName": "PROD 485"
},{
"id": ["BB002", "AA002"],
"ProductName": "PROD 555"
}]
}
];
$.each(data, function () {
var data = this;
//console.log(data);
$.each(data.digital_assets, function () {
var dAssets = this,
id = dAssets['id'];
// console.log(id);
});
$.each(data.products, function () {
var proData = this,
prod_id = proData['id'];
// console.log(prod_id);
$.each(prod_id, function () {
var arr_id = this;
console.log(arr_id);
});
});
});
Do I need to create new arrays and push the values into the new arrays? Then concat() these array to one. ? Bit lost any help will be appreciated.
Here is one way you can do this via Array.reduce, Array.includes, Object.entries and Array.forEach:
var data = [{ "digital_assets": [{ "id": "AA001", "url": "https://via.placeholder.com/150" }, { "id": "AA002", "url": "https://via.placeholder.com/150" } ] }, { "products": [{ "id": ["BB001", "AA001"], "ProductName": "PROD 485" }, { "id": ["BB002", "AA002"], "ProductName": "PROD 555" } ] } ]
const result = data.reduce((r,c) => {
Object.entries(c).forEach(([k,v]) =>
k == 'digital_assets'
? v.forEach(({id, url}) => r[id] = ({ id, url }))
: v.forEach(x => Object.keys(r).forEach(k => x.id.includes(k)
? r[k].ProductName = x.ProductName
: null))
)
return r
}, {})
console.log(Object.values(result))
You can use Array.prototype.find, Array.prototype.includes and Array.prototype.map to achieve this very gracefully.
let data = [
{
"digital_assets": [
{
"id": "AA001",
"url": "https://via.placeholder.com/150"
},
{
"id": "AA002",
"url": "https://via.placeholder.com/150"
}
]
},
{
"products": [
{
"id": ["BB001", "AA001"],
"ProductName": "PROD 485"
},
{
"id": ["BB002","AA002"],
"ProductName": "PROD 555"
}
]
}
];
// Find the 'digital_assets' array
let assets = data.find(d => d['digital_assets'])['digital_assets'];
// Find the 'products' array
let products = data.find(d => d['products'])['products'];
// Return an array of composed asset objects
let details = assets.map(a => {
return {
id : a.id,
url : a.url
name : products.find(p => p.id.includes(a.id)).ProductName
};
});
console.log(details);
changed answer to fit your needs:
var data = [
{
"digital_assets": [
{
"id": "AA001",
"url": "https://via.placeholder.com/150"
},
{
"id": "AA002",
"url": "https://via.placeholder.com/150"
}
]
},
{
"products": [
{
"id": ["BB001", "AA001"],
"ProductName": "PROD 485"
},
{
"id": ["BB002","AA002"],
"ProductName": "PROD 555"
}
]
}
]
let matchingIds = [];
let data_assetsObject = data.find(element => {
return Object.keys(element).includes("digital_assets")
})
let productsObject = data.find(element => {
return Object.keys(element).includes("products")
})
data_assetsObject["digital_assets"].forEach(da => {
productsObject["products"].forEach(product => {
if (product.id.includes(da.id)){
matchingIds.push({
url: da.url,
productName: product.ProductName
})
}
})
})
console.log(matchingIds);
working fiddle: https://jsfiddle.net/z2ak1fvs/3/
Hope that helped. If you dont want to use a new array, you could also store the respective data within the element you are looping through.
Edit:
I think i know why i got downvoted. My example works by making data an object, not an array. changed the snippet to show this more clearly.
Why is data an array anyway? Is there any reason for this or can you just transform it to an object?
Edit nr2:
changed the code to meet the expectations, as i understood them according to your comments. it now uses your data structure and no matter whats in data, you can now search for the objects containing the digital_assets / products property.
cheers
https://jsfiddle.net/2b1zutvx/
using map.
var myobj = data[0].digital_assets.map(function(x) {
return {
id: x.id,
url: x.url,
ProductName: data[1].products.filter(f => f.id.indexOf(x.id) > -1).map(m => m.ProductName)
};
});