Skip to content

Instantly share code, notes, and snippets.

@moonwave99
Created March 30, 2023 09:54
Show Gist options
  • Save moonwave99/7b29ed3a43005ad07dc9691dd52f4a60 to your computer and use it in GitHub Desktop.
Save moonwave99/7b29ed3a43005ad07dc9691dd52f4a60 to your computer and use it in GitHub Desktop.
Function exercise

Understanding instructions

Given the following task:

Write a function named max that accepts two numbers as parameters, and returns the greater of the two.

Let's parse this sentence as a programmer:

  • write a function;
  • the function should be named max;
  • the parameters should be two;
  • in JS we cannot enforce data types, so we have to trust that we are passed numbers;
  • the function should return the greater of the parameters.

Even though you can start writing the function right away, let's first write just the signature of the function, i.e. the function with an empty body and no implementation.

The first challenge is how to name the parameters: while the function name is given, how should we call the passed numbers? Since they don't carry any particular meaning, using a and b is entirely fine:

function max(a, b) {
    // we'll write the code later
}

In another context, like in calculating the item price for a cart item, you would like to be more explicit - think of function getTotal(itemCount, itemPrice, discount) { ... } where the parameters play a very specific role and should be named carefully.

After that, let's set an expectation by writing some test cases:

function max(a, b) {
    // we'll write the code later
}

console.log(max(2, 3)); // should log 3
console.log(max(3, 2)); // should log 3
console.log(max(-5, 2)); // should log 2

How many cases should you write? For now we are just testing that the function works regardless of the order of the arguments (that is, the function is symmetric), and that negative numbers don't do anything weird.

Important: those test cases are not going to work...because the function is empty yet! But now we have something to check, without having to go back and forth between the implementation and the function calls.

function max(a, b) {
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

console.log(max(2, 3)); // logs 3
console.log(max(3, 2)); // logs 3
console.log(max(-5, 2)); // logs 2

Wonderful! Let's add a final touch by removing the else block, since there is no code after it:

function max(a, b) {
    if (a > b) {
        return a;
    }
    return b;
}

Once can go even further and use the dreaded ternary operator:

function max(a, b) {
    return a > b ? a : b;
}

Is there any benefit in this additional simplification? Not that much actually, because the code looks more obscure if you are not familiar with the syntax; albeit every programmer should be familiar with the ternary operator, this specific function fulfills its purpose without any additional refactoring.

Let's not forget that Math.max(a, b) is a thing, so you don't need to invest any additional time writing this function!

Bonus: add type checking

Even though we cannot prevent passing non numerical values from outside, we can make some considerations from within the function:

function max(a, b) {
    if (typeof a !== 'number' || typeof b !== 'number') {
        throw new Error('Both arguments should be numbers');
    }
    if (a > b) {
        return a;
    }
    return b;
}

Now trying to call max(2, 'hello') will throw an error:

Uncaught Error: Both arguments should be numbers
    at max (...)
    at ...
```

Should we do this considerations for all functions that we write? Ideally yes, but that it's incredibly tedious, that's why people invented Typescript and you'll have to learn it in the future.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment