I am exporting a variable which is an array from file1.js:
// file1.js
const myArray = [
{
"name": "Judah Hendricks",
"email": "diam#lobortis.net",
"hobbies": [
{
"sports": "tennis",
"recent": true
},
{
"sports": "football",
"recent": false
},
{
"sports": "volleyball",
"recent": false
}
]
},
{
"name": "Jakeem Olsen",
"email": "at#tellus.org",
"hobbies": [
{
"sports": "swimming",
"recent": false
},
{
"sports": "running",
"recent": true
}
]
}
];
module.exports = { myArray };
and I am using it inside a route:
//main.js
const { myArray } = require('./file1.js');
router.get('/getData', (req, res) => {
dataBase.users.findOne({ email: req.user.email }, function(error, data){
if (data){
const myDataClone = [...myArray];
let newData = [];
newData = myDataClone.map( dt => {
dt.oldHobbies = []
for (let i = 0; i < dt.hobbies.length; i++) { // Cannot read property 'length' of undefined
if (dt.hobbies[i].recent) {
dt.recentHobby = dt.hobbies[i];
} else {
dt.oldHobbies.push(dt.hobbies[i]);
}
}
delete dt.hobbies;
return dt;
})
return res.status(200).json({userInfo: newData})
} else {
// do another
}
});
}
when I hit this route for the first time, it returns the expected data. my problem is when I revisit this route for the second time, I get Cannot read property 'length' of undefined for the dt.hobbies .
Could somebody help me understand what is wrong here and how to fix it?
The problem is here:
const myDataClone = [...myArray];
You're only shallow cloning the array, not deep cloning it; although the myDataClone array is different from myArray, the objects inside both arrays refer to the same objects in memory. So, when you
delete dt.hobbies;
the original object's hobbies property gets deleted as well, which means when you try to access the hobbies property of the object later, errors are thrown.
Fix it by making a deep clone at the beginning instead:
const myDataClone = JSON.parse(JSON.stringify(myArray));
As mentioned here, JSON.parse(JSON.stringify()) is probably the quickest way to deep clone objects. There are other methods like adding jQuery as a dependancy and using jQuery.extend, or writing your own custom cloning function, but serializing and deserializing works just fine.
Related
So I am having trouble figuring out how to create an array with two objects, looping through my object and adding some values to those objects in Javascript. Currently I have the following mock response:
const mockResponse =
{
"errors": [
{
"errorKey": "ERROR_NO_DELIVERY_OPTIONS",
"errorParameters": [
{
"errorMessage": "ERROR_DELIVERY_OPTIONS_YOU_SELECTED_NOT_AVAILABLE_NOW",
"partNumbers": [
19308033,
19114798
]
},
{
"errorMessage": "Ship to Home not available for these orderItemId",
"orderItemIds": [
10315031,
10315032
],
"availableShipModeId": 13203
},
{
"errorMessage": "Pickup At Seller not available for these orderItemIds",
"orderItemIds": [
10222222,
10333333
],
"availableShipModeId": 13203
}
],
"errorMessage": "ERROR_NO_DELIVERY_OPTIONS",
"errorCode": "ERROR_NO_DELIVERY_OPTIONS"
}
]
}
I would like to have an array with two objects. One for the first error message("Ship to home...") and another for the second error message("Pickup at Seller..."). I would like to then loop through the JSON and add each "orderItemIds" to there respective object. For example, 10315031,10315032 would go to the first object and 10222222, 10333333 to the second.
You can use reduce to loop through your errors and use the errorMessage property as a key
const result = mockResponse.errors[0].errorParameters.reduce((prev, item) => {
const { errorMessage, orderItemIds } = item;
if (prev[errorMessage]) {
prev[errorMessage] = [...prev[errorMessage], ...orderItemsIds];
} else {
prev[errorMessage] = orderItemIds
}
return prev
}, {})
Let me know if this does answer your question
I have an array of objects where the value I need to filter on is buried in a long string. Array looks like:
{
"data": {
"value": "{\"cols\":[\"parent_sku\"],\"label\":\"Style\",\"description\":\"Enter Style.\",\"placeholderText\":\"Style 10110120103\"}",
"partnerId": 1
}
},
So if I wanted to grab all the partnerId objects where value includes parent_sku how would I do that?
console.log(data.value.includes('parent_sku') returns cannot read property 'includes' of null.
EDIT:
Didn't think this mattered, but judging by responses, seems it does. Here's the full response object:
Response body: {
"data": {
"configurationByCode": [
{
"data": {
"value": "{\"cols\":[\"parent_sku\"],\"label\":\"Style\",\"description\":\"Enter Style.\",\"placeholderText\":\"Style 10110120103\"}",
"partnerId": 1
}
}
I'm passing that into a re-usable function for filtering arrays:
const parentSkuPartners = filterArray(res.body.data.configurationByCode, 'parent_sku');
Function:
function filterArray(array, filterList) {
const newList = [];
for (let i = 0; i < array.length; i += 1) {
console.log('LOG', array[i].data.value.includes('parent_sku');
}
}
The problem is somewhere else. The code you've tried should work to find if a value contains a string – I've added it the snippet below and you'll see it works.
The issue is how you are accessing data and data.value. The error message clearly states that it believes that data.value is null. We would need to see the code around it to be able to figure out what the problem is. Try just logging to console the value of data before you run the includes function.
const data = {
"value": "{\"cols\":[\"parent_sku\"],\"label\":\"Style\",\"description\":\"Enter Style.\",\"placeholderText\":\"Style 10110120103\"}", "partnerId": 1
};
console.log('includes?', data.value.includes('parent_sku'));
You can use data.value.includes('parent_sku') as you have suggested. The issue here is that your object is nested inside an unnamed object.
try:
"data": {
"value": "{\"cols\":[\"parent_sku\"],\"label\":\"Style\",\"description\":\"Enter Style.\",\"placeholderText\":\"Style 10110120103\"}",
"partnerId": 1
}
The problem was some of the values for value were null. Adding an extra conditional fixed it:
if (array[i].data.value !== null) {
Use lodash includes, and lodash filter like
let configurationByCode = [{
data: {
value: {
cols:["parent_sku"],
label:"Style",
description:"Enter Style.",
placeholderText:"Style 10110120103"
},
"partnerId": 1
}
}, {
data: {
value: {
cols:["nothing"],
label:"Style",
description:"Enter Style.",
placeholderText:"Style 10110120103"
},
"partnerId": 2
}
}];
let wantedData = _.filter(configurationByCode, (config) => {
return _.includes(config.data.value.cols, 'parent_sku');
});
console.log( wantedData );
https://jsfiddle.net/76cndsp2/
I'm new to Node and Firebase.
I'm currently working on a crafting calculator for a game and have the game items stored in Firebase. Some items are composite items, for example:
1 Lumber Plank = 5 Logs
Based on such requirements, I've structured all the items as a single collection titled as items in Firebase.
Log would be persisted as:
{
"type": "basic",
"name": "log"
}
While lumber plank would be:
{
"type": "composite",
"name": "lumber plank",
"materials": ["log"],
"material_values": [5]
}
With such a structure, I'm trying to construct a crafting tree by recursively searching through the database. A final structure would look as such:
{
"name": "board",
"count": 1,
"materials": [
{
"name": "lumber plank",
"count": 1,
"materials": [
{
"name": "log",
"count": 5,
"materials": null
}
]
}
]
}
I'm having trouble with understanding the callbacks while debugging and this piece of code currently returns undefined followed by log (I'm assuming this comes from the console.log within the search function).
async function search(item, result, count) {
let calcItem = {
name: item,
count: count
};
db.collection("items")
.doc(item)
.get()
.then(doc => {
const data = doc.data();
if (data.type === basic) {
calcItem.materials = null;
result.push(calcItem);
return result;
} else {
let materials = data.materials;
let materialsCount = data.material_values;
calcItem.materials = [];
for (let i = 0; i < materials.length; i++) {
console.log(materials[i]);
search(materials[i], calcItem.materials, materialsCount[i]);
}
}
});
}
let item = "lumber plank";
search(item, [], 1).then(result => console.log(result));
Would appreciate any pointers/tips here. Thanks
Following feedback from Doug,
I've kinda refactored my code based on your comments and I'm seeing some progress.
function recursiveSearch(item, count, result) {
let calcItem = {
name: item,
count: count
};
dbSearch(item).then(function (doc) {
const data = doc.data();
console.log(data);
if (data.type === basic) {
calcItem.materials = null;
result.push(calcItem);
return result;
} else {
let materials = data.materials;
let materialsCount = data.material_values;
calcItem.materials = [];
for (let i = 0; i < materials.length; i++) {
recursiveSearch(materials[i], materialsCount[i], calcItem.materials);
}
}
});
}
function dbSearch(item) {
return Promise.resolve(db.collection("items")
.doc(item)
.get()
.then());
}
Log now outputs the search correctly.
{
material_values: [ 5 ],
materials: [ 'log' ],
name: 'lumber plank',
type: 'composite'
}
{
name: 'log',
type: 'basic'
}
However, if I understand it correctly, if I were to add in this line it's still going to return undefined, am I right?
console.log(recursiveSearch("lumber plank", 1, [])
If so, how do I actually log out the entire item structure whilst completing all the recursive searches?
Sorry if the question sounds kinda dumb. I primarily come from a Java background and dealing with promises/async/await is entirely new to me
You're not dealing with promises correctly. search doesn't actually return a "real" promise, but the caller is expecting it to do so. Since it's async, but doesn't return a value directly, it's actually returning is a promise that always resolves to undefined. It's also apparently intended to be a recursive function, which is making it harder to understand (did you mean to return the promise from the inner call to search?).
Minimally, you should start by making search return the promise chain that it establishes:
return db.collection("item")...get().then()
This will let you receive the value returned by the then callback.
I'll also point out that you're started to use async/await syntax, but never committed to using await to make this code more easy to read, which is a bit confusing.
I have created route.js file. Based on queryString it has to filter the Json object. When I used _.filter method it returns entire object as response. Actually i want filter that productlist node and include the remaining nodes as response
Please Help me .. Thanks in Advance...
Here is the code..
The JSON File
{
"productList": [
{
"productName": "xyz",
"productType": "mobile"
},
{
"productName": "xyz",
"productType": "mobile"
},
{
"productName": "xyz",
"productType": "mobile"
}
],
"totalProducts": 3,
"FilteredProducts": 0,
"test1": 11,
"test11": 12,
"test33": 13
}
route.js
var filterByProduct = function(coll, productType){
return _.forEach(coll, function(o){
return _.find(o, function(item){
});
});
};
var queryString = function(req, res, next) {
if (req.query.productType ) {
var stringObj = JSON.stringify(filterByProduct(jsonFile, req.query.productType),null,4);
res.end(stringObj);
} else if (req.query !== {}) {
var stringObj = JSON.stringify(jsonFile,null,4);
res.end(stringObj);
} else {
res.end('Not a Query String');
}
}
router.get('/test', queryString, function(req,res){
//
});
The problem here is that the filterByProduct parameter coll isn't bound to the productList array but the top-level object that contains the productList array as well as the other nodes. So you should be targeting coll.productList instead.
Also, using _.filter (on coll.productList) as you originally mentioned is a better idea than using _.forEach because it iterates through the array and filters the items. Try this version of filterByProduct instead:
var filterByProduct = function(coll, productType){
return _.filter(coll.productList, function(o) {
return o.productType === productType;
})
};
Finally, to return an object similar to your JSON data file that has the filtered version of productList nodes plus the other top-level nodes, you could use the _.clone method to shallow clone your jsonFile object and then overwrite the productList and FilteredProducts properties with the values returned from your filterByProduct function and the length of the results of filterByProduct, respectively. Here's what I came up with:
if (req.query.productType) {
var stringObj = _.clone(jsonFile);
stringObj.productList = filterByProduct(jsonFile, req.query.productType);
stringObj.FilteredProducts = stringObj.productList.length;
res.end(stringObj);
}
I need to create a dynamic array with a loop.. but I cant seem to get the desired results.
the array I want is:
{
"CategoryName": "somecategoryname",
"Date": "02-17-2012",
"Id": 24,
"ProductToHide": [
{
"IsHide": true,
"ProductId": "someid"
}
],
"ProductsToAdd": [
{
"MealSequence": "S1",
"ProductId": "Someid"
},
{
"MealSequence": "S2",
"ProductId": "Snack_11"
}
],
"UserId": "1"
}
and I am using the following function to add products:
addProduct: function(id){
var tempArr = [];
$.each(this.mCData.ChildCategories, function(i, item){
$.each(item.PList, function(j, jsonPr){
if (jsonPr.TID == id){
addProduct = new mealTypeProduct();
addProduct.data = jsonPr;
tempArr = addProduct.modifyProduct();
}
})
})
// queryStr = {"add" : tempArr};
// this.modificationArray.push(queryStr);
this.modificationArray['add'].push(tempArr);
console.log(this.modificationArray);
}
Its giving me the following error:
this.modificationArray.add.push is not a function
this.modificationArray['add'].push(tempArr);
the initializing is done in the following manner:
var mealType = {
chosenDate: new Date(), tabViewHtml: '',
modificationArray: [], saveArray: [],
}
What am I doing wrong?
This line:
tempArr = addProduct.modifyProduct();
replaces tempArr with a new value, so at the end of the $.each call it will have the value from the last matching product (which may not be an array) rather than being an array of all of them. If that is what you want then that's not a problem.
As to the actual error you quote: what type of thing is this.modificationArray? Does it have a member called add? Does that member have a push() function? Is it initialized properly, or is it left uninitialized? If the latter, then there's your problem: you need to initialize it before you can call push().
UPDATE: If this.modificationArray is initialized as [], as in the new sample code, then it is an array itself; it does not have an add member. The code should say this.modificationArray.push(tempArr).
Alternatively, if you really do want an add member, then this.modificationArray should be initialized as modificationArray : {add:[]}