JS array push error in IE only - javascript

I have the following script, which essentially extracts each value from the "data" array being passed and returns json value.
function parsejson(data) {
var temp2 = new Array();
if (data) {
$.each(data, function (i, val) {
vproductid = data[i].productid;
vproductname = data[i].product_name;
vexpirydt = data[i].expiry;
temp2.push({vproductid,vproductname,vexpirydt});
});
console.log([temp2]);
return [temp2];
}
}
So in this case "data" within my console log comes back as:
Array [ Object, Object ]
Which contains the following objects -> values, etc, and in some instances my "expiry" date value is blank space, but still appears to work properly in Firefox and Chrome.
The issues seems to be exclusively with IE 11 in my case.... I keep getting the following Error in IE only which ties back to the "push" to append to my array somehow. I don't know if it's a syntax error or the way in which I'm trying to append to my array, but obviously I'm doing something wrong. My intention is simply to return a second array in json format, so might be a simpler way.
SCRIPT1003: Expected ':'

Comments provide the answear to IE error, here is a sample code that will work on IE and Chrome:
function parsejson(data) {
var temp2 = []; // this syntax might be prefered to create a new array
if (data) {
temp2 = data.map(function(element) {
return {
vproductid: element.productid,
vproductname: element.product_name,
vexpirydt: element.expiry
};
});
}
console.log(temp2);
return temp2;
}
var sampleData = [{
productid: 1,
product_name: 'a',
expiry: 'Today',
someThingelse: '',
}, {
productid: 2,
product_name: 'b',
expiry: 'Today',
someThingelse: '',
}, {
productid: 3,
product_name: 'c',
expiry: 'Today',
someThingelse: '',
}];
parsejson(sampleData);

Related

Taking an array of Objects and filtering each objects keys by an array of string

I am pulling backend data to my application, but i want the user to be able to select which keys they want to see. So i have been trying to build a way for them to generate an array of strings and have that be compared to each object in the array and output the new array of objects with each key excluded.
Here is the Filter array:
const filterData =
[
'TestOne',
'TestTwo',
]
Here is the array of objects:
const data = [
{
_id: "62ec2f1084c7f48175a9cb4a",
Date: "2022-08-04T15:41:37.567Z",
facilityId: "62e5a9fd45f2646fc7361fa3",
userId: "62e16d390f4685e4fdb6a288",
formData: {
Date: "2022-08-04T15:41:37.567Z",
TestOne: 60002,
TestTwo: 19998,
TestThree: 102,
TestFour: "True"
},
},
{
_id: "62ec2f1c84c7f48175a9cb58",
Date: "2022-08-04T15:41:52.932Z",
facilityId: "62e5a9fd45f2646fc7361fa3",
userId: "62e16d390f4685e4fdb6a288",
formData: {
Date: "2022-08-04T15:41:52.932Z",
TestOne: 60003,
TestTwo: 19997,
TestThree: 103,
TestFour: "True"
},
},
]
I want to build a function that takes the data.formData and filters out any keys that are not included in the filterData. I am having a hard time figuring out what exactly needs done to acheive this. If anyone could help id greatly appreciate it.
----EDIT----
Here is something of a process that i have thought of but it returns errors and cant really think of why.
const formObject =
datas.length > 0 &&
datas.map((data, i) => {
const filterData = ['TestOne', 'TestTwo']
filterData.map((filter) => {
delete data.formData[filter]
})
})
This function gives me errors of Uncaught TypeError: Cannot delete property 'TestThree' of #<Object>, ive tried making a new instance of datas but it doesnt work
I would do it this way:
const result = data.map(x => {
for (const [key, value] of Object.entries(x.formData)) {
if (!filterData.includes(key)) {
delete x.formData[key];
}
}
return x;
})
Hope this helps
The key highlights to this function are:
Before the first loop, the array is cloned✥ then each object is skimmed over to find "formData". Since the actual keys are known, I'm going on the logical assumption that the object that has them is known as well -- hence the second parameter path.
Object.keys(obj['formData']) = ["Date", "TestOne", "TestTwo", "TestThree", "TestFour"] :
Figure I
function filterData(array, path, ...keys) {
let clone = structuredClone(array);
for (let obj of clone) {
Object.keys(obj[path]).flatMap(key =>
//... obj[path] = [ {{["formData"]}}...
//... ["Date", "TestOne", "TestTwo", "TestThree", "TestFour"]
On the second loop, the keys form the first loop is checked vs. the third parameter ...keys, a rest parameter that consists of one or more keys to filter out. The if condition has been reversed with ! operator:
Figure II
if (![...keys].includes(key)) {
return [];
}
return delete obj[path][key];
✥The TypeError sounds like the object is frozen so by cloning the array you can freely work on the clone.
const data = [{
_id: "62ec2f1084c7f48175a9cb4a",
Date: "2022-08-04T15:41:37.567Z",
facilityId: "62e5a9fd45f2646fc7361fa3",
userId: "62e16d390f4685e4fdb6a288",
formData: {
Date: "2022-08-04T15:41:37.567Z",
TestOne: 60002,
TestTwo: 19998,
TestThree: 102,
TestFour: "True"
}
}, {
_id: "62ec2f1c84c7f48175a9cb58",
Date: "2022-08-04T15:41:52.932Z",
facilityId: "62e5a9fd45f2646fc7361fa3",
userId: "62e16d390f4685e4fdb6a288",
formData: {
Date: "2022-08-04T15:41:52.932Z",
TestOne: 60003,
TestTwo: 19997,
TestThree: 103,
TestFour: "True"
}
}];
/**
* Find any given number of keys and remove them
* #param {array<object>} array - An array of objects
* #param {string} path - Name of the key if the target object is one
* level down. (todo: recursive algorithm to dig deeper)
* #param {string/array<string>} keys - List of keys to filter out
* #return {array<object>} The array sans filtered keys
*/
function filterData(array, path, ...keys) {
let clone = structuredClone(array);
for (let obj of clone) {
Object.keys(obj[path]).flatMap(key => {
if (![...keys].includes(key)) {
return [];
}
return delete obj[path][key];
});
}
return clone;
}
let x = filterData(data, "formData", "Date", "TestFour");
console.log(x);

How to use forEach function to get an array of results from an api?

I'm trying to use an array to get specific results from an API to display in a row in a spreadsheet using google script. This is a sample of what the API log looks like:
[20-06-07 22:38:53:839 BST] [ {
"symbol" : "AMZN",
"date" : "2020-03-31",
"currentRatio" : 1.07870933748165254,
"quickRatio" : 0.842142238837801593,
},{...
The code written below seems to work fine until the forEach array, where I get this error:
"TypeError: results.forEach is not a function"
function callAPI(symbol) {
// Call the api
var dataSheet = SpreadsheetApp.getActive().getSheetByName("Fundamental Analysis Model");
var url = 'https://financialmodelingprep.com/api/v3/ratios/'
var apikey = '?period=quarter&apikey=x'
var response = UrlFetchApp.fetch(url + symbol + apikey);
// Parse the JSON reply
var json = response.getContentText();
var data = JSON.parse(json);
console.log(data)
return JSON.parse(json)
}
function displayFinancials(array) {
var dataSheet = SpreadsheetApp.getActive().getSheetByName("Fundamental Analysis Model");
var symbol = "AMZN"
var api = callAPI(symbol);
var results = api[0]
var output = []
results.forEach(function(elem) {
output.push([elem["currentRatio"],elem["quickratio"]]);
});
dataSheet.appendRow([output])
}
If anyone can help correct the array so it will output the 2 example results that would be amazing. Thanks!
Log Data:
[20-06-07 23:46:12:257 BST] Logging output too large. Truncating output. [ { symbol: 'AMZN',
date: '2020-03-31',
currentRatio: 1.0787093374816525,
quickRatio: 0.8421422388378016,
cashRatio: 0.341245248460062,
daysOfSalesOutstanding: 86.28187456926258,
daysOfInventoryOutstanding: 155.51901394129743,
operatingCycle: 241.80088851056001,
daysOfPayablesOutstanding: 330.35316447115713,
cashConversionCycle: -244.07128990189454,
grossProfitMargin: 0.4134416582728092,
operatingProfitMargin: 0.052868048560674334,
pretaxProfitMargin: 0.04483645231405397,
netProfitMargin: 0.03359751895244659,
effectiveTaxRate: 0.21992314513745195,
returnOnAssets: 0.011458248582973991,
returnOnEquity: 0.03883748008334355,
returnOnCapitalEmployed: 0.02390356610399429,
netIncomePerEBT: 0.7493349098433343,
ebtPerEbit: 1,
ebitPerRevenue: 0.04483645231405397,
debtRatio: 0.7049693090698704,
debtEquityRatio: 2.3894778771908323,
longTermDebtToCapitalization: 0.26420092662525785,
totalDebtToCapitalization: null,
interestCoverage: 8.41542288557214,
cashFlowToDebtRatio: null,
companyEquityMultiplier: 3.3894778771908323,
receivablesTurnover: 4.230320699708455,
payablesTurnover: 1.1048781705612143,
inventoryTurnover: 2.346979901362889,
fixedAssetTurnover: 0.9700818987130202,
assetTurnover: 0.34104448602862075,
operatingCashFlowPerShare: 6.152610441767068,
freeCashFlowPerShare: -7.491967871485944,
cashPerShare: 98.97991967871486,
payoutRatio: 0,
operatingCashFlowSalesRatio: 0.04060859884429836,
freeCashFlowOperatingCashFlowRatio: -1.2176892950391645,
cashFlowCoverageRatios: null,
shortTermCoverageRatios: null,
capitalExpenditureCoverageRatio: 0.45091979396615156,
dividendPaidAndCapexCoverageRatio: null,
dividendPayoutRatio: null,
priceBookValueRatio: 18.875658781713444,
priceToBookRatio: 18.875658781713444,
priceToSalesRatio: 16.32895085617346,
priceEarningsRatio: 486.0165680473373,
priceToFreeCashFlowsRatio: -330.22031626909677,
priceToOperatingCashFlowsRatio: 402.10574412532634,
priceCashFlowRatio: 402.10574412532634,
priceEarningsToGrowthRatio: 74.06250027159547,
priceSalesRatio: 16.32895085617346,
dividendYield: null,
enterpriseValueMultiple: 131.35365201582718,
priceFairValue: 18.875658781713444 },...
[20-06-07 23:46:12:265 BST] TypeError: results.forEach is not a function
at displayFinancials(Stock Research & Analysis:123:13)
The problem is api indeed is an array, but api[0] is an object, forEach method cannot iterate objects. for..in or Object.keys() methods will do the trick.
const results = [{
symbol: 'AMZN',
date: '2020-03-31',
currentRatio: 1.0787093374816525,
quickRatio: 0.8421422388378016,
cashRatio: 0.341245248460062,
},{
symbol: 'AMZN',
date: '2020-03-31',
currentRatio: 2,
quickRatio: 2,
cashRatio: 0.341245248460062,
},{
symbol: 'AMZN',
date: '2020-03-31',
currentRatio: 3,
quickRatio: 3,
cashRatio: 0.341245248460062,
}
];
const output = results.map( result => [ result.currentRatio, result.quickRatio ])
console.log(output); // Should print [ [ 1.0787093374816525, 0.8421422388378016 ], [ 2, 2 ], [ 3, 3 ] ]
Or if you only want the first result it's really simple:
const results = { symbol: 'AMZN',
date: '2020-03-31',
currentRatio: 1.0787093374816525,
quickRatio: 0.8421422388378016,
cashRatio: 0.341245248460062,
daysOfSalesOutstanding: 86.28187456926258,
daysOfInventoryOutstanding: 155.51901394129743,
operatingCycle: 241.80088851056001,
};
const output = [results.currentRatio, results.quickRatio];
console.log(output); // will print [1.0787093374816525, 0.341245248460062]
As a supplementary answer: forEach method does not exist on Object.prototype, but only on the Array.prototype hence the error message "not a function". If you wanted to, you could define your own (although, it is not advisable).
Instead, you could define a subclass of Object with a forEach method, if you are more comfortable with it (another best practice would be not to use class):
class MyObject extends Object {
constructor(value) {
super(value);
}
forEach(callback, thisArg) {
const boundCbk = callback.bind(thisArg || this);
Object.values(this).forEach((value,idx) => {
boundCbk(value,idx,this);
});
}
}
const test = new MyObject();
test.one = 1;
test.two = 2;
test.forEach(value => console.log(value));

Vue computed property changes the data object

I have basically this structure for my data (this.terms):
{
name: 'First Category',
posts: [
{
name: 'Jim James',
tags: [
'nice', 'friendly'
]
},
{
name: 'Bob Ross',
tags: [
'nice', 'talkative'
]
}
]
},
{
name: 'Second Category',
posts: [
{
name: 'Snake Pliskin',
tags: [
'mean', 'hungry'
]
},
{
name: 'Hugo Weaving',
tags: [
'mean', 'angry'
]
}
]
}
I then output computed results so people can filter this.terms by tags.
computed: {
filteredTerms: function() {
let self = this;
let terms = this.terms; // copy original data to new var
if(this.search.tags) {
return terms.filter((term) => {
let updated_term = {}; // copy term to new empty object: This doesn't actually help or fix the problem, but I left it here to show what I've tried.
updated_term = term;
let updated_posts = term.posts.filter((post) => {
if (post.tags.includes(self.search.tags)) {
return post;
}
});
if (updated_posts.length) {
updated_term.posts = updated_posts; // now this.terms is changed even though I'm filtering a copy of it
return updated_term;
}
});
} else {
return this.terms; // should return the original, unmanipulated data
}
}
},
filteredTerms() returns categories with only the matching posts inside it. So a search for "angry" returns just "Second Category" with just "Hugo Weaving" listed.
The problem is, running the computed function changes Second Category in this.terms instead of just in the copy of it (terms) in that function. It no longer contains Snake Pliskin. I've narrowed it down to updated_term.posts = updated_posts. That line seems to also change this.terms. The only thing that I can do is reset the entire data object and start over. This is less than ideal, because it would be loading stuff all the time. I need this.terms to load initially, and remain untouched so I can revert to it after someone clears their search criterea.
I've tried using lodash versions of filter and includes (though I didn't really expect that to make a difference). I've tried using a more complicated way with for loops and .push() instead of filters.
What am I missing? Thanks for taking the time to look at this.
Try to clone the object not to reference it, you should do something like :
let terms = [];
Object.assign(terms,this.terms);
let terms = this.terms;
This does not copy an array, it just holds a reference to this.terms. The reason is because JS objects and arrays are reference types. This is a helpful video: https://www.youtube.com/watch?v=9ooYYRLdg_g
Anyways, copy the array using this.terms.slice(). If it's an object, you can use {...this.terms}.
I updated my compute function with this:
let terms = [];
for (let i = 0; i < this.terms.length; i++) {
const term = this.copyObj(this.terms[i]);
terms.push(term);
}
and made a method (this.copyObj()) so I can use it elsewhere. It looks like this:
copyObj: function (src) {
return Object.assign({}, src);
}

Chai - Testing for values in array of objects

I am setting up my tests for the results to a REST endpoint that returns me an array of Mongo database objects.
[{_id: 5, title: 'Blah', owner: 'Ted', description: 'something'...},
{_id: 70, title: 'GGG', owner: 'Ted', description: 'something'...}...]
What I want my tests to verify is that in the return array it conatins the specific titles that should return. Nothing I do using Chai/Chai-Things seems to work. Things like res.body.savedResults.should.include.something.that.equals({title: 'Blah'}) error out I'm assuming since the record object contains other keys and values besides just title.
Is there a way to make it do what I want? I just need to verify that the titles are in the array and don't care what the other data might be (IE _id).
Thanks
This is what I usually do within the test:
var result = query_result;
var members = [];
result.forEach(function(e){
members.push(e.title);
});
expect(members).to.have.members(['expected_title_1','expected_title_2']);
If you know the order of the return array you could also do this:
expect(result).to.have.deep.property('[0].title', 'expected_title_1');
expect(result).to.have.deep.property('[1].title', 'expected_title_2');
As stated here following code works now with chai-like#0.2.14 and chai-things. I just love the natural readability of this approach.
var chai = require('chai'),
expect = chai.expect;
chai.use(require('chai-like'));
chai.use(require('chai-things')); // Don't swap these two
expect(data).to.be.an('array').that.contains.something.like({title: 'Blah'});
Probably the best way now a days would be to use deep.members property
This checks for unordered complete equality. (for incomplete equality change members for includes)
i.e.
expect([ {a:1} ]).to.have.deep.members([ {a:1} ]); // passes
expect([ {a:1} ]).to.have.members([ {a:1} ]); // fails
Here is a great article on testing arrays and objects
https://medium.com/building-ibotta/testing-arrays-and-objects-with-chai-js-4b372310fe6d
DISCLAIMER: this is to not only test the title property, but rather a whole array of objects
ES6+
Clean, functional and without dependencies, simply use a map to filter the key you want to check
something like:
const data = [{_id: 5, title: 'Blah', owner: 'Ted', description: 'something'},{_id: 70, title: 'GGG', owner: 'Ted', description: 'something'}];
expect(data.map(e=>({title:e.title}))).to.include({title:"Blah"});
or even shorter if you only check one key:
expect(data.map(e=>(e.title))).to.include("Blah");
https://www.chaijs.com/api/bdd/
Here is another approach that I found to be more helpful. Basically, use string interpolation and map your array of objects to an array of string literals. Then you can write expectations against the array of strings.
const locations: GeoPoint[] = [
{
latitude: 10,
longitude: 10
},
{
latitude: 9,
longitude: 9
},
{
latitude: -10,
longitude: -10
},
{
latitude: -9,
longitude: -9
}
];
const stringLocations: string[] = locations.map((val: GeoPoint) =>
`${val.latitude},${val.longitude}`
);
expect(stringLocations).to.contain('-9.5,-9.5');
expect(stringLocations).to.contain('9.5,9.5');
I loved the suggestion from #sebastien-horin
But another way with Should syntax (for the specific property):
const data = [
{ _id: 5, title: 'Blah', owner: 'Ted', description: 'something' },
{ _id: 7, title: 'Test', owner: 'Ted', description: 'something' },
];
data.map((e) => e.title).every((title) => title.should.equal('Blah'));
An alternative solution could be extending the array object with a function to test if an object exists inside the array with the desired property matching the expected value, like this
/**
* #return {boolean}
*/
Array.prototype.HasObjectWithPropertyValue = function (key, value) {
for (var i = 0; i < this.length; i++) {
if (this[i][key] === value) return true;
}
return false;
};
(i put this in my main test.js file, so that all other nested tests can use the function)
Then you can use it in your tests like this
var result = query_result;
// in my case (using superagent request) here goes
// var result = res.body;
result.HasObjectWithPropertyValue('property', someValue).should.equal(true);

Eloquent Javascript, listToArray. Why does my for loop return rest [object] when input array has more than 3 elements

I'm currently working through Eloquent Javascript chapter 4. The exercise asks
Write a function arrayToList that builds up a data structure like the previous one when given [1, 2, 3] as argument
Using the code below I can get the necessary output - but only when the number of elements in the array is 3 or less.
function createNode (value, rest) {
return {
value: value,
rest: rest }
};
function arrayToList(arr) {
var index = arr.length;
firstNode = createNode(index,null);
listBuild = firstNode;
for (i =(index-1); i >0; i--) {
listBuild = createNode(i,listBuild)
}
Question
arrayToList([1,2,3]) produces the desired result of "{ value: 1, rest: { value: 2, rest: { value: 3, rest: null } } }"
arrayToList([1,2,3,4]) produces output of "{ value: 1,
rest: { value: 2, rest: { value: 3, rest: [Object] } } }"
Why does this function generate a rest property of [Object] in this case?
Thanks!
Your function seems to work fine, so I suspect this is an output issue, not any problem with your code.
If you're using Node.js to test your code, then you've just run into a gotcha with its implementation of console.log. In Node.js, console.log formats objects using util.inspect, and by default util.inspect will only display nested objects to a depth of 2. You can change this by calling util.inspect directly and using the depth option. You could set it to a higher number, or you can set it to null for unlimited depth:
var util = require('util');
var result = arrayToList([1,2,3,4]);
console.log(util.inspect(result, { depth: null }));

Categories