I want to add a new object for each nested array. I'm calling this function any time I add a product to my orderintake:
add2order(productID, productName, productRatePlans) {
this.orderIntake.push({ productID, productName, productRatePlans });
let i = 0;
this.orderIntake[0].productRatePlans[0].productRatePlanCharges.forEach(element => {
i++;
this.orderIntake[0].productRatePlans[0].productRatePlanCharges[
i
].quantity = this.orderIntake[0].productRatePlans[0].productRatePlanCharges[
i
].defaultQuantity;
});
}
this is an example response from the server:
{
"id": "8adc8f996928b9a4016929c59b943a8f",
"sku": "SKU-00006778",
"Partner_Account_ID__c": null,
"productRatePlans": [
{
"id": "8adce4216928c28d016929c59bff3372",
"status": "Active",
"name": "Enterprise",
"description": null,
"effectiveStartDate": "2016-02-26",
"effectiveEndDate": "2029-02-26",
"productRatePlanCharges": [
{
"id": "8adc8f996928b9a4016929c59d183a92",
"name": "USAGE_COUNTER_2",
"type": "Usage",
"model": "Volume",
"uom": "Each",
"pricingSummary": [
"Up to 5000 Each: USD0 flat fee"
],
"pricing": [
{
...
}
],
"defaultQuantity": null,
"applyDiscountTo": null,
"discountLevel": null,
"discountClass": null,
...
"financeInformation": {
..,
}
}
]
}
],
"productFeatures": [
{
...
}
]
}
The data is being retrived this way from an external REST backend so unfortunately I can't initialize the data including the new property...
so in every productRatePlanCharges there should be 1 new object 'quantity'.
How can I add this field to every productRatePlanCharges?
Right now I'm getting: ERROR
TypeError: Cannot read property 'productRatePlanCharges' of undefined
And how can I make sure I'm always adding this to the last orderIntake element? Don't mind productRatePlans there is only 1 in each orderintake...
thanks for your support!
Here you have to create productDetails object with inititalised array like below so that you won't get the error.
add2order(productID, productName, productRatePlans) {
// Create object like below
let productDetails = { productID : productID, productName : productName, productRatePlans : productRatePlans
}
this.orderIntake.push(productDetails);
let i = 0;
this.orderIntake[0].productRatePlans[0].productRatePlanCharges.forEach(element => {
i++;
this.orderIntake[0].productRatePlans[0].productRatePlanCharges[
i
].quantity = this.orderIntake[0].productRatePlans[0].productRatePlanCharges[
i
].defaultQuantity;
});
}
Hope this will help!
as you used Angular you probably use Typescript too. I recommend that you create a model like your incoming model and there define your quantity: number inside productRatePlanCharges object. then map the incoming data to your own model. therefore you will have a quantity=0 in your model that you can change it later in a loop.
If you want to continue with your own way take a look at this:
Add new attribute (element) to JSON object using JavaScript
there is no problem to add an element to current model almost like you did, and the problem might be somewhere else as your error refers to existence of productRatePlanCharges!
as you used forEach I prefer to use that 'element' and double iterating with i++; is not a good idea to me.
this might be better:
element.quantity = element.defaultQuantity;
Related
I want to update a single Object in my localStorage. I made a detail page, where I can submit new values (progress and value)
When I want to update the value, it changes the value in both objects. How can I change just one object.
Here is my deployment link.(its work in progress)
https://mastery-app.herokuapp.com/
This is my localStorage array:
skills[
{
"title": "Sewing",
"imageSrc": "images.unsplash.com",
"description": "Make your own clothes",
"category": "crafting",
"progress": 500,
"isDone": false,
"rank": 0,
"value": 0
},
{
"title": "Crocheting",
"imageSrc": "images.unsplash.com",
"description": "Interlock loops of yarn",
"category": "crafting",
"progress": 500,
"isDone": false,
"rank": 0,
"value": 0
}
]
This is how I update the localStorage:
const update = skills.map((skills) => {
skills.title === skills.title;
const updateProgress = skills.progress - value;
const rankNumber = parseInt(ranking);
const updateRank = skills.rank + rankNumber;
console.log(updateRank);
const updateValue = skills.value + value;
return {
title: skills.title,
rank: updateRank,
description: skills.description,
progress: updateProgress.toFixed(1),
imageSrc: skills.imageSrc,
category: skills.category,
isDone: false,
value: updateValue,
};
});
localStorage.setItem('skills', JSON.stringify(update));
You may consider using the find method to find the object you want to update. map is not the right function to be used for your use case.
Also skills.title === skills.title; has no effect at all (Maybe you wanted to use an if statement to do some kind of filtering by using title but that always would return true). Please remove that.
Now, I don't exactly know which field are you going to use to search for the object you want to update, but it has to be unique. If none of the fields in the objects are unique you should consider adding an unique id field in the skills objects. But if title is unique you can use the title to search. Then you can do something like the pseudo code below:
const toUpdate = skills.find(skill => skill.title === your_title_here)
toUpdate.field_to_update_1 = some_value_1
toUpdate.field_to_update_2 = some_value_2
localStorage.setItem('skills', JSON.stringify(skills))
Please also check the MDN docs to see how map, find and other array methods work and some of their use cases.
EDIT: Re-structured question, cleaer, and cleaner:
I have a data object from Sequelize that is sent by node-express:
{
"page": 0,
"limit": 10,
"total": 4,
"data": [
{
"id": 1,
"title": "movies",
"isActive": true,
"createdAt": "2020-05-30T19:26:04.000Z",
"updatedAt": "2020-05-30T19:26:04.000Z",
"questions": [
{
"questionsCount": 4
}
]
}
]
}
The BIG question is, how do I get the value of questionsCount?
The PROBLEM is, I just can't extract it, these two methods give me undefined result:
category.questions[0].questionsCount
category.questions[0]['questionsCount']
I WAS ABLE to get it using toJSON() (From Sequelize lib I think), like so:
category.questions[0].toJSON().questionsCount
But I'd like to know the answer to the question, or at least a clear explanation of why do I have to use toJSON() just to get the questionsCount?
More context:
I have this GET in my controller:
exports.getCategories = (req, res) => {
const page = myUtil.parser.tryParseInt(req.query.page, 0)
const limit = myUtil.parser.tryParseInt(req.query.limit, 10)
db.Category.findAndCountAll({
where: {},
include: [
{
model: db.Question,
as: "questions",
attributes: [[db.Sequelize.fn('COUNT', 'id'), 'questionsCount']]
}
],
offset: limit * page,
limit: limit,
order: [["id", "ASC"]],
})
.then(data => {
data.rows.forEach(function(category) {
console.log("------ May 31 ----> " + JSON.stringify(category.questions[0]) + " -->" + category.questions[0].hasOwnProperty('questionsCount'))
console.log(JSON.stringify(category))
console.log(category.questions[0].toJSON().questionsCount)
})
res.json(myUtil.response.paging(data, page, limit))
})
.catch(err => {
console.log("Error get categories: " + err.message)
res.status(500).send({
message: "An error has occured while retrieving data."
})
})
}
I loop through the data.rows to get each category object.
The console.log outputs are:
------ May 31 ----> {"questionsCount":4} -->false
{"id":1,"title":"movies","isActive":true,"createdAt":"2020-05-30T19:26:04.000Z","updatedAt":"2020-05-30T19:26:04.000Z","questions":[{"questionsCount":4}]}
4
https://github.com/sequelize/sequelize/blob/master/docs/manual/core-concepts/model-querying-finders.md
By default, the results of all finder methods are instances of the model class (as opposed to being just plain JavaScript objects). This means that after the database returns the results, Sequelize automatically wraps everything in proper instance objects. In a few cases, when there are too many results, this wrapping can be inefficient. To disable this wrapping and receive a plain response instead, pass { raw: true } as an option to the finder method.
(emphasis by me)
Or directly in the source code, https://github.com/sequelize/sequelize/blob/59b8a7bfa018b94ccfa6e30e1040de91d1e3d3dd/lib/model.js#L2028
#returns {Promise<{count: number, rows: Model[]}>}
So the thing is that you get an array of Model objects which you could navigate with their get() method. It's an unfortunate coincidence that you expected an array, and got an array so you thought it is "that" array. Try the {raw:true} thing, I guess it looks something like this:
db.Category.findAndCountAll({
where: {},
include: [
{
model: db.Question,
as: "questions",
attributes: [[db.Sequelize.fn('COUNT', 'id'), 'questionsCount']]
}
],
offset: limit * page,
limit: limit,
order: [["id", "ASC"]],
raw: true // <--- hopefully it is this simple
}) [...]
toJSON() is nearby too, https://github.com/sequelize/sequelize/blob/59b8a7bfa018b94ccfa6e30e1040de91d1e3d3dd/lib/model.js#L4341
/**
* Convert the instance to a JSON representation.
* Proxies to calling `get` with no keys.
* This means get all values gotten from the DB, and apply all custom getters.
*
* #see
* {#link Model#get}
*
* #returns {object}
*/
toJSON() {
return _.cloneDeep(
this.get({
plain: true
})
);
}
So it worked exactly because it did what you needed, removed the get() stuff and provided an actual JavaScript object matching your structure (POJSO? - sorry, I could not resist). I rarely use it and thus always forget, but the key background "trick" is that a bit contrary to its name, toJSON() is not expected to create the actual JSON string, but to provide a replacement object which still gets stringified by JSON.stringify(). (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON_behavior)
try to do so category.data[0].questions.questionCount
As mentioned by others already, you need category.data[0].questions[0].questionCount.
Let me add to that by showing you why. Look at your object, I annotated it with how each part would be accessed:
category = { // category
"page": 0,
"limit": 10,
"total": 2,
"data": [ // category.data
{ // category.data[0]
"id": 1,
"title": "movies",
"createdAt": "2020-05-30T19:26:04.000Z",
"updatedAt": "2020-05-30T19:26:04.000Z",
"questions": [ // category.data[0].questions
{ // category.data[0].questions[0]
"questionCount": 2 // category.data[0].questions[0].questionCount
}
],
"questionsCount": "newValue here!"
}
]
}
try this
category.data[0].questions[0].questionCount
the reason why you have to use toJSON is because it's sometimes it is used to customise the stringification behavior. like doing some calculation before assinging the value to the object that will be returned , so it is most likley been used here to calculate the "numb of questions and then return an object with the property questionscount and the number calculated
so the object you retreived more or less looks like this
var cathegory = {
data: 'data',
questions:[{
// some calulation here to get the questionsCount
result=4,
toJSON () {
return {"questionsCount":this.result}
}
}
]
};
console.log(cathegory.questions[0].toJSON().questionsCount) //4
console.log(JSON.stringify(cathegory)) // {"data":"data","questions":[{"questionsCount":4}]}
console.log("------ May 31 ----> " + JSON.stringify(cathegory.questions[0]) + " -->" + cathegory.questions[0].hasOwnProperty('questionsCount')) //false
I am in Angular environment using Kendo. All I want to do is following:
Take Json
Produce Kendo tree using it
I have tried it with simple data and it seems to work fine. But this time I have somewhat complex data and it seems like it does not work well with complex Json. I have been trying to have it render Json but it seems like it keeps on thinking and never comes back. I have created a sample Dojo for reference:
http://dojo.telerik.com/EdOqE
I am not sure what am I doing wrong but it just does not seem to work. Can anyone help me with this please?
I presume you have controll over the resultant json, because you'll have to change it a little to fit the TreeView's expected format. Check this out:
{
"items": [{ // Projects
"Id": 0,
"Name": "Your Example Project",
"CreatedOn": "",
"hasChildren": true,
"items": [{ // Analyses
"Id": 0,
"Name": "1.0 - Your Example Run",
"CreatedOn": "",
"hasChildren": true,
"items": [{ // Samples
"Id": 0,
"Name": "Sample 1",
"hasChildren": false,
"Description": "ample frample sample"
}, {
"Id": 0,
"Name": "Sample 2",
"hasChildren": false,
"Description": null
}]
}]
}]
};
The above json is what I did to work in the widget. First of all, the collection properties were renamed to items. All of them, in all levels. With that, kendo will know how property it should deal with. A hasChildren property was added to let it know when it has to show the expand icon. Otherwise it will show the expand option even if the item doesn't haves any children. So user clicks it and get an empty result.
This is the widget initialization options:
{
dataSource: new kendo.data.HierarchicalDataSource({
data: things,
schema: {
data: "items"
}
}),
dataTextField: "Name"
};
With schema.data I tell which property kendo will deal as the collection item. The dataSource expects an array, but if you give him an object, you have to set this property. If it was an array, then kendo would look for item property of each child for default. dataTextField is the name of the property it will use as the label.
Demo
Here is another demo with the data as an array. No need to set schema.data.
Update:
I was afraid you would say that. Yes, there is a way to deal with the data if you can't change it in the server-side. You have to intercept the data at the schema.parse() method and change the resultant data object property to items, so then the widget will understand:
schema: {
data: "items",
parse: function(data) {
if (data.hasOwnProperty("Projects")) {
return { items: data.Projects };
}
else if (data.hasOwnProperty("Analyses")) {
return { items: data.Analyses };
}
else if (data.hasOwnProperty("Samples")) {
return { items: data.Samples };
}
}
}
Demo
Every node when opened will call parse with items collection as data parameter. You have to return a new object with the property name as items instead of Projects, Analysis or Samples.
I forgot you can't touch the data, so can't add hasChildren property as well. Then you have to add a tiny logic into parse to set those properties in each level, otherwise the expand icon would not appear:
schema: {
data: "items",
parse: function(data) {
if (data.hasOwnProperty("Projects")) {
data.Projects.forEach(p => {
p.hasChildren = false;
if (p.hasOwnProperty("Analyses")) {
p.hasChildren = true;
}
});
return { items: data.Projects };
}
else if (data.hasOwnProperty("Analyses")) {
data.Analyses.forEach(a => {
a.hasChildren = false;
if (a.hasOwnProperty("Samples")) {
a.hasChildren = true;
}
});
return { items: data.Analyses };
}
else if (data.hasOwnProperty("Samples")) {
return { items: data.Samples };
}
}
}
Demo
It is ugly, I know. But get used to Kendo, it is the it goes with it.
I've got the following document named "clients" which includes id, name and list of projects (array of objects):
{
"_id": {
"$oid": "572225d997bb651819f379f7"
},
"name": "ppg",
"projects": [
{
"name": "aaa",
"job_description": "",
"projectID": 20
},
{
"name": "bbbb",
"job_description": "",
"projectID": 21
}
]
}
I would like to update "job_description" of project with given "projectID" like this:
module.exports.saveJobDesc = function(client, idOfProject, textProvided) {
db.clients.update({ name: client},
{ $set: {'projects.0.job_description': textProvided }});
};
But instead of hardcoded index "0" of array I want to find specific project using "projectID". Is there a way to achieve this without changing the structure of collection and/or document?
If you want to update the "job_description" where name="ppg" and project_id=20 then you can use below mongo query:-
db.clients.update({ "name":"ppg","projects.projectID":20 },{$set: {"projects.$.job_description": "abcd"}})
Please let me know if any thing else is required
You cannot update multiple array elements in single update operation, instead you can update one by one which takes time depends upon number of elements in array and number of such documents in collection. see New operator to update all matching items in an array
db.test2.find().forEach( function(doc) {
var projects = doc.projects;
for(var i=0;i<projects.length;i++){
var project = projects[i];
if(project.projectID == 20){
var field = "projects."+i+".job_description";
var query = {};
query[field] = "textasdsd";
db.test2.update({ _id: doc._id},{ $set:query});
}
}
})
I am on a Dojo app, is there any special JS parser which enables me default values within JSON data ?
Example, I'd like to set "maxHeadingLength" in item[1] to the value in item[0].
{
items:[
{
"class": "pmedia.types.PMPlatformRenderConfiguration",
"listItemHeight": "70",
"listItemIconHeight": "60",
"maxDesktopItemsWithBannerP": "9",
"maxDesktopItemsWithBannerL": "9",
"platform": "Default",
"maxHeadingLength":
{
P:300,
L:400
}
},
{
"class": "pmedia.types.PMPlatformRenderConfiguration",
"listItemHeight": "70",
"listItemIconHeight": "60",
"platform": "IPAD",
"maxHeadingLength": "Default"
},
Something like this would good too :
"maxHeadingLength": "this.items[0].maxHeadingLength"
Thanks
Update, It seems I can and need to precise the question a bit better now.
I'd like to write any string expression into the pure JSON string data(file), as string of course and evaluate the data within the same data set without using "eval".
Thanks to all !
You can either do this in a backend process or using a front end process as you process the JSON.
Let's assume you're wanting to do it in the front end using JS, I would suggest firstly setting the items[0].maxHeadingLength as a var, like so:
var defaultMaxHeadingLength = items[0].maxHeadingLength;
And then as you loop through your JSON you can check whether the maxHeadingLength attribute has a value or not and default it, like so:
var item, i;
for ( i = 0; i < items.length; i++ )
{
item = items[ i ];
item.maxHeadingLength = item.maxHeadingLength ? item.maxHeadingLength : defaultMaxHeadingLength;
}
Here's an idea of one way to do it (if I understand what you're trying to do):
var defaultItem = {
"maxHeadingLength": {
P: 300,
L: 400
}
};
var jsondata = {
items: [
{
"class": "pmedia.types.PMPlatformRenderConfiguration",
"listItemHeight": "70",
"listItemIconHeight": "60",
"platform": "IPAD",
"maxHeadingLength": this.defaultItem.maxHeadingLength
}
]
};
alert(jsondata.items[0].maxHeadingLength.L);
//returns 400
Basically you have a default item separate to all your other items, allowing you to refer to the defaults in the main data.
I don't think you can use this.jsondata from within the same object, since it hasn't finished being created yet (but I could be wrong).