I noticed something weird while reviewing this mergesort implementation on Code Review…
/************************************************************
* Mergesort implementation
***********************************************************/
function sort(array) {
var len = array.length;
var middle = Math.floor(len*0.5);
var left = array.slice(0,middle);
var right = array.slice(middle, len);
if (len == 1) {
return array;
} else {
}
return merge(sort(left), sort(right));
}
function merge(left, right) {
var a = left.length;
var b = right.length;
if (a > 0 && b > 0) {
if (left[0] > right[0]) {
return [].concat(left[0], merge(left.slice(1,a), right));
} else {
return [].concat(right[0], merge(right.slice(1,b), left));
}
} else if (a == 0) {
return right;
} else of (b == 0)
return left;
}
/************************************************************
* Demonstration
***********************************************************/
function doSort() {
var array = document.getElementById('in').value.split(/[, ]+/).map(function(e) {
return parseInt(e);
});
var sorted = sort(array);
document.getElementById('out').value = sorted;
}
function generateRandom(len) {
var array = [];
for (var i = 0; i < len; i++) {
array.push(Math.round(Math.random() * 100));
}
document.getElementById('in').value = array;
};
generateRandom(20);
<button onclick="generateRandom(20)">⬇︎ Generate random numbers ⬇︎</button>
<div><input id="in" size="80"></div>
<button onclick="doSort()">⬇︎ Sort ⬇︎</button>
<div><input id="out" size="80" disabled></div>
The last conditional branch is else of rather than else if. Normally, else of should result in a syntax error. Yet, no matter how hard I try, I can't trigger the syntax error — it always successfully returns an array sorted in descending order!
I know, else of (b == 0) could just be replaced by else, but still, I want to know: How could this code possibly work?
This works because of a combination of 2 "bad things" about Javascript: skipping braces in block statements that contain only a single statement, and semicolon insertion.
Your if statement, properly braced, should look like this:
if (a > 0 && b > 0) {
if (left[0] > right[0]) {
return [].concat(left[0], merge(left.slice(1,a), right));
} else {
return [].concat(right[0], merge(right.slice(1,b), left));
}
} else if (a == 0) {
return right;
} else of (b == 0) {
return left;
}
but, because of the missing braces and semicolon insertion, Javascript is seeing/parsing it like this:
if (a > 0 && b > 0) {
if (left[0] > right[0]) {
return [].concat(left[0], merge(left.slice(1,a), right));
} else {
return [].concat(right[0], merge(right.slice(1,b), left));
}
} else if (a == 0) {
return right;
} else {
of(b == 0);
}
return left;
If you always pass in legitimate left and right arrays then this last else branch is never reached, hence why you haven't seen an exception.
If you pass in an empty right array, it will reach the last branch and throw of is not a function:
merge([10, 20, 30], []);
Any respectable coding standard should explicitly require these 2 "features" of Javascript never be used... but that's just one opinion.
of is a keyword in ES6, used for iterating over objects..
but in this case the of is behaving as function not keyword..
The function code never goes to else of (b == 0) return left; part.. so compiler is not throw ReferenceError: of is not defined
if you will change of keyword into another word like else often(b==0).... then also code work
when you send right side empty then code will throw error ReferenceError: of is not defined, so finally this is only typo.
Related
I am a newbie who is trying hard to have a grip on javascript. please help me to consolidate my fundamentals.
input will be a string of letters.
following are the requirements.
function should return true if following conditions satisfy:
letters are in alphabetical order. (case insensitive)
only one letter is passed as input. example :
isAlphabet ('abc') === true
isAlphabet ('aBc') === true
isAlphabet ('a') === true
isAlphabet ('mnoprqst') === false
isAlphabet ('') === false
isAlphabet ('tt') === false
function isAlphabet(letters) {
const string = letters.toLowerCase();
for (let i = 0; i < string.length; i++) {
const diff = string.charCodeAt(i + 1) - string.charCodeAt(i);
if (diff === 1) {
continue;
} else if (string === '') {
return false;
} else if (string.length === 1) {
return true;
} else {
return false;
}
}
return true;
}
It's generally a better practice to start your function off with dealing with the edge-cases rather than putting them somewhere in the middle. That way, the function returns as soon as it can - and it's a lot easier to read than a waterfall of if..else statements.
function isAlphabet(letters) {
if ("" == letters) {
return false;
}
if (1 == letters.length) {
return true;
}
const string = letters.toLowerCase();
// carry on with your loop here.
}
You've got the right idea, but it can be simplified to just fail on a particular error condition, i.e when a smaller character follows a larger one:
function isAlphabet(letters) {
const string = letters.toLowerCase();
let lastChar;
for (let i = 0; i < string.length; i++) {
// Grab a character
let thisChar = string.charCodeAt(i);
// Check for the failure case, when a lower character follows a higher one
if (i && (thisChar < lastChar)) {
return false;
}
// Store this character to check the next one
lastChar = thisChar;
}
// If it got this far then input is valid
return true;
}
console.log(isAlphabet("abc"));
console.log(isAlphabet("aBc"));
console.log(isAlphabet("acb"));
You can use the simple way to achieve the same as below
function isAlphabet(inputString)
{
var sortedString = inputString.toLowerCase().split("").sort().join("");
return sortedString == inputString.toLowerCase();
}
console.log("abc = " + isAlphabet("abc"));
console.log("aBc = " + isAlphabet("aBc"));
console.log("acb = " + isAlphabet("acb"));
console.log("mnoprqst = " + isAlphabet("mnoprqst"));
Note: Mark the answer is resolves your problem.
I'm tackling a recursion problem that returns a string of "hi"'s where the first "hi" has a capital H and the string ends with an exclamation point. I have the code below so far but I'm not sure how to prevent subsequent occurrences of "hi" having a capital H. Any guidance would be welcome.
function greeting(n) {
if (n === 0) {
return "";
} else if (n === 1) {
return "Hi!"
} else {
return `${'Hi' + greeting(n - 1)}`
}
}
console.log(greeting(3)) // should return Hihihi!
console.log(greeting(5)) // should return Hihihihihi!
One way to work around your problem is to pass a flag to the function which indicates whether this is the first call, and only in that case capitalise the hi. Note that you can simplify the code slightly by returning a ! when n == 0; then you don't need to special case n == 1:
function greeting (n, first = true) {
if (n === 0) {
return "!";
}
else {
return `${(first ? 'Hi' : 'hi') + greeting(n - 1, false)}`
}
}
console.log(greeting(3)) // should return Hihihi!
console.log(greeting(5)) // should return Hihihihihi!
Just a .toLowerCase() is missing in your code.
`${'Hi' + greeting(n - 1).toLowerCase()}`
You don't need the n === 1 step.
function greeting(n) {
if (n === 0) {
return "!";
} else {
return `${'Hi' + greeting(n - 1).toLowerCase()}`
//------------------------------^^^^^^^^^^^^^^
}
}
console.log(greeting(3)) // should return Hihihi!
console.log(greeting(5)) // should return Hihihihihi!
I have read about this error on other threads that mostly ask the user to remove semicolons. This seems to have no effect on my code.
function isEven(number){
if (number > 1)
return isEven(number-2);
else if (number == 1)
return false;
else if (number == 0)
return true;
else if (number < 0)
number *= -1
return isEven(number);
else
return "Error";
}
It seems that this line [number *= -1] is causing the error as if I remove it the code runs with no error except for negative numbers where the stack runs out of memory. What I'm trying to do is to make all numbers positive.
Anyone that would like to help me out?
Solution
function isEven(number){
if (number < 0){
number *= -1
return isEven(number);}
else if (number > 1){
return isEven(number-2);}
else if (number == 1){
return false;}
else if (number == 0){
return true;}
else
return "Error";
}
Short answer: Wrap your codes with {}
if (){
..
..
}else {
..
..
}
Long answer :
else if (number < 0)
number *= -1
return isEven(number);
You are not using {} hence the very next line of the condition only considered as a statement belong to the else if and the later will be treated as general statements.
Since you have 2 lines of code in else if, the link broken there and your else became an orphan. That is the reason you seeing the error. Please wrap up your conditions in {}
function isEven(number){
if (number > 1){
return isEven(number-2);
}
else if (number == 1){
return false;
}
else if (number == 0){
return true;
}
else if (number < 0){
number *= -1
return isEven(number);
}
else{
return "Error";
}
}
Note: Not only to avoid this specific error but to avoid many other weird things always try to use {}. That makes everyones life easy.
Problem is without curly brackets {}
Better you could add {}(curly brackets) on each if and else statement
function isEven(number) {
if (number > 1) {
return isEven(number - 2);
} else if (number == 1) {
return false;
} else if (number == 0) {
return true;
} else if (number < 0) {
number *= -1
return isEven(number);
} else {
return "Error";
}
}
console.log(isEven(-2))
The problem is that you are trying to execute 2 lines of code in the 3rd else if without wrapping it up in curly braces.
By default only single line gets executed when you don't wrap up the block in curly braces.
function isEven(number){
if (number > 1){
return isEven(number-2);
}
else if (number == 1){
return false;
}
else if (number == 0){
return true;
}
else if (number < 0){
number *= -1;
return isEven(number);
}
else{
return "Error";
}
}
I'm was just fiddling around with javascript and I wrote function using Math.random that I thought would return a coin-flip. Then I was curious so I ran it through a loop to test how often it flips true/false. I found out my loop is skipping about half of the else if conditions, and was able to verify this by catching them in the errors var. So, why is it doing this?
var truths = 0;
var alternativetruths = 0;
var errors = 0;
function game() {
var score = Math.random()*10;
return score>5;
}
for(i=0;i<999999;i++) {
if (game() === true) {
truths++
} else if (game() === false) {
alternativetruths++
} else {
errors++
}
}
console.log("truths:",truths,"alternativetruths:",alternativetruths,"errors:",errors)
truths: 500393 alternativetruths: 249580 errors: 250026
Your code calls game() twice. If the first call isn't true, then the second might or might not be true.
Just call game() once and assign the result to a variable. Don't make explicit comparisons to true and false; if statements work off boolean values, and your function already returns one of those.
for (var i = 0; i < 999999; i++) {
var result = game();
if (result)
truths++;
else
alternativetruths++;
}
Because you're calling game twice, and thus getting two different random flags.
The minimal change is to remember the number:
for(i=0;i<999999;i++) {
var flag = game();
if (flag === true) {
truths++
} else if (flag === false) {
alternativetruths++
} else {
errors++
}
}
But a couple of other notes:
If flag is a boolean (and it is, it's the result of the > operator), then if (flag === true) is pointless. Just use if (flag). The result of the === operator is a boolean too, so if you don't trust them to be true or false, where do you stop? :-) if ((flag === true) === true)?
Separately, if (flag === false) should just be if (!flag).
If flag is a boolean (and it is), then if you have if (flag), having else if (!flag) and then else doesn't make any sense. There is no way you'll reach that final else. Just if (flag) { } else { } is all you need.
But, game isn't fair, it has a very slight bias toward returning false. Remember, the range returned by Math.random() * 10 is 0 to just under 10, so checking for > 5 means you're skipping the midpoint. Because you're dealing with very small fractional numbers, you don't notice the bias, but it's there; it would be more obvious if you rounded to whole numbers, at which point it would be roughly 40%/60% true/false. You want >= 5 for fair results.
game can rather moer succinctly be written: return Math.random() >= 0.5;.
Because you're not declaring i, you're falling prey to The Horror of Implicit Globals (that's a post on my anemic little blog). Remember to declare your variables.
Re > vs. >=, here's an example where I've rounded to whole numbers to make the effect clearer:
for (var n = 0; n < 4; ++n) {
setTimeout(test.bind(null, n, true), 200 * n);
setTimeout(test.bind(null, n, false), 200 * n + 100);
}
function test(num, gte) {
var truths = 0;
var alternativetruths = 0;
var errors = 0;
function game() {
var score = Math.floor(Math.random() * 10);
return gte ? score >= 5 : score > 5;
}
for (var i = 0; i < 999999; i++) {
var flag = game();
if (flag) {
truths++;
} else {
alternativetruths++;
}
}
showStats(num, gte, truths, alternativetruths);
}
function showStats(num, gte, truths, alternativetruths) {
var total = truths + alternativetruths; // Should be 999999 of course
var truthsPercent = (truths * 100) / total;
var altPercent = (alternativetruths * 100) / total;
console.log(num, gte ? ">=" : ">", "truths:", truths, "alternativetruths:", alternativetruths, "(" + truthsPercent.toFixed(2) + "% vs. " + altPercent.toFixed(2) + "%)");
}
.as-console-wrapper {
max-height: 100% !important;
}
You need to assign game to a var before test for its value. Otherwise, everytime yout check the value with game() you will check a new value. So it can be false at the first check and true at the second and for this reason increment your errors.
Try this:
for(i=0;i<999999;i++) {
let gameResult = game();
if (gameResult === true) {
truths++
} else if (gameResult === false) {
alternativetruths++
} else {
errors++
}
}
I'm trying to create a script that returns the factorial of the input number as part of a challenge. When I try to run it, it returns the proper factorial, but apparently I did it wrong somehow.
It looks like this:
function FirstFactorial(num) {
if (num > 1) {
var x = num;
for (var i = 1; i < x; i++) {
num = num * i;
}
} else if (num === 1) {
return 1;
} else {
console.log("That's not a number!");
}
return num;
}
Then I tried doing it like this, but it still doesn't work!
function FirstFactorial(num) {
if (num < 0) {
num = 0;
console.log("You have to input a number!");
}
if (num === 0) {
return 1;
}
return num * FirstFactorial(num - 1);
}
The most likely reason is that they expected and intended you to use recursion (a function that calls itself).
If you think about factorials, each builds on the result of the previous one, which is the classic case for using recursion.
(Note that I'm specifically not posting code doing this with recursion, because presumably the point here is for you to work out how to do it.)