Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I was hoping someone could point me in the direction of a link/article that would help me understand refactoring JavaScript. I tend to use alot of nested if/else statements and want to learn to clean up code.
Any explanation on how you would refactor the code below would help tremendously. Thanks in advance!
var yourself = {
fibonacci : function(n) {
if (n === 0) {
return 0;
}
if (n === 1) {
return 1;
}
else {
return this.fibonacci(n - 1) +
this.fibonacci(n - 2);
}
}
};
For me, refactoring is mostly a matter of style and readability. First I noticed that the first two if branches look almost identical. Surely I can combine them somehow? So I did this:
var yourself = {
fibonacci : function(n) {
if (n === 0 || n === 1) {
return n;
}
else {
return this.fibonacci(n - 1) + this.fibonacci(n - 2);
}
}
};
Not too shabby. What else can we do? The if branches don't actually need braces. This is actually a dangerous habit, but I'll explain later.
var yourself = {
fibonacci : function(n) {
if (n === 0 || n === 1)
return n;
else
return this.fibonacci(n - 1) + this.fibonacci(n - 2);
}
};
Now let's get exotic and use the ternary operator!
var yourself = {
fibonacci : function(n) {
return (n === 0 || n === 1) ? n : this.fibonacci(n - 1) + this.fibonacci(n - 2);
}
};
Wow that's pretty ugly isn't it? Hard to read, all on one line, but so concise.
That's why it's important to re-evaluate every time you refactor.
Ask yourself: Is this easy to understand? Can I tell at a glance just what's going on? In that last example we lost a lot of indentation which is helpful for figuring out the general structure and flow of your code. We lost some helpful visual information by preferring concise code.
I wouldn't go with the second refactor attempt either. Removing those braces is a bad idea because it only works with one-line branches. If you go back later and add a second line, but you forget to add the braces back then you'll get a very cryptic error when you try to run your code.
I think your original code is actually great. It's concise and clear, and easy to understand. It literally spells out the definition of the fibonacci function: "If it's 0 or 1, return that. Otherwise, recursively compute the result."
Refactoring comes from experience, from twiddling with the code until it looks right, and from constantly asking yourself whether this is really an improvement. Value clarity above all, then performance, and be wary of conciseness.
For some extra reading, the AirBnB style guide is well regarded:
https://github.com/airbnb/javascript
Just use a cache.
var yourself = {
cache : {},
fibonacci : function(n) {
if (n === 0) {
return 0;
}
if (n === 1) {
return 1;
}
if(this.cache[n]){
console.log('cache return');
return this.cache[n];
}
else {
this.cache[n] = this.fibonacci(n - 1) +
this.fibonacci(n - 2);
return this.cache[n];
}
}
};
console.log(
yourself.fibonacci(12)
)
In this case I think I would note that a return is a return (exit path) from the code. The else should be unnecessary.
var yourself = {
fibonacci : function(n) {
// Return quickly for the trivial cases
if (n < 2) { return n; }
// ...otherwise calculate it recursively
return this.fibonacci(n - 1) + this.fibonacci(n - 2);
}
};
A good book is Code Complete. I can't tell you if the latest revision includes JavaScript yet it goes into great detail about cleaning up code and the benefit of good source code formatting.
Others will strongly disagree to the fact that I didn't add a hard return after the if statement and yet I find this short test-and-exit code style to make for faster reading.
Related
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 11 months ago.
Improve this question
Okay. So, I'm quite a newbie to JS, but not to programming in general. I've written a little bit of a recursive function (don't mind the logic here), that works perfectly in Python but returns undefined in JavaScript. This may totally be a noob thing here, but I can't really find the source of this issue, especially as it works fine in Python.
I had initially thought it was a control flow issue but I wrote other control flow functions that worked fine. Also, right before the final return statement, I console.log the array and the values are returned. Any help will be appreciated
path = []
let find_multiplier = (numb) => {
if (numb === 1) {
console.log("a result is possible for this number");
console.log(path)
return path;
} else if (numb > 1) {
if (numb % 3 === 0) {
path.push("multiply by 3");
find_multiplier(numb / 3);
} else {
path.push("add 5");
find_multiplier(numb - 5);
}
} else {
return false
}
};
console.log(find_multiplier(13)); // returns undefined. should return values.
console.log(find_multiplier(15)); // returns undefined. should return false.
The problem here is that 2 of your 4 conditions does not return anything.
You can fix this by adding the return keyword in front of your recursive call.
path = []
let find_multiplier = (numb) => {
if (numb === 1) {
console.log("a result is possible for this number");
console.log(path)
return path;
} else if (numb > 1) {
if (numb % 3 === 0) {
path.push("multiply by 3");
return find_multiplier(numb / 3); //here
} else {
path.push("add 5");
return find_multiplier(numb - 5); //here
}
} else {
return false
}
};
console.log(find_multiplier(13)); // returns undefined. should return values.
console.log(find_multiplier(15)); // returns undefined. should return false.
I believe two of your cases are missing return statements:
if (numb % 3 === 0){
path.push("multiply by 3");
return find_multiplier(numb / 3); // <-- add return here
} else {
path.push("add 5");
return find_multiplier(numb - 5); // <-- and here
}
However, you are also mutating a variable (path) outside of the scope of your function, so I would guess even they way you have it currently, path may actually contain the result you're looking for. I would choose to either mutate the out-of-scope path array and not try to also return it, or declare the variable in your function and return that, with a preference to the latter.
So I was doing this popular challenge "palindrome" and I was copying this solution from the "Frontend Masters" Javascript Series and I'm getting a different output. I want to know if there is something that changes or am I missing something. This is my first question on StackOverflow because this is just MindF***k.
What is going on?
'use strict'
function isPalindrome(str) {
if (str.length <= 1) return true;
var first = str[0];
var last = str[str.length - 1];
if (first === last) {
console.log(str.substring(1, str.length - 1))
isPalindrome(str.substring(1, str.length - 1));
}
return false;
}
console.log(isPalindrome("abcdcba")) // Return false on my machine
I try this on the RunJS app as well as the VScode terminal and also I run Node on the file.
KEEP RETURNING FALSE !!
The function will return true if and only if the length of the input is <= 1:
if (str.length <= 1) return true;
Which it isn't:
isPalindrome("abcdcba")
The only other return statement in the function is:
return false;
It looks like you meant to return the recursive result:
return isPalindrome(str.substring(1, str.length - 1));
Otherwise the function never does anything with the result from calling itself recursively, and just defaults to returning false on the last line.
I fixed your code...
'use strict'
function isPalindrome(str) {
if (str.length <= 1) return true;
var first = str[0];
var last = str[str.length - 1];
if (first === last) {
console.log(str.substring(1, str.length - 1))
return isPalindrome(str.substring(1, str.length - 1));
}else return false;
}
console.log(isPalindrome("abcdcba")) //
I see recursive functions having two different parts:
Going forward when the function starts calling itself and stacking function calls.
in a reverse way going backward when all the stack functions start returning values. In your example this phase is kickoff just after if (str.length <= 1) return true or if first !== last.
I think you you did it totally right going forward, but there was a minor details in the come back. You need to remember that ones you have your solution you need to return values through all the calls back to the initial one.
A recursive function is kind of split in two:
function recursive{
//forward processing
..
resultValue = recursive()
..
// backward processing if needed
return kindOfResultValue //resultValue or a transformation if needed
}
Note: Remember to check all conditional branches of your recursive function to return always a value after calling itself
I noticed that all the deep equality implementations I've found are using recursion and theoretically the iterative form should be faster. However, it's a bit slower for me and I don't understand why.
Assume the data is the result of JSON.parse (i.e. primitives, plain objects, and arrays).
Recursive:
function equals1(x, y) {
if (x === y) return true;
if (Array.isArray(x) && Array.isArray(y)) {
if (x.length !== y.length) return false;
for (let i = 0; i < x.length; i++) {
if (!equals1(x[i], y[i])) return false;
}
return true;
}
if ((typeof x !== 'object') || (typeof y !== 'object')) return false;
const xKeys = Object.keys(x);
const yKeys = Object.keys(y);
if (xKeys.length !== yKeys.length) return false;
for (const k of xKeys) {
if (!y.hasOwnProperty(k)) return false;
if (!equals1(x[k], y[k])) return false;
}
return true;
}
Iterative:
function equals2(a, b) {
const stack = [a, b];
let idx = 2;
while (idx > 0) {
const x = stack[idx - 1];
const y = stack[idx - 2];
idx -= 2;
if (x === y) continue;
if (Array.isArray(x) && Array.isArray(y)) {
if (x.length !== y.length) return false;
for (let i = 0; i < x.length; i++) {
idx += 2;
if (idx > stack.length) stack.push(x[i], y[i]);
else {
stack[idx - 1] = x[i];
stack[idx - 2] = y[i];
}
}
} else {
if ((typeof x !== 'object') || (typeof y !== 'object')) return false;
const xKeys = Object.keys(x);
const yKeys = Object.keys(y);
if (xKeys.length !== yKeys.length) return false;
for (const k of xKeys) {
if (!y.hasOwnProperty(k)) return false;
idx += 2;
if (idx > stack.length) stack.push(x[k], y[k]);
else {
stack[idx - 1] = x[k];
stack[idx - 2] = y[k];
}
}
}
}
return true;
}
I'm using the index instead of the traditional stack.pop approach because it's slightly faster.
JSPerf: https://jsperf.com/deep-object-compare-123/1
The data is from Reddit: https://www.reddit.com/r/javascript.json
For me, the iterative version is 20-25% slower on Chrome and Edge, and the same speed on Firefox. I tried pre-allocating the stack array and removing the continue, but it didn't change the results. As far as I know, JS engines can optimize tail-recursive functions, but this isn't tail-recursive.
Any ideas what's going on?
A major difference between the two approaches is that your recursive function is doing a normal depth-first search for the first unequal value while your iterative function is putting all the children of an array/object onto the stack before searching into last child. This causes the stack array to grow much larger than the call stack of the recursive function will ever become, and it does quite some unnecessary copying of the entire data structure into a heterogenous array instead of keeping the values in local variables.
You do a single test on one single data structure, and from that you mean you can conclude that in general, recursive equality checks are faster than iterative ones? Are you serious? From that one single test, you can conclude nothing whatsoever. Neither could I conclude (anything much) of any one test where some iterative algorithm wins. There are sooooooooooooo many recursive and iterative ways to do deep equality testing (and other things), and there are sooooooooo many data structures. I did more than one test, and my results are, very decisively: INCONCLUSIVE, see below.
But first, one thing:
There is a little bug in your code: you don't properly check for the null case. If one argument is null, and the other one is a non-null object, an error will be thrown.
That can be easily fixed by adding the following line:
if ((x===null)||(y===null)) return false;
In equals1, put it right after if (x === y) return true;, and in equals2 put it after the continue. If both arguments were null, then the line before the inserted line does the right thing and makes the code not reach the inserted line, and if only one of the arguments is null, then the inserted line will take care of false being returned, instead of an error being thrown.
I have to admit that I find your iterative version very hard to read. I just can't understand it, but very much would like to. How does it work? Could you enlighten us, please? Does it use LIFO (last in first out) stack, corresponding to depth first search, or is it based on something different? I really would like to know.
I wrote another iterative version, based on your equals1, using a FIFO queue (first in first out, corresponding to breadth first search) - I find that very much easier to read.
And I added 3 test cases to jsperf.com, here they are:
redditData
linkedList1Knodes
linkedList10Knodes
All 3 tests use your equals1 and equals2 you quoted here (with the null bug fixed) and the FIFO version I wrote.
First test uses the original reddit data from your question, second uses a linked list with 1 thousand nodes, and third uses a linked list with 10 thousand nodes.
First test confirms that that your iterative version is about 20% slower than the recursive version, and my FIFO version is in between the two, at about 10% slower than the recursive version.
In the second test, your iterative version is the clear winner, it is much faster than the recursive version, and FIFO comes in last (it's a teeny wee bit slower than the the recursive).
In the third test, the recursive version crashes - stack overflow error, and again your iterative version is the winner (FIFO being about 30% slower)
Sorry I can't explain to you why all that is. A proper explanation probably would have to shed light onto many different aspects, I don't think there is a "singular elephant" explaining it all; maybe it makes sense to add more heterogeneous test cases, instead just one example from reddit (even if that is "real world"...)
And here is the FIFO iterative version
function equals_FIFO(x, y){
if (x===y) return true;
if ((x===null)||(y===null)||((typeof x)!=='object')||((typeof y)!=='object')) return false;
var xStack = [x], yStack = [y];
var currentIdx = 0;
var item1, item2, kid1, kid2, keys1, keys2, i, key;
while (currentIdx<xStack.length){
item1 = xStack[currentIdx];
item2 = yStack[currentIdx];
keys1 = Object.keys(item1);
keys2 = Object.keys(item2);
if (keys1.length!==keys2.length) return false;
for (i=0; i<keys1.length; i++){
key = keys1[i];
if (!item2.hasOwnProperty(key)) return false;
kid1 = item1[key];
kid2 = item2[key];
if (kid1!==kid2){
if ((kid1===null)||(kid2===null)||((typeof kid1)!=='object')||((typeof kid2)!=='object')) return false;
xStack.push(kid1);
yStack.push(kid2);
}
}
currentIdx++;
}
return true;
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
could you please your help.
how to answer this question ?
I need your help thinks
function factorial(n) {
if (n === 0) {
return 1;
} else {
return n * n ;
}
}
factorial(6)
this is not right.. i need to use writing code with Loop javascript.
Get the factorial of n. The inheritance of n is the number 1 through n multiplied by all numbers.
example)
answer should be like this
factorial(4); // => 24
factorial(5); // => 120
One way is to use a simple for loop.
var answer = 1;
for(var i = 2; i <= n; i++)
{
answer *= i;
}
return answer;
You can also do this recursively by multiplying your input by the result of the previous value.
function factorial(n){
if(n <= 1) return 1;
return n * factorial(n-1);
}
I find myself in this position occasionally, and I'm sure there is a better way to do it than I am currently.
In this example, I'm trying to sort a group of times where I have conflicting items. I need to know what times are high-priority,can't be moved, vs low priority, can be moved.
But I'm pretty sure this code is very inefficient.
var holdLowPriorities = [];
for (conflict = 0; conflict < sortedTimes.length - 1; conflict++) {
var firstConflictTimeStart = sortedTimes[conflict][0];
var firstConflictTimeEnd = sortedTimes[conflict][1];
var secondConflictTimeStart = sortedTimes[conflict + 1][0];
var secondConflictTimeEnd = sortedTimes[conflict + 1][1];
if (firstConflictTimeStart < secondConflictTimeEnd &&
firstConflictTimeEnd > secondConflictTimeStart) {
// are either of the conflicts a high priority
var firstContactPriority = sortedTimes[conflict][2];
var secondContactPriority = ortedTimes[conflict + 1][2]
if (firstConflictPriority == 2) {
//this is high priority, can't move
}
if (secondConflictPriority == 2) {
// this is also a priority, but has to move
}
// are either of the conflicts a low priority?
if (firstConflictPriority == 0) {
// this is a low priority so I can adjust the time
} else if (secondConflictPriority == 0) {
// this is a low priority so I can adjust the time
}
}
}
Unfortunately, I don't even know what to call this type of a problem, and therefore don't know what to look for, though I'm sure the answer isn't overly complicated (I hope not anyway).
A switch statement might help clean up your code a bit.
Edit: you've altered the question so most of this becomes irrelevant.
Here is a simple clarification of it.
var holdLowPriorities = [];
for(conflict=0; conflict<sortedTimes.length-1; conflict++){
var conflictTime = sortedTimes[conflict],
nextConflictTime = sortedTimes[conflict + 1];
if (conflictTime[0] >= nextConflictTime[1] || conflictTime[1] <= nextConflictTime[0]) {
continue;
}
// are either of the conflicts a high priority
if (data[conflictTime[2]].stepsArr[conflictTime[3]].priority==2) {
alert(data[conflictTime[2]].stepsArr[conflictTime[3]].
}
if (data[nextConflictTime[2]].stepsArr[nextConflictTime[3]].priority == 2) {
alert(data[nextConflictTime[2]].stepsArr[nextConflictTime[3]].
}
// are either of the conflicts a low priority?
if (data[conflictTime[2]].stepsArr[conflictTime[3]].priority==0) {
holdLowPriorities.push([conflictTime[2], conflictTime[3], conflict]);
} else if (data[nextConflictTime[2]].stepsArr[nextConflictTime[3]].priority == 0) {
holdLowPriorities.push([nextConflictTime[2], nextConflictTime[3], conflict+1])
}
//alert(data[nextConflictTime[2]].stepsArr[nextConflictTime[3]].prerequisite+' '+conflictTime[0]+' '+conflictTime[1]+' '+nextConflictTime[0]+' '+nextConflictTime[1]+' '+data[nextConflictTime[2]].stepsArr[nextConflictTime[3]].taskid+' / '+data[conflictTime[2]].stepsArr[conflictTime[3]].taskid);
}
Then this can be made more obvious by using a helper method and a couple more variables.
function conflictData(conflict) {
return data[conflict[2]].stepsArr[conflict[3];
}
var holdLowPriorities = [];
for(conflict=0; conflict<sortedTimes.length-1; conflict++){
var conflictTime = sortedTimes[conflict],
nextConflictTime = sortedTimes[conflict + 1];
if (conflictTime[0] >= nextConflictTime[1] || conflictTime[1] <= nextConflictTime[0]) {
continue;
}
var thisConflictData = conflictData(conflictTime),
nextConflictData = conflictData(nextConflictTime);
// are either of the conflicts a high priority
if (thisConflictData.priority == 2) {
alert(thisConflictData);
}
if (nextConflictData.priority == 2) {
alert(nextConflictData);
}
// are either of the conflicts a low priority?
if (thisConflictData.priority == 0) {
holdLowPriorities.push([conflictTime[2], conflictTime[3], conflict]);
} else if (nextConflictData.priority == 0) {
holdLowPriorities.push([nextConflictTime[2], nextConflictTime[3], conflict+1])
}
//alert(nextConflictData.prerequisite + ' ' + conflictTime[0] + ' ' + conflictTime[1] + ' ' + nextConflictTime[0] + ' ' + nextConflictTime[1] + ' ' + nextConflictData.taskid + ' / ' + thisConflictData.taskid);
}
When expressed like this, I think you can start to see probable bugs; high priority has conflictTime and nextConflictTime checks as if, if, whereas low priority has if, else if. Is this what is desired? You may be able to then put this/next conflict priority in a switch or reorganise the code more. But I think it's now at the stage where it can be understood more readily.
In terms of data structures, there is nothing inherently inefficiently with if..else blocks. Even nested if..else are not a problem. In terms of readability it's another matter altogether. As a rule of thumb, large and deeply nested if..else blocks are hard to follow (for a human, computers of course have no problems with them).
First of all, I'd go with Mitch's suggestion to use intermediate variables to reduce code verbosity. In terms of efficiency it is definitely less memory efficient (though for javascript, it could potentially speed up access depending on the browser). But efficiency is not the point, code clarity is.
Secondly, use array literal syntax instead of new Array() : holdLowPriorities.push([...]). Again, less noise on the screen, the easier it is to see what's going on.
Third, there are several places where all you're doing is checking the priority of something. Use either a helper function or a method to simplify the code here: checkProirity(sortedTimes,conflict,2) or sortedTimes.checkProirity(conflict,2). Again, a function/method call is inherently less efficient but the point is to improve code clarity for good readability.