HeadFirst JS: questions in an example of code - javascript

The purpose of the function is to validate input. The input should consist of two characters: the first one A-G; the second one: 0-6; e.g A0; B2. The example is taken from the book.
I want to understand is there a point in lines such as: "guess===null (Can a string be equal to null?)" ; " column < 0 " row >= model.boardsize;
Note that model.boardsize is a specified number. In this case, it is 7.
function parseGuess(guess) {
var alphabet = ["A" , "B" , "C", "D", "E", "F", "G"];
if (guess===null || guess.length !== 2) {
alert("Oops, please enter a letter and a number on the board.");
} else {
var firstChar = guess.charAt(0);
var row = alphabet.indexOf(firstChar);
var column = guess.charAt(1);
if (isNaN(row) || isNaN(column)) {
alert("Oops, that isn't on the board.");
} else if (row < 0 || row >= model.boardSize ||
column < 0 || column >= model.boardSize) {
alert("Oops, that's off the board!");
} else {
return row + column;
}
}
return null;
};

I haven't read the book and it's not clear how this function gets called, but I would say that's a pretty poor check for determining if guess has a good, expected value. The typical reason why one would check for null first is because if guess is null, then guess.length will throw a TypeError because null does not have a length property. Since it is the first check in the OR condition, it short-circuits the second half and shows the alert() right away when guess is null.
However, an error would also be thrown if guess was undefined (a common case when an argument isn't passed in the function call) or a boolean. Additionally, objects, arrays, and other data structures also have a length property, which means they could pass the first if condition test but then methods like indexOf and charAt would throw errors. You would probably want to change it to something like:
if (typeof guess !== 'string' && guess.length !== 2) {
As for the board size, yes you want to check if the column is less than 0 or greater than the size, otherwise you will pick something out-of-bounds and your program will throw an error.

Related

Why does ```typeof(a) != Number``` //evaluate True - even when typeof(a)==Number?

Student of The Odin Project Here
https://github.com/TheOdinProject/javascript-exercises/tree/solutions/sumAll
I managed to get the sum part of it work (so it's only the first if statement that is the problem)
They want "ERROR" returned if a non-number is passed as either a or b , or if a or b is a negative number.
I used the code below.
First if statement is evaluating to true even if both a and b are numbers
Their solution was to use !Number.isInteger(a)||!Number.isInteger(b)
Any ideas why the first if statement done as below does not work as intended
If I console.log(a) it logs number
const sumAll = function(a,b) {
let c =0
if((a||b)<0 || (typeof(a)||typeof(b)) !=Number){
return "ERROR"
}
else if(a<b){
for(let i=a; i<=b; i++){
c += i;
}
return c ;
}else if(a>b){for(let i=b; i<=a; i++){
c += i;
}
return c;
}
};
If you want to individually check for a<0 and b<0, you cannot do (a||b)<0. Reason: a||b gets evaluated first and you get the first truthy value out of a and b. So, it becomes a matter of order. If you pass 1,-2 you will get 1 as your result of a||b and obviously 1<0 return false.
function func(a,b){
console.log(a||b);
console.log((a||b)<0);
}
func(1,-2);
func(-1,2);
func(1,2);
Similar problem with (typeof(a)||typeof(b)) !=Number. You are supposed to individually check ((typeof(a) != 'number') || (typeof(b) != 'number')).
Notice how I have changed Number to 'number'. typeof returns a string as mentioned in comments.
You need to amend your if conditions accordingly.

Why when I print an element of an array it starts with undefined and then the numbers that I insert

Why when I print an element of an array it starts with undefined and then the numbers that I insert. I even tried to cosnole.log(search[j]) and its exactly what I want. But for some reason I think the first += that I do is undefined.
function new_vid(){
let x=1;
console.log("################################################################");
for(let i=0; i<search1.length; i++){
if(search1[i]=="s" && search1[i+1]=="n" && search1[i+2]=="i" && search1[i+3]=="p" && search1[i+4]=="p" && search1[i+5]=="e" && search1[i+6]=="t"){
for(let j=i+36; j<i+46; j++){
console.log(search1[j]);
dates[x]+=search1[j];
}
x++;
}
}
console.log(dates[0]);
console.log(dates[1]);
console.log("##################################################################");
}
the output is:
undefined2021-08-04
undefined2021-08-01
dates[x] is undefined before the first insert. And dates[x] += search1[j] in principle does the following:
dates[x] = dates[x] + search1[j]
But undefined + "foo" results in undefinedfoo in JS. Use
dates[x] = (dates[x] || "") + search1[j]
This way dates[x] || "" will return the empty string if dates[x] is undefined, and thus concatenate the empty string with search1[j]
But as you say you are coming from a c++ background, I wonder a little bit about your careless usage of indexes without any bound-checking. And your algorithm will go out of bounds, because you are iterating your index i all over the search1 array, and then access an index i+1, i+2 or even i+36.
This will not crash in JS, but be aware that search1[j] will as well return undefined if the index j is out of bounds of your array, so you may need
dates[x] = (dates[x] || "") + (search1[j] || "")
as well.
If the variable dates is array, the 1st element basically is undefined since you haven't declared before. ​
You can do the check before adding into the dates
if(dates.length == x+1)
​dates.push(search1[j]);
else
​dates[x]+=search1[j];
If you are wondering about push, it's just the same mechanism as the function push_back() in C++ Vector.
One more thing is, this line of code will make you always find the undefined when you do the console.log in the next line.
for(let j=i+36; j<i+46; j++){
When it happen? It will happen if it's any possibility for the search1 to contain the word snippet at the end of string, then the string will not have anything at the i+36 or higher indexes.

if else preventing loop for passing through object

I am having trouble returning a statement when my RegExp finds no matches :
function ai(message) {
if (username.length < 3) {
username = message;
send_message("Oh, well hello " + username + ", My name is Donald J Trump and i'm a big boy");
} else {
for (i = 0; i <= botChat.length; i++) {
var re = new RegExp(botChat[i][0], "i");
if (re.test(message)) {
var length = botChat[i].length - 1;
var index = Math.ceil(length * Math.random());
var reply = botChat[i][index];
send_message(reply);
}
}
}
}
When I enter a phrase it can correctly match the first line in an array as per the for loop. The issue I'm having is when I try to add an else statement it ceases to loop through my array properly.
I have tried :
else if (re.test(message) === false) {
send_message("i can't be expected to know everything");
}
But it doesn't work, it prevents the loop from looping past botChat[0][0].
I've also tried :
if (send_message().val() == "") {
// ...
}
But every time my code no longer works. Is there a method of adding something to my array that says 'if you don't find a match above, choose this'?
Or just a way of working my code so that the if/else works?
attached is the codepen.
I checked your codepen and the lines 190 and 194 console.log(send_message().val()); seems to be breaking the loop because those lines are throwing an exception since send_message() returns undefined and undefined does not have the .val() method.
Your regular expressions are working fine. I recommend not creating new RegExp objects every iteration, instead, use the one defined in the array if (botChat[i][0].test(message)), the overhead will be less.

Traverse an object or array to determine if elements fit within ranges

I am building a decorator for arrays of items, the array of objects is meant to be slotted into a defined range of values if it fits there.
Currently, I am doing this using some conditionals to check for the range but the code does not feel clean enough to me.
Does anyone have any suggestions about how write this code in a more concise and expandable way?
Example of current setup...
thingsToSort.forEach(function(thing) {
if (thing > 1 || thing < 3) {
// set the item to 1
}
if (thing > 3 || thing < 5) {
// set to 3
}
})
Note: I am really looking for a better way to loop through this logic and determine if object falls in the range.
One another implementation.
Created a function to represent the Range, Range
A function to identify the range and take appropriate action. setcompareRange
Notice the usage of the some method in the function compareRange. Since a number can be found in one range only, All the ranges are not evaluated and till the matched range traversal is done.
function Range(min, max){
this.min = min;
this.max = max;
}
var rangeArray = [ new Range(1,3), new Range(3,5)];
function compareRange(c,i,arr){
var result = rangeArray.some(x=> {
return setcompareRange(c, x.min, x.max)
});
}
function setcompareRange(thing, min, max){
if (thing > min && thing < max) {
// set the item to 1
console.log("set thing = " + thing + " in range = " + min);
return true;
}
}
var thingsToSort = [2,4];
thingsToSort.forEach(compareRange);
I would first double-check your logic...
thingsToSort.forEach(function(thing) {
This conditional will set ANYTHING greater than 1 to 1, and ignore the second condition (thing < 3):
if (thing > 1 || thing < 3) {
// set the item to 1
}
You should be using an && operator to AND these two conditions:
if (thing > 1 && thing < 3) {
// set the item to 1
}
The same thing goes for this conditional which will set ANYTHING greater than 3 to 3.
if (thing > 3 || thing < 5) { //should be &&
// set to 3
}
})
You are also not breaking the loop after meeting a conditional. This means that even though you have already determined that a thing meets the first condition, you are still checking to see if it meets the other conditions. This wastes resources. Use else if to prevent this:
if (thing > 1 && thing < 3) {
// set the item to 1
}
else if (thing > 3 && thing < 5) {
// set to 3
}
Other than that, it's already pretty clean. This is very similar to the classic fizzbuzz problem, of which, there are many possible refactorings

Google Script - Consolidated operator syntax not working in IF statement

Conext
I would like my onEdit(e) function below to add or remove rows from a spreadsheet based on the new and old values of a cell. This requires the OR (||) operator in my IF statement to check the values. Below is my first attempt which sort of worked but led to some odd behavior such as rows appearing then disappearing (the second IF statement) after a single edit, two rows appearing whenever I hit the "delete" key, etc:
function onEdit(e) {
var range = e.range;
var newValue = e.value;
var oldValue = e.oldValue;
var targetRow = range.getRow();
//insert 2 rows if edited cell value = "a" or "b"
if (newValue === "a" || "b") {
sheet.insertRowsAfter(targetRow, 2);
}
//delete 2 rows if former cell value was "a" or "b"
if (oldValue === "a" || "b") {
sheet.deleteRows(targetRow + 1, 2);
}
}
Applications Currently Used
Google Sheets
Google Apps Script
What I've Tried So Far
When I changed my IF statements to restate the variable after each OR operator, the desired result was produced cleanly:
if (newValue === "a" || newValue === "b") {
sheet.insertRowsAfter(targetRow, 2);
}
if (oldValue === "a" || oldValue === "b") {
sheet.deleteRows(targetRow + 1, 2);
}
Question
Is there a consolidated form of writing these statements that will continue to produce the desired result? As I continue writing this function, these IF statements are likely to become more complex and the same variable will need to be checked using OR and AND operators. It would be much easier to only state the variable name once for each operator.
To recap:
I would like to be able to write the consolidated format of the code as shown in the first code snippet:
if (newValue === "a" || "b") { //etc...
However, it only works properly when written in the longer version:
if (newValue === "a" || newValue == "b") { //etc...
Thank you!
You could use the switch statement to consolidate.
switch (newValue) {
case "a":
case "b":
sheet.insertRowsAfter(targetRow, 2);
break;
default:
}
You could add more cases there to "OR" the overall condition if required.

Categories

Resources