Accessing array values - javascript

I am Iterating through an array of locations that have longitude and latitude values. I am using these values to call the Open Weather API asynchronously. I am then looping through the returned JSON array to grab each location's forecast date and time, forecast description, and the forecast icon. Then I want to store them in an array.
My issue is when I try to push the data back to the 'forecastArray' (so I can place the data into my HTML dynamically with jQuery) I can't access the data, when I console.log it the browser it returns as 'undefined'. the actual array will log to the browser but I can't assign the data to variables.
I am fairly new to web development so I would appreciate any advice given. I must not be understanding the scope limitations.
Here is my code:
let ForcastArray = []
for (let i = 0; i < cities1Array.length; i++) {
// console.log(citiesArray1[i])
let lat = citiesArray1[i][0]
let lng = citiesArray1[i][1]
openWeatherForcast(lat, lng).then(forcast => {
// console.log(forcast)
for (let j = 0; j < 10; j++) {
// console.log(forcast['list'][j]['dt_txt'])
let dateTime = forcast['list'][j]['dt_txt']
// console.log(forcast['list'][j]['weather'][0]['description'])
let description = forcast['list'][j]['weather'][0]['description']
// console.log(forcast['list'][j]['weather'][0]['icon'])
let iconcode = forcast['list'][j]['weather'][0]['icon'];
let iconurl = "http://openweathermap.org/img/wn/" + iconcode + "#2x.png";
ForcastArray.push(dateTime, description, iconurl)
}
})
}
console.log(ForcastArray)
// Returns:
// []
// 0: "2020-09-27 12:00:00"
// 1: "few clouds"
// 2: "http://openweathermap.org/img/wn/02d#2x.png"
// 3: "2020-09-27 15:00:00"
// 4: "clear sky"
// 5: "http://openweathermap.org/img/wn/01d#2x.png"
// 6: "2020-09-27 18:00:00"
// 7: "clear sky"
// 8: "http://openweathermap.org/img/wn/01n#2x.png"
// 9: "2020-09-27 21:00:00"
// 10: "few clouds"
// 11: "http://openweathermap.org/img/wn/02n#2x.png"
// 12: "2020-09-28 00:00:00"
// 13: "scattered clouds"
// 14: "http://openweathermap.org/img/wn/03n#2x.png"
// 15: "2020-09-28 03:00:00"
// 16: "light rain"
// 17: "http://openweathermap.org/img/wn/10n#2x.png"
// 18: "2020-09-28 06:00:00"
// 19: "light rain"
// 20: "http://openweathermap.org/img/wn/10d#2x.png"
// 21: "2020-09-28 09:00:00"
// 22: "moderate rain"
// 23: "http://openweathermap.org/img/wn/10d#2x.png"
// 24: "2020-09-28 12:00:00"
// 25: "light rain"
// 26: "http://openweathermap.org/img/wn/10d#2x.png"
// 27: "2020-09-28 15:00:00"
// 28: "overcast clouds"
// 29: "http://openweathermap.org/img/wn/04d#2x.png"
// length: 30
// __proto__: Array(0)
console.log(ForcastArray[0])
// Returns:
// undefined

If the array is populated you should be able to access it with the array access operator (the square brackets) [0] like you wrote (console.log(ForcastArray[0]).
As Deniz pointed out this is probably a timing issue. You should put the console.logs inside your .then() block, the array will be accessible when this block is executed.
openWeatherForcast(lat, lng).then(forcast => {
for (let j = 0; j < 10; j++) {
// console.log(forcast['list'][j]['dt_txt'])
let dateTime = forcast['list'][j]['dt_txt']
// console.log(forcast['list'][j]['weather'][0]['description'])
let description = forcast['list'][j]['weather'][0]['description']
// console.log(forcast['list'][j]['weather'][0]['icon'])
let iconcode = forcast['list'][j]['weather'][0]['icon'];
let iconurl = "http://openweathermap.org/img/wn/" + iconcode + "#2x.png";
ForcastArray.push(dateTime, description, iconurl)
}
console.log(ForcastArray[0])
})
You can count the returned promises and see if they match the number of cities (length of cities1Array or citiesArray1). When returned promises == citiesArray1.length you can execute another function to start the DOM manipulation (insert the data in your HTML via jQuery).

Related

App Script getValue() Returning Empty Strings

I'm having an issue with my script that I created. I wanted to be able to archive metrics for work on a weekly basis. Everything works as expected, except when I try to pull values from my overview tab.
The cells that I am trying to grab values from are the results of various calculations that give me a good idea of my progress; I at first thought it may have something to do with my use of ARRAYFORMULA.
Here is an example of one of the cells:
=ARRAYFORMULA({
"Total Number of Attempts";
IFERROR(
COUNTA(
Metrics!$F$2:$F
),
0
)
})
In this particular array formula, the formula is typed into cell E21, the "Total Number of Attempts" appears in E21, and I would be pulling the value from E22.
My thought was that since I haven't put any data in the cell myself, the script was treating it like a blank cell and returning an empty string. However, I tested what happens when I do put values in the cell and I get the same empty string returned.
I don't see anything logically wrong with my code, so I must be missing something with the way that Google Sheets/App Script operates. Below, you will find my code for the archiveData() function:
/*
This function will be set up with a trigger
It will run every week, depositing the current values into their correct columns in the
Archive tab.
*/
function archiveData() {
// Grab the sheet names
var archive = SpreadsheetApp.getActive().getSheetByName('Archive');
var data = SpreadsheetApp.getActive().getSheetByName('Overview');
// Get the week number
var d = Utilities.formatDate(new Date(), "GMT", "w");
// Initialize row/column number for sheets to loop through
row = 2;
column = 1;
// Starting at A2, if the cell is filled, advance to the next cell in column
// If not, it will break out of loop and continue
while(archive.getRange(row, column).getValue() != "") {
row++;
}
// Place the week number in Column A
archive.getRange(row, column).setValue(d);
// Add a variable to store a row, column pair
var metric = [];
// Loop through the remaining columns
while(column <= 18) {
var addData = false;
// If the column is already populated, move to the next column
if(archive.getRange(row, column).getValue() != "") {
column++;
} else {
addData = true;
// Use a switch case to select the correct row, column pair for the correct metric on the
// Overview tab
switch(column) {
case 2:
// Total Avg Number of Tracks Missed on First Attempt
metric = [3, 14]; // C14
break;
case 3:
// Total Avg Number of Attempts Per Task
metric = [5, 14]; // E14
break;
case 4:
// Total Avg Number of Tasks Per Week
metric = [7, 14]; // G14
break;
case 5:
// Avg Number of Tracks Missed on First Attempt in Last 5 Tasks
metric = [3, 17]; // C17
break;
case 6:
// Avg Number of Attempts Per Task in Last 5 Tasks
metric = [5, 17]; // E17
break;
case 7:
// Avg Number of Tasks Per Week in Last Two Weeks
metric = [7, 17]; // G17
break;
case 8:
// Total Number of Tracks
metric = [3, 22]; // C22
break;
case 9:
// Total Number of Attempts
metric = [5, 22]; // E22
break;
case 10:
// Total Avg Tracks Per Week
metric = [7, 22]; // G22
break;
case 11:
// Total Number of Tracks in last 5 Attempts
metric = [3, 25]; // C25
break;
case 12:
// Total Number of Attempts in last 5 Tasks
metric = [5, 25]; // E25
break;
case 13:
// Avg Number of Tracks Per Week in Last Two Weeks
metric = [7, 25]; // G25
break;
case 14:
// Total Number of Tracks This Week
metric = [7, 28]; // G28
break;
case 15:
// Longest Task
metric = [5, 31]; // E31
break;
case 16:
// Shortest Task
metric = [5, 34]; // E34
break;
case 17:
// Total Avg Task Time
metric = [7, 31]; // G31
break;
case 18:
// Avg Task Time in Last 5 Attempts
metric = [7, 34]; // G34
break;
// this shouldn't run
default:
"ERROR";
break;
}
}
if(addData) {
console.log(metric[0], metric[1]); // This correctly shows the row, column pair
var metricData = data.getRange(metric[0], metric[1]).getValue();
console.log("metricData: ", metricData); // This shows metricData is an empty string
archive.getRange(row, column).setValue(metricData);
column++;
}
}
}

How do i round a variables value down to a whole number in JS?

I'm creating a calculator that takes weight, location, and quantity as a parameter. I have the prices set as key value pairs, and when a weight is entered it will iterate over the object array, find the key that is equal to it, and display a message of the price using the value. The problem i'm running into is whenever a number is entered that isn't a whole number, nothing is displayed back. The following is my code:
const calculateRate = () => {
let price
let ausWeight = {
1: 19, 2: 25, 3: 25, 4: 28, 5: 28, 6: 31.5, 7: 35, 8: 38.5, 9: 42, 10: 45.5, 11: 49, 12: 52.5, 13: 56, 14: 59.5,
15: 63, 16: 66.5, 17: 70, 18: 73.5, 19: 77, 20: 80.5};
let inputWeight = Object.keys(ausWeight)
let inputValue = Object.values(ausWeight)
let x = Math.floor(weight);
console.log(x)
for (var i = 0; i < inputWeight.length; i++){
if (x === inputWeight[i] && region.value === 'Australia') {
price = `Estimated delivery cost is $${inputValue[i] * quantity}`;
setShowResults(true)
setPrice(price);
}
}
}
As you can see, i'm setting x to equal to weight rounded down with Math.floor(), but i'm not getting an output. You can also see that i'm logging the value of x to the console and it's giving the result i'm expecting when i look at it through dev tools. For context, this is a functional react component, with weight being set by setState().
Any idea's on how to fix this would be appreciated. Thanks.
You simply compare a string with a number.
By using Object.keys, you get an array of strings. But later you take a strict comparing.
To overcome this, you need to convert the string value into a number
x === +inputWeight[i]

JavaScript, I can not understand switch parameter

I recently started to study javascript
I'm currently watching Javascript course in Udemy.
While code challenging, There's something I cant get it about parameter of 'switch'
let john = {
fullName: 'John Smith',
bills: [124, 48, 268, 180, 42],
calcTips: function() {
this.tips = [];
this.finalValues = [];
for (let i = 0; i < this.bills.length; i++) {
let percentage;
let bill = this.bills[i]
switch (bill) { // If I put parameter as 'bill' variation, The result is only defalut.
case bill < 50:
percentage = 0.2;
break;
case bill >= 50 && bill < 200:
percentage = 0.15;
break;
default:
percentage = 0.1;
}
this.tips[i] = bill * percentage;
this.finalValues[i] = bill + bill * percentage;
}
}
}
john.calcTips();
console.log(john);
However
let john = {
fullName: 'John Smith',
bills: [124, 48, 268, 180, 42],
calcTips: function() {
this.tips = [];
this.finalValues = [];
for (let i = 0; i < this.bills.length; i++) {
let percentage;
let bill = this.bills[i]
switch (true) { // If I put 'ture' as a parameter, It work's. Why?
case bill < 50:
percentage = 0.2;
break;
case bill >= 50 && bill < 200:
percentage = 0.15;
break;
default:
percentage = 0.1;
}
this.tips[i] = bill * percentage;
this.finalValues[i] = bill + bill * percentage;
}
}
}
john.calcTips();
console.log(john);
I've searched in google about this problem.
But I can't find specific way to solve this issue.
I'll appreciate your help.
Switch statements compare values strictly. Which means that you can compare for the exact value of the switch variable.
switch (x) {
case 1: console.log(1); break;
case 2: console.log(2); break;
}
You can do a trick however if you want to make the switch statement work on numerical ranges like this:
var x = this.dealer;
switch (true) {
case (x < 5):
alert("less than five");
break;
case (x < 9):
alert("between 5 and 8");
break;
case (x < 12):
alert("between 9 and 11");
break;
default:
alert("none");
break;
}
The implementation works on the strict comparison of booleans. The switch statement is for true and will match wherever the case is true.
Related question: Switch on ranges of integers in JavaScript
The switch statement tests the value of a variable and compares it with multiple cases. Once the case match is found, a block of statements associated with that particular case is executed. So in this case you switching on a constant value.
More detail :
javascript: using a condition in switch case

Slopes not working in Phaser using arcade physics

I'm currently having this issue when I am trying to get a sprite to go up a slope within phaser using the phaser-arcade-slopes.min.js plugin. I am using a .csv tilemap also with a tile size of 32x32. I'm unsure if my names are just incorrect or I am using the wrong type of file for the tilemap itself. I have been getting errors such as - Tilemap.createLayer: Invalid layer ID given: null & Cannot read property 'resizeWorld' of undefined(…). Any help would be much appreciated.
"snow_tiles_32.png" is the name of the tileset I created and I'm using
"tiles.csv" the tilemap created inside Tiled.
var game = new Phaser.Game(800, 600, Phaser.AUTO, '', { preload: preload, create: create, update: update });
function preload() {
this.game.load.tilemap('tilemap', 'assets/tilemaps/csv/tiles.csv', null, Phaser.Tilemap.CSV);
this.game.load.spritesheet('tiles', 'assets/tilemaps/tiles/snow_tiles_32.png', 32,32);
this.game.load.spritesheet('player', 'assets/penguin.png', 32,48);
}
var player;
var cursors;
function create() {
this.game.physics.startSystem(Phaser.Physics.ARCADE);
this.game.plugins.add(Phaser.Plugin.ArcadeSlopes);
cursors = game.input.keyboard.createCursorKeys();
this.map = this.game.add.tilemap('tilemap');
this.map.addTilesetImage('snow_tiles_32', 'tiles');
this.game.stage.backgroundColor = '#80e3ff';
this.ground = this.map.createLayer('collision');
this.ground.resizeWorld();
this.game.slopes.convertTilemapLayer(this.ground,{
2: 'FULL',
3: 'HALF_BOTTOM_LEFT',
4: 'HALF_BOTTOM_RIGHT',
6: 'HALF_TOP_LEFT',
5: 'HALF_TOP_RIGHT',
15: 'QUARTER_BOTTOM_LEFT_LOW',
16: 'QUARTER_BOTTOM_RIGHT_LOW',
17: 'QUARTER_TOP_RIGHT_LOW',
18: 'QUARTER_TOP_LEFT_LOW',
19: 'QUARTER_BOTTOM_LEFT_HIGH',
20: 'QUARTER_BOTTOM_RIGHT_HIGH',
21: 'QUARTER_TOP_RIGHT_HIGH',
22: 'QUARTER_TOP_LEFT_HIGH',
23: 'QUARTER_LEFT_BOTTOM_HIGH',
24: 'QUARTER_RIGHT_BOTTOM_HIGH',
25: 'QUARTER_RIGHT_TOP_LOW',
26: 'QUARTER_LEFT_TOP_LOW',
27: 'QUARTER_LEFT_BOTTOM_LOW',
28: 'QUARTER_RIGHT_BOTTOM_LOW',
29: 'QUARTER_RIGHT_TOP_HIGH',
30: 'QUARTER_LEFT_TOP_HIGH',
31: 'HALF_BOTTOM',
32: 'HALF_RIGHT',
33: 'HALF_TOP',
34: 'HALF_LEFT'
});
this.map.setCollisionBetween(2,34, true, 'collision');
//player
this.player = this.game.add.sprite(100,50,'player');
this.game.physics.arcade.enable(player);
this.player.body.bounce.y = 0.2;
this.player.body.gravity.y = 2000;
this.player.body.collideWorldBounds = true;
this.player.animations.add('left', [0,1,2,3], 10, true);
this.player.animations.add('right', [5,6,7,8], 10, true);
this.game.slopes.enable(this.player);
this.game.camera.follow(this.player);
}
function update() {
this.game.physics.arcade.collide(this.player, this.ground);
this.player.body.velocity.x = 0;
if(cursors.left.isDown){
this.player.body.velocity.x = -150;
this.player.animations.play('left');
}
else if (cursors.right.isDown){
this.player.body.velocity.x = 150;
this.player.animations.play('right');
}
else{
this.player.animations.stop();
this.player.frame = 4;
}
if(cursors.up.isDown && player.body.onFloor()){
this.player.body.velocity.y = -350;
}
}
LayerID you provide as parameter for createLayer() must match map layer defined in map file, see documentation. So my best guess would be to open map file in tiled once again and check what layer names you have.
Additionally there's example how to use .csv files (here)[http://phaser.io/examples/v2/tilemaps/csv-map].
Hope that'll help somehow.

Javascript error - NaN

i have been going at this code all afternoon and i cannot figure out why this error is occurring. here is the code:
var ancestry = [{
"name": "Carolus Haverbeke",
"sex": "m",
"born": 1832,
"died": 1905,
"father": "Carel Haverbeke",
"mother": "Maria van Brussel"
}];
function average(array) {
function plus(a, b) {
return a + b;
}
return array.reduce(plus) / array.length;
}
var byCentury = new Object();
for (var i = 0; i < ancestry.length; i++) {
var centName = Math.ceil(ancestry[i].died / 100);
if (centName in byCentury) {
byCentury[centName].push(ancestry[i].age = ancestry[i].died - ancestry[i].born);
} else {
byCentury[centName] = [centName];
}
}
for (var century in byCentury) {
// For some reason no value is returned
var ages = byCentury[century].map(function(person) {
return person.age;
});
console.log(century + ': ' + average(ages));
}
The output i get is:
16: NaN
17: NaN
18: NaN
19: NaN
20: NaN
21: NaN
While the desired output is:
16: 43.5
17: 51.2
18: 52.8
19: 54.8
20: 84.7
21: 94
here is the challenge it has a built in code sandbox: http://eloquentjavascript.net/code/#5.3
The line
byCentury[centName].push(ancestry[i].age = ancestry[i].died - ancestry[i].born);
isn't pushing ancestry[i] into your array - it's pushing the value of ancestry[i].age. So later on, where you have:
var ages = byCentury[century].map(function(person) {
return person.age;
});
person is actually a number, so person.age will be undefined.
The other problem you have at the moment is what happens when you come to a person in the ancestry array whose century of birth hasn't been stored in byCentury yet.
if (centName in byCentury) {
...
} else {
byCentury[centName] = [centName];
}
Instead of storing their age, you store the number representing that century. One way of fixing it would be to change that block to:
if (byCentury[centName] === undefined) {
byCentury[centName] = [];
}
byCentury[centName].push(ancestry[i].died - ancestry[i].born);
and simply logging century + ': ' + average(byCentury[centName]) in your loop at the end. Finally, add in some rounding to make your output match that of the exercise, and you'll have your final solution.

Categories

Resources