I'm trying to loop a complex/nested JSON object. I'm trying to loop var_color.color.primary.
Questions:
What causes this error?
Cannot read property '_id' of undefined
How can I output the primary?
Kindly use only vanilla Javascript.
Example Data
products.json file
{
"products": [
{
"_id": "000",
"name": "Name 1",
"description": "Long description 1"
},
{
"_id": "001",
"name": "Name 2",
"description": "Long description 2",
"var_color": {
"_id": "12341",
"color": {
"primary": "pink",
"secondary": "penk"
}
}
},
{
"_id": "002",
"name": "Name 3",
"description": "Long description 3"
},
{
"_id": "003",
"name": "Name 4",
"description": "Long description 4",
"var_color": {
"_id": "12342",
"color": {
"primary": "red",
"secondary": "rid"
}
}
}
],
"categories": []
}
// main.js
async function getData(product) {
let response = await fetch(`./api/products/${product}`);
let data = await response.json();
return data;
}
getData('products.json').then(data => {
for (let i in data.products) {
let all = data.products[i];
let name = all.name;
let description = all.description;
console.log(name);
console.log(description);
for (let j in all) {
let variantColor = all.var_color[j].primary;
console.log(variantColor);
}
}
});
This is my current script as of the moment.
all.var_color does not exist in each entry, therefore you need to check for its presence.
You are treating all.var_color[j] as if it were an array, but it is an object.
To get the primary color, replace the inner loop (for (let j in all)) with a simple test:
if(all.var_color) {
let variantColor = all.var_color.color.primary;
console.log(variantColor);
}
var data = {
"products": [
{
"_id": "000",
"name": "Name 1",
"description": "Long description 1"
},
{
"_id": "001",
"name": "Name 2",
"description": "Long description 2",
"var_color": {
"_id": "12341",
"color": {
"primary": "pink",
"secondary": "penk"
}
}
},
{
"_id": "002",
"name": "Name 3",
"description": "Long description 3"
},
{
"_id": "003",
"name": "Name 4",
"description": "Long description 4",
"var_color": {
"_id": "12342",
"color": {
"primary": "red",
"secondary": "rid"
}
}
}
],
"categories": []
};
for (let i in data.products) {
let all = data.products[i];
let name = all.name;
let description = all.description;
console.log(name);
console.log(description);
if(all.var_color) {
let variantColor = all.var_color.color.primary;
console.log(variantColor);
}
}
Related
How would you group an array by a property of the objects in a nested array. Either vanilla or lodash.
For example, how would you group an array of tasks by the assignee, when tasks can have more than one person assigned.
Example data:
[
{
"assignees": [
{
"name": "John",
"uid": "id0001"
},
{
"name": "Sally",
"uid": "id0002"
}
],
"title": "Task 1",
"status": "To do"
},
{
"assignees": [
{
"name": "John",
"uid": "id0001"
}
],
"title": "Task 2",
"status": "To do"
},
{
"assignees": [
{
"name": "Sally",
"uid": "id0002"
}
],
"title": "Task 3",
"status": "To do"
}
]
Example desired result:
{
"id0001": {
"name": "John",
"tasks": [
{
"title": "Task 1",
"status": "To do"
},
{
"title": "Task 2",
"status": "To do"
}
]
}
},
{
"id0002": {
"name": "Sally",
"tasks": [
{
"title": "Task 1",
"status": "To do"
},
{
"title": "Task 3",
"status": "To do"
}
]
}
}
I have a lot of things both vanilla and lodash - a simple example is below. I was expecting an array of three groups. One for Sally, one for John and one for Sally and John:
const grouped = _.groupBy(taskList, (task) => task.assignees);
But that returns one group [object Object] with all the tasks in it.
Since uids are unique, the Array seems like an overhead.
I suggest to get an Object literal with the tasks grouped by the user's uids: like:
{
id0001: {...user, tasks: []},
id0002: {...user, tasks: []},
}
Example:
const groupTasksByUserId = (arr) => arr.reduce((ob, {assignees, ...task}) => {
assignees?.forEach((user) => {
ob[user.uid] ??= {...user, tasks: []};
ob[user.uid].tasks.push(task)
});
return ob;
}, {});
const data = [{
"assignees": [{"name": "John", "uid": "id0001"}, {"name": "Sally","uid": "id0002"}],
"title": "Task 1",
"status": "To do"
}, {
"assignees": [{"name": "John", "uid": "id0001"}],
"title": "Task 2",
"status": "To do"
}, {
"assignees": [{"name": "Sally", "uid": "id0002"}],
"title": "Task 3",
"status": "To do"
}];
const res = groupTasksByUserId(data);
// console.log(res);
console.log(JSON.stringify(res, 0, 2));
Find out more about the above used Array methods (reduce and forEach) on MDN Docs
I'm a bit new with javascript. Is there a way to find all icon and get their value and then replace their values with a new one?
I need to replace all of fa/FaCopy with fcb/FcbCopy in the json payload below. Any libraries or functions you can share?
[
{
"section": "feature-list",
"data": {
"title": "Title here",
"body": "body here",
"features": [
{
"title": "Title here 1",
"image": "body here",
"icon": "fa/FaCopy"
},
{
"title": "Title here 2",
"image": "body here",
"icon": "fa/FaCopy"
},
{
"title": "Title here 3",
"image": "body here",
"icon": "fa/FaCopy"
}
]
}
},
{
"section": "title-list",
"data": {
"title": "Title here",
"titles": {
"list": [
{
"title": "Title here 1",
"icon": "fa/FaCopy"
},
{
"title": "Title here 2",
"icon": "fa/FaCopy"
},
{
"title": "Title here 3",
"icon": "fa/FaCopy"
}
]
}
}
}
]
This will be very customized solution and deeply depended on your data.
If your data type will not change and remain same then you could use/modify something like this.
// DATA = Your array
const result = DATA.map((list) => ({
...list,
data: {
...list.data,
features: list.data.features?.map((feature) => ({
...feature,
icon: feature.icon === 'fa/FaCopy' ? 'fcb/FcbCopy' : feature.icon
})),
},
}));
console.log(result); // your expected result
I have an array like this
[
{
"id": 1,
"name": "Personal Information",
"TabFields": [
{
"name": "First Name",
"field": {
"code": "personFirstName"
}
},
{
"name": "Gender",
"field": {
"code": "personGenderD"
}
},
{
"name": "Last Name",
"field": {
"code": "personLastName"
}
},
{
"name": "Mobile Number",
"field": {
"code": "mobileNumber"
}
},
{
"name": "Email Address",
"field": {
"code": "emailAddress"
}
}
]
}
]
What I need is to group the objects inside the TabFields to their respective TAB (PERSONAL_INFORMATION, CONTACT_DETAILS) by code value inside the field object
The object
"name": "First Name",
"field": {
"code": "personFirstName"
}
"name": "Gender",
"field": {
"code": "personGenderD"
}
"name": "Last Name",
"field": {
"code": "personLastName"
}
is belong to PERSONAL_INFORMATION and the object
"name": "Mobile Number",
"field": {
"code": "mobileNumber"
}
"name": "Email Address",
"field": {
"code": "emailAddress"
}
is belong to CONTACT_DETAILS. So the output would be
[
{
"id": 1,
"name": "Personal Information",
"TabFields": [
{
"label": "PERSONAL_INFORMATION",
"code": "PERSONAL_INFORMATION",
"fields": [
{
"name": "First Name",
"field": {
"code": "personFirstName"
}
},
{
"name": "Gender",
"field": {
"code": "personGenderD"
}
},
{
"name": "Last Name",
"field": {
"code": "personLastName"
}
}
]
},
{
"label": "CONTACT_DETAILS",
"code": "PERSONAL_INFORMATION",
"fields": [
{
"name": "Mobile Number",
"field": {
"code": "mobileNumber"
}
},
{
"name": "Email Address",
"field": {
"code": "emailAddress"
}
}
]
}
]
}
]
How to do it in javascript?
You can also do this with map:
var arr=[ { "id": 1, "name": "Personal Information", "TabFields": [ { "name": "First Name", "field": { "code": "personFirstName" } }, { "name": "Gender", "field": { "code": "personGenderD" } }, { "name": "Last Name", "field": { "code": "personLastName" } }, { "name": "Mobile Number", "field": { "code": "mobileNumber" } }, { "name": "Email Address", "field": { "code": "emailAddress" } } ] }];
personalContact = ["Mobile Number", "Email Address"];
result = arr.map(val=>{
personalInfo = { label:'personal', code:val.name, fields:val.TabFields.filter(k=>!personalContact.includes(k.name))};
contactInfo = { label:'contact', code:val.name, fields:val.TabFields.filter(k=>personalContact.includes(k.name))};
val.TabFields = [personalInfo, contactInfo];
return val;
});
console.log(result);
Idea would be to have an array which should distinguish between the contact details and personal details using which you can apply filter to get the data.
You can express transformations simply with a library I created, rubico.
const { pipe, fork, assign, get, map, filter } = require('rubico')
const PERSONAL_INFORMATION_FIELDS = new Set(['First Name', 'Gender', 'Last Name'])
const CONTACT_DETAILS_FIELDS = new Set(['Mobile Number', 'Email Address'])
// [datum] => [datum_with_grouped_TabFields]
const groupTabFields = assign({ // reassign TabFields to new grouped TabFields
TabFields: fork([
fork({
label: () => 'PERSONAL_INFORMATION',
code: () => 'PERSONAL_INFORMATION',
fields: pipe([
get('TabFields'), // datum => datum.TabFields
filter(pipe([ // FILTER: for each TabField of TabFields
get('name'), // TabField => TabField.name
field => PERSONAL_INFORMATION_FIELDS.has(field), // check if PERSONAL_INFORMATION_FIELDS has name
])),
]),
}),
fork({ // same as above but with contact details
label: () => 'CONTACT_DETAILS',
code: () => 'CONTACT_DETAILS',
fields: pipe([
get('TabFields'),
filter(pipe([
get('name'),
field => CONTACT_DETAILS_FIELDS.has(field)
])),
]),
}),
]),
})
x = map(groupTabFields)(data)
console.log(JSON.stringify(x, null, 2)) // output is what you wanted
I've added some comments, but for a deeper understanding of the library and code I've given you, I recommend reading the intuition and then reading the docs
I would like to find number of items which has specific text using js filter method.
var num =
[
{
"name": "name1 ",
"category": "test"
},
{
"name": " name2",
"category": "test2"
},
{
"name": "name3",
"category": "cat3"
},
{
"name": "name 4",
"category": "cat 4"
}
];
num is an object;
Now, i want to find number of categories which has text 'cat'. So i want the result 2. How to get that using filter method.
here's how you can do it
var num =
[
{
"name": "name1 ",
"category": "test"
},
{
"name": " name2",
"category": "test2"
},
{
"name": "name3",
"category": "cat3"
},
{
"name": "name 4",
"category": "cat 4"
}
];
console.log( num.filter(i => i.category.indexOf("cat") === 0).length )
num is an object;
True, but specifically it's an array object.
You could use filter for this, but reduce would be the more appropriate option if you don't want the array of matching results:
var result = num.reduce(function(sum, entry) => sum + (entry.category.includes("cat") ? 1 : 0), 0);
Live Example:
var num =
[
{
"name": "name1 ",
"category": "test"
},
{
"name": " name2",
"category": "test2"
},
{
"name": "name3",
"category": "cat3"
},
{
"name": "name 4",
"category": "cat 4"
}
];
var result = num.reduce(function(sum, entry) {
return sum + (entry.category.includes("cat") ? 1 : 0);
}, 0);
console.log(result);
Or with ES2015+:
const num =
[
{
"name": "name1 ",
"category": "test"
},
{
"name": " name2",
"category": "test2"
},
{
"name": "name3",
"category": "cat3"
},
{
"name": "name 4",
"category": "cat 4"
}
];
const result = num.reduce((sum, entry) => sum + (entry.category.includes("cat") ? 1 : 0), 0);
console.log(result);
Or of course, a simple for loop.
This is fairly simple. In ES6 the solution would be this:
const countOfCats = num.filter(entry => entry.category.match('cat')).length;
Another way could be:
const countOfCats = num.filter(entry => entry.contains("cat")).length;
I have some data and I need a loop which creates 2 arrays...
So I first create the 2 arrays:
namelist = [];
countList = [];
{
"id": "622",
"name": "main",
"sub": {
"637": {
"id": "637",
"name": "name 1",
"stats": {
"count": 5
}
},
"638": {
"id": "638",
"name": "name 2",
"stats": {
"count": 10
}
}
}
}
The desired result for this example would be:
For namelist:
['name 1', 'name 2']
For countList:
[5, 10]
How can I do this?
var nameList = [];
var countList = [];
var myObj =
{
"id": "622",
"name": "main",
"sub": {
"637": {
"id": "637",
"name": "name 1",
"stats": {
"count": 5
}
},
"638": {
"id": "638",
"name": "name 2",
"stats": {
"count": 10
}
}
}
};
for(var key in myObj.sub){
nameList.push(myObj.sub[key].name);
countList.push(myObj.sub[key].stats.count);
}
console.log(nameList);
console.log(countList);
for(var key in obj.sub){
nameList.push(obj.sub[key].name);
countList.push(obj.sub[key].stats.count;
}
Object.keys may help you to walk through object properties. Example related to your object:
var namelist = [],
countList = [],
obj = {
"id": "622",
"name": "main",
"sub": {
"637": {
"id": "637",
"name": "name 1",
"stats": {
"count": 5
}
},
"638": {
"id": "638",
"name": "name 2",
"stats": {
"count": 10
}
}
}
};
Object.keys(obj.sub).forEach(function(item) {
namelist.push(obj.sub[item].name);
countList.push(obj.sub[item].stats.count);
});
console.log(namelist, countList);
Working example: https://jsfiddle.net/ry0zqweL/
Obviously, you can optimise it in many ways. It's just illustrating one of the many solutions.