Javascript error - NaN - javascript

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.

Related

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]

Accessing array values

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).

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

How to populate array with numbers based on odds?

I recently saw a roulette wheel of sorts which contained the following possible numbers. 1, 2, 9, 16, 24, 49j and 49f. Each number has odds of itself over 1. So the odds of rolling a 2 are 2/1 and a 9 is 9/1. I thought it would be a fun (and simple) exercise to populate an array with the right amount of each type of number but it's proved anything but. My first idea was to build a name/value array to hold each numbers odds and then a second one to hold a counter value.
let numbers = {
"1": "1",
"2": "2",
"9": "9",
"16": "16",
"24": "24",
"49f": "49",
"49j": "49"
};
let counter = {
"1": "0",
"2": "0",
"9": "0",
"16": "0",
"24": "0",
"49f": "0",
"49j": "0"
};
let tc = {
"1": "0",
"2": "0",
"9": "0",
"16": "0",
"24": "0",
"49f": "0",
"49j": "0"
};
That last one tc is just to tally how many of each number is in the final array and confirm my mathematical genius. So from here it should be a simple matter of looping 50 times and looping through each number, incrementing its counter by 1 and when the counter value equals the odds value, push that number into the array and reset its counter to 0. So each iteration I should get a 1 and every 3rd iteration I should get a 2 and so on.
var wheel = [];
function load_numbers( ) {
for(let number in numbers) {
var count = parseInt(counter[number], 10);
var odd = parseInt(numbers[number], 10);
var t = parseInt(tc[number], 10);
count++;
if (count == odd) {
wheel.push(number);
count = 0;
t++; tc[number] = t;
}
counter[number] = count;
}
}
function load_wheel( ) {
for (i = 0; i < 50; i++) {
load_numbers();
}
for(let mc in tc) {
document.write(mc + ": " + tc[mc] + " of " + wheel.length + " <br>");
}
}
However that code produces the following
1: 50 of 87
2: 25 of 87
9: 5 of 87
16: 3 of 87
24: 2 of 87
49f: 1 of 87
49j: 1 of 87
These odds are clearly wrong but I can't see what's wrong with the method, I've tried doubling the odds and looping 100 times, still wrong. Setting a breakpoint after 49j == 1 also gives me these odds. In desperation I tried calculating the percentage of each numbers odds and adding them together (ie 1 = 50%, 2 = 33%) and that procedure keeps giving me 108%! So at this point I have to conclude I've been wrong about math my whole life or the Casino is pulling a fast one! Or is there something I'm overlooking?

I think I'm having a scope issue with a custom sort method

I've been working on a javascript class to simplify the sorting and comparing of library of congress call numbers. For the most part, the majority of it is working, except for one of the the main methods to return a sorted list. I can do the sort outside of the class given other methods of the class, so I'm not sure why I'm losing the scope when I enter the sort() function.
I'm not sure if there's a way I can pass the scope into the sort() or what I need to do to have access to that method.
I've created a github gist of the javascript and a quick html test of the class (including the error) that should demonstrate things:
https://gist.github.com/rayvoelker/accaa95c6b5db28f7f84429f8a3d8cdf
Here's the class methods that I'm having trouble with. Maybe I'm just not seeing something that should be simple.
locCallClass.prototype.localeCompare = function (a, b) {
try {
var a_norm = this.returnNormLcCall(a),
b_norm = this.returnNormLcCall(b);
return ( a_norm < b_norm ? -1 : (a_norm > b_norm ? 1 : 0) );
}
catch (err) {
// console.log("error")
}
}
locCallClass.prototype.sortCallNumbers = function (callnumbers) {
var sorted = callnumbers.sort(function (a,b) {
return this.localeCompare(a,b);
});
return sorted;
}
And here is the way that I'm calling it:
var loc = new locCallClass();
var set1 = ["LC 346 .65 .B29","LC 346 .M634","HX 754 .5 .C15","HX 754 .C7723"];
var sorted = loc.sortCallNumbers(set1);
And here's a snippet of the same demonstration code from the gist:
// js-loc-callnumbers
// A javascript class to normalize and perform various sorting options on
// Library of Congress Call Numbers within a library institution.
function locCallClass() {
// the regular expression that defines the Library of
// Congress call number. Thanks to Bill Dueber's post on the subject for the regex
// http://robotlibrarian.billdueber.com/2008/11/normalizing-loc-call-numbers-for-sorting/
this.lc = /^\s*([A-Z]{1,3})\s*(\d+(?:\s*\.\s*\d+)?)?\s*(?:\.?\s*([A-Z]+)\s*(\d+)?)?(?:\.?\s*([A-Z]+)\s*(\d+)?)?\s*(.*?)\s*$/;
//local storage for an array of call numbers
this.callNumberArray = [];
//a mostly sorted array of call numbers for testing
this.testCallNumbers = ["Z10.1 A", "Z5.1 A", "CB3 .C55", "CS418 .J82", "CS425 .B95", "D21 .W93", "D21.1.D58 1981", "D761 .W54", "D761.9.F7 G8", "DA86 .P884", "DA86 .R52", "DA506.A2 A4", "DA506.A2 B75", "DA784.5 .M23 1989", "DA785 .S12", "DC129 .W6", "DC130.A2 H3", "DF623 .C46", "DF631 .B313", "DK 267 .W78", "DK268.A1D56213 1999", "DS 70.7 .O6", "DS 70.7 .S27", "DS557.C28S6", "DS 557 .F3", "DS 917 .W47", "DS 917.35 .P33", "DT 636 .S5 A3", "DT636.2.F65 1996", "E 160 .F72", "E 160 .F73", "E184.A1I444 1992", "E184.A1I445 1996", "E 302 .J442 1984", "E302.J442 2004B", "E 487 .C746", "E 487 .C746 1966", "E 876 .U84 1986", "E 876 .V53", "F548.9.N4R437 2005", "F548.9.N4S77 2007", "F 1656 .C7 1968", "F 1659 .B55 F4", "GN 51 .A58", "GN 51 .A58 1988B", "GV 741 .B56", "GV741 .I58", "Q183.9.I34 2002", "Q183.9.L45 1993", "QA 29 .P775 A3 1987", "QA29.R3A4 1995", "QA 76.758 .H86 1989", "QA76.758.K365 2008", "QA 164 .C63 1969", "QA 164 .C63 1969", "QA274.73.R84 2004", "QA274.73.S2813 1999", "QA 353 .E5 Z4313 1993", "QA 353 .G44 W55 1990", "QA 640.7 .B55 1970", "QA641.A587 1996", "QC 173.98 .M44 V.1", "QC 173.98 .M44 V.1", "QD 21 .C51", "QD 21 .C51", "QD501 .B242 V.37", "QD501.B242 V.38", "QH 81 .B73", "QH 81 .B754", "QH540.I5", "QH540.I5", "QK 314 .F54", "QK 321 .A3613", "QL951 .C8", "QL951 .C8", "QP 601 .C733 V.171", "QP 601 .C733 V.172", "RA 410.7 .C5", "RA 410.7 .E73", "RC455.S8 1961", "RC 455 .S928", "RD 98 .G38", "RD 99 .E42 1955", "S 942 .C6 1974", "S 942 .K63 1972", "TA166.W68 1980", "TA167.A965 1998", "TA1520.S87 2007", "TA1520.W35 1998", "TD 741 .I33", "TD 741 .I33 1956", "TJ163.2 .A55", "TJ163.2 .A55", "TK5105.875.I57G723 2005", "TK5105.875.I57H338 1996", "TL215.J58L35 2005", "TL215.M18D53 2005", "TP155.C69 V.5 1997", "TP 155 .C74 1986", "TS156 .I838 2003", "TS 156 .J324"];
}
// locCallClass.returnNormLcCall(call_number)
// returns a "normalized" call number
locCallClass.prototype.returnNormLcCall = function(call_number) {
var result = this.lc.exec(call_number);
if (!result) {
throw new Error(call_number + " Not a Call Number");
}
// track the position of what we're looking at in the callnumber
var before_first_cutter = true;
var return_string = "";
// work through the results starting at 1 (the 0 value in the array is the original input)
for(var i=1; i<=(result.length-1); i++) {
if (i>1) {
return_string = return_string + "."
}
if (i>2) {
before_first_cutter = false;
}
return_string = return_string + this.padZed(result[i], before_first_cutter);
}
// TODO: consider adding further checks to see if the return string
// consists of 8 segments 9 characters and throw an error if not
return return_string;
}
// locCallClass.localeCompare(b,a)
// replicates functionality of the normal compare function
// so that it may be used in external sorting operations:
//
// A negative number if the reference string (a) occurs before the
// given string (b);
// positive if the reference string (a) occurs after
// the compare string (b);
// 0 if they are equivalent.
locCallClass.prototype.localeCompare = function (a, b) {
try {
var a_norm = this.returnNormLcCall(a),
b_norm = this.returnNormLcCall(b);
return ( a_norm < b_norm ? -1 : (a_norm > b_norm ? 1 : 0) );
}
catch (err) {
// console.log("error")
}
}
// locCallClass.sortCallNumbers()
// takes a variable list of call numbers as arguments, and returns
// a sorted array of call numbers in their original format/
// You can also use this method with an array like the following:
// loc.sortCallNumbers.call(loc_instance,input)
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
//
// TODO: Consider removing this method, instead using something like the
// following:
// var loc = new locCallClass();
// loc.testCallNumbers.sort(function(a,b) {return loc.localeCompare(a,b)});
locCallClass.prototype.sortCallNumbers = function (callnumbers) {
var sorted = callnumbers.sort(function (a,b) {
return this.localeCompare(a,b);
});
return sorted;
}
// locCallClass.isBetween(a,b,c)
// returns true if a <= c <= b
locCallClass.prototype.isBetween = function (a,b,c) {
//this.localeCompare(a, b) <= 0 if in sort order
return ( (this.localeCompare(a,c) <= 0 && this.localeCompare(c,b) <=0) ? true : false );
}
// locCallClass.padZed()
// returns portion of the call number padded out to enable sorting.
locCallClass.prototype.padZed = function (value, before_first_cutter) {
//pad value with zeros - return value will have a length of 9
// The exceptions here are going to be if the number is before the
// cutter, then we should treat it as two different parts: whole
// number, and decimal portion.
if(value) {
if(before_first_cutter && !isNaN(value) ) {
//this is a number before the first cutter, split it, and then
// pad each of the parts
var int_portion = Math.floor(value).toString();
var dec_portion = (value % 1).toFixed(3).toString().substr(2,3);
var pad_zeros = "";
for (var i=0; i<(9 - int_portion.length); i++) {
pad_zeros = pad_zeros + "0";
}
return_value = pad_zeros + int_portion;
var pad_zeros = "";
for (var i=0; i<(9 - dec_portion.length); i++) {
pad_zeros = pad_zeros + "0";
}
return_value += "." + dec_portion + pad_zeros;
return return_value;
} // end if
else {
//pad the value to the right
var pad_zeros = "";
for (var i=0; i<(9 - value.length); i++) {
pad_zeros = pad_zeros + "0";
}
return value + pad_zeros;
}
}
else {
return "000000000";
}
}
/* test script */
var loc = new locCallClass();
var output1 = document.getElementById("output1");
// define, show and then sort the first set
var set1 = ["LC 346 .65 .B29",
"LC 346 .M634",
"HX 754 .5 .C15",
"HX 754 .C7723"
];
output1.innerHTML = "<b>pre-sort</b><br>";
for(var i=0; i<set1.length; i++) {
output1.innerHTML += set1[i] + "<br>";
}
//sort with the specialized sort method from our class, "loc"
var sorted1 = set1.sort(function(a,b) {
return loc.localeCompare(a,b);
});
output1.innerHTML += "<hr><b>sorted</b><br>";
for(var i=0; i<sorted1.length; i++) {
output1.innerHTML += sorted1[i] + "<br>";
}
// now test the built in method to sort call numbers
// which doesn't work for some reason
var alt_sorted = loc.sortCallNumbers(set1);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>locCallClass test sorting of call numbers</title>
</head>
<body style="font-family:monospace;">
<h3>test sorting of call numbers</h3>
<div id="output1"></div>
</body>
Thanks in advance to anyone who could help me out!
Cache this:
locCallClass.prototype.sortCallNumbers = function(callnumbers) {
var self = this;
var sorted = callnumbers.sort(function(a, b) {
return self.localeCompare(a, b);
});
return sorted;
}
Or hard bind the sort's callback function to this:
locCallClass.prototype.sortCallNumbers = function(callnumbers) {
var sorted = callnumbers.sort(function(a, b) {
return this.localeCompare(a, b);
}.bind(this));
return sorted;
}

Categories

Resources