Question
There can’t be a simpler question than this… All you have to do is finish the makeNegative function, which takes a number and returns a negative number regardless of the value…
function makeNegative(num) {
// Finish your code here
}
Instructions:
Input is only going to be a number, so don’t worry about checking it’s type.
If the input is a positive number, then it should convert it to a negative number.
If the input is zero, then it should return zero.
If the input is a negative number, then it should stay negative and return as it is.
Approach #1: Very Basic, Very Verbose
Now that we understand the simplest question, we can proceed with a solution. If we analyze what the question asks us to do, we realize that the final result is returned based on a condition such as,
Return zero, if the input number is zero.
Return the negative version, if the input number is positive. (just multiply with -1)
Return the input number as it is, when the input is negative.
And when we put it on code, we get…
function makeNegative(num) {
// checking for zero
if (num === 0) {
return 0;
}
// checking for positive number
if (num > 0) {
return num * -1;
}
// checking for negative number
if (num < 0) {
return num;
}
}
This is a pretty basic implementation and obviously not an optimal one, BUT… If you observe carefully, you can see that we have translated all three possible test cases into code. In other words, this is a practical (but very basic, very small) implementation of TDD.
Well, not the whole of it, but a starting step. Now, all we gotta do is iterate over this implementation by refactoring the code that could lead us to the most optimal solution, just like how we do things in a TDD environment.
Approach #2: Basic Optimization
Now that we have got our base implementation of a working solution, let’s refactor it a bit with small things that can make a lot of sense. So, let’s list out the obvious,
Merging Similar Results
If you observe closely, there are two cases where we’ll be returning the input as it is. It is when the input is zero or a negative number. So, let’s combine them…
function makeNegative(num) {
// checking for positive number
if (num > 0) {
return num * -1;
}
// checking for zero or negative number
if (num <= 0) {
return num;
}
}
Great! Now when the code executes, there are only two conditional statements instead of three. And if you ever studied conditional statements in any programming language, then you might know it’s better to have a single if-else
conditional statement rather than two if
conditional statements. So, let’s refactor it again…
function makeNegative(num) {
if (num > 0) {
return num * -1;
} else {
return num;
}
}
Awesome! Now it’s more optimized as we have a single conditional statement instead of three or two. We have optimized from a total of three conditions evaluation to a single evaluation.
To be honest, this might not make much sense in the current scenario as the control returns from the function to the executor and it wouldn’t matter if we used three if
s or even a single if-else
statement.
Nevertheless, I recommend using if-else
as it often yields better results in most cases and can help simplify future refactoring.
A Better Way to Negate
It’s common for anyone to say just “multiple with -1” to negate its value, but what if I say there is a better way to negate a number with a different type of operator? If your answer is -
as in unary operator, then you’re absolutely right…
Operations that use a unary operator are optimized by the compiler/interpreter and -
as in unary operator does a better job at negating the given number, even better than redundant multiplication with -1
which, of course, uses the *
binary operator.
So, here’s our refactored code…
function makeNegative(num) {
if (num > 0) {
return -num;
} else {
return num;
}
}
That’s great, we’re one more step closer to getting a better solution.
Wondering why Unary operators are better than Binary operators (in situations like this)???…
*
binary operator to negate values sign using -1, it takes more space and computes power compared to the -
unary operator.Possible One-liner
Doesn’t that if-else
statement look redundant to you ???… Please don’t tell me that I’m the only one.
I mean, all we need to do is return a single value based on a single condition that returns a boolean value. Okay fine, I’m gonna stop beating around the bush. It’s the ternary operator. Yea, I know people might say it’s more language-dependent… but guess what genius, most widely used and popular languages have it.
So why don’t you cut us some slack and do the refactoring which looks like this,
function makeNegative(num) {
return num > 0 ? -num : num;
}
Well Done!, You have reached one of the most optimal solutions which most of us have gotten even without going through this much of a thought process.
I didn’t mean it sarcastically, but more of a “point the obvious” kinda thing as many people don’t even care to think this way as they say “It’s a waste of time”. But I think this is the foundation of problem-solving, understanding your tools and making your best move to achieve the most optimal and viable solution for your problem.
Approach #3: Native Methods
That’s one optimal solution, but it’s not always the right way to solve when you’re dealing with a huge project. In a huge project, people expect you to deal with abstraction.
A word of advice
Even though some of these approaches might not be optimal and some might even add some overhead, they make our lives as developers more easier to maintain and troubleshoot huge code bases. So, always remember and try to solve problems using abstractions like native methods. And who knows, it might lead to an optimal solution that might contribute to the whole application rather than a section of it.
I know these native methods might vary from language to language, but in most popular languages there might be a few similarities that can bridge the gap between these implementations.
Coming to our solution
We can see that we’re returning the same value with or without -
unary operator. So, can’t we just make the input value (that could be any number) a positive number so that we could negate it anyway? Thus voiding the dual use of the same value with and without -
unary operator.
If only I could make the input number positive without writing additional code… Wait, I do remember something like that from my math class… It’s called absolute value. When we place a given number between modulus it results in a positive number.
$$\vert{x}\vert = \begin{cases} x & (x \space \geqq \space 0) \\ -x & (x \space \lt \space 0) \end{cases}$$
$$\vert\pm{x}\vert = + \space x$$
I’m glad I remember that, but if only there had been a way to implement that in JavaScript. Hold on, how did I even forget that JavaScript has Math
object 🤦♂️!!!…
Now, I can just use the absolute method to refactor my current code for an even better solution that would look something like this…
function makeNegative(num) {
return -Math.abs(num);
}
Fantastic!!!… Now that we have used a native method, we don’t have to work on performance as it’s an abstraction dealt with by the developer up holding optimization under the hood (you know that scary low-level stuff, yea that one).
Conclusion
Now you have gone through the whole chain of thought to arrive at the optimal solutions, let’s summarize everything for one last time…
We have started off with a not-so-optimal solution that meets all the constraints for the required task, just like how we do things in the TDD environment. Then we iterated over our solution, refactoring our solution with all the basic programming concepts, optimizing one thing at a time and ended up on the following optimal solution,
function makeNegative(num) {
return num > 0 ? -num : num;
}
which is the most common optimal solution that people arrive at… Then we went a step ahead and used mathematical functions using the native method from the Math
object and arrived at our final, more desirable, abstract, optimal solution.
function makeNegative(num) {
return -Math.abs(num);
}
Thank you for sticking with me till the end, I hope you learnt a thing or two from my chain of thought for deriving this optimal solution.