Skip to content

Instantly share code, notes, and snippets.

@moonwave99
Created March 31, 2023 09:14
Show Gist options
  • Save moonwave99/4164eb717bef946c7d1e6554bce96b45 to your computer and use it in GitHub Desktop.
Save moonwave99/4164eb717bef946c7d1e6554bce96b45 to your computer and use it in GitHub Desktop.

Function Exercises

Max of an array

Write a function that accepts an array of number, and returns the greatest of them:

function max(numbers) {
    // find a strategy to determine the biggest entry of the array
    // hints:
    // declaring a variable with let
    // and looping over the numbers
    // may be helpful!
}

// test cases
console.log(max([1, 2, 3])); // logs 3
console.log(max([3, 1, 2])); // logs 3
console.log(max([-3, 1, 2])); // logs 2
console.log(max([])); // logs undefined

Min of variadic arguments

Write a function that accepts multiple arguments, and returns the lesser of them:

function min(...numbers) {
    // see above
}

// test cases
console.log(min(1, 2, 3)); // logs 1
console.log(min(3, 1, ])); // logs 1
console.log(min(-3, 1, 2)); // logs -3
console.log(min()); // logs undefined

Generate the email for a user

New friends are joining your company, and you have to provide them with a username.

Write a function that accepts two parameters: an object with firstName and lastName keys, and a string that contains the website of your company.

It should return {firstName}.{lastName}@{domain}.com (see example below).

If no lastName is provided in the object, use the first name only.

function getEmail(user, website) {
    // observe how the email is all in lowercase
    // observe how the www should not be part of the email
    // useful string tools:
    // SEE: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase
    // SEE: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
}

console.log(getEmail({ firstName: 'Jane', lastName: 'Doe'}, 'www.flamingos.com')); // logs 'jane.doe@flamingos.com'
console.log(getEmail({ firstName: 'John', lastName: 'Doe' }, 'www.acme.net')); // logs 'john.doe@acme.net'
console.log(getEmail({ firstName: 'John' }, 'www.flamingos.com')); // logs 'john@flamingos.com'

Function exercises solutions

Max of an array

While a human eye can tell the biggest number in a row with a glimpse, computers are almost blind and prefer another approach: they start from the beginning of the array, and check if the current number is bigger than any they have seen so far:

function max(numbers) {
    // let's set the first number as the potential max
    let maxNumber = numbers[0];
    // we can safely start from 1, why?
    for (let i = 1; i < numbers.length; i++) {
        // if the current number in the loop is greater than the current max...
        if (numbers[i] > maxNumber) {
            // ...set it as the new max!
            maxNumber = numbers[i];
        }
    }
    // don't forget to return your result!
    return maxNumber;
}

// test cases
console.log(max([1, 2, 3])); // logs 3
console.log(max([3, 1, 2])); // logs 3
console.log(max([-3, 1, 2])); // logs 2
console.log(max([])); // logs undefined

You can use Array.prototype.forEach if you prefer:

function max(numbers) {
    let maxNumber = numbers[0];
    // observe how this way you cannot skip the first iteration
    numbers.forEach((n) => {
        if (n > maxNumber) {
            maxNumber = n;
        }
    });
    return maxNumber;
}

Min of variadic arguments

There are two little plot twists here - we have to find the min and not the max, and the function signature differs: instead of a single argument of type array, the function accepts a variable number of arguments:

// we use the Rest parameters syntax
// SEE: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
function min(...numbers) {
    let minNumber = numbers[0];
    for (let i = 1; i < numbers.length; i++) {
        if (numbers[i] < minNumber) {
            minNumber = numbers[i];
        }
    }
    return minNumber;
}

// test cases
console.log(min(1, 2, 3)); // logs 1
console.log(min(3, 1, ])); // logs 1
console.log(min(-3, 1, 2)); // logs -3
console.log(min()); // logs undefined

Generate the email for a user

Here the function signature gets a bit more interesting, because we expect an object and a string, and inside the function we'll have to access two properties of the object (firstName and lastName).

This problem can be split in two sub-problems:

  • formatting the part before the @, i.e. the username;
  • formatting the part after the @, i.e. the domain.

Let's start with the first

function getEmail(user, website) {
    // let's destructure the props we need so we have to type less annoying dots
    const { firstName, lastName } = user;
    const userName = `${firstName}.${lastName}`;
    // ...
}

Let's stop here for a second and let's check if this is working. You can do so by calling the function and logging what the function returns so far:

function getEmail(user, website) {
    const { firstName, lastName } = user;
    const userName = `${firstName}.${lastName}`;
    return userName;
}

console.log(
    getEmail({ firstName: 'Jane', lastName: 'Doe' })
); // logs 'Jane.Doe'

Great, we are getting somewhere! The first and last name are joined with a dot. But they are still in upper case - let's fix this:

function getEmail(user, website) {
    const { firstName, lastName } = user;
    const userName = `${firstName.toLowerCase()}.${lastName.toLowerCase()}`;
    return userName;
}

console.log(
    getEmail({ firstName: 'Jane', lastName: 'Doe' })
); // logs 'jane.doe'

Fixed! Now we can move forward to the domain part, or cover the case when the last name is not provided. Let's finish the username part first:

function getEmail(user, website) {
    const { firstName, lastName } = user;

    // let's declare the variable with let, so we can decide what to assign in the following if
    let userName;

    if (lastName) {
        userName = `${firstName.toLowerCase()}.${lastName.toLowerCase()}`;
    } else {
        userName = firstName.toLowerCase();
    }

    return userName;
}

console.log(
    getEmail({ firstName: 'Jane' })
); // logs 'jane'

console.log(
    getEmail({ firstName: 'Jane', lastName: 'Doe' })
); // logs 'jane.doe'

Great! Observe how we have to check TWO scenarios now, because we introduced an if in our code and we have to make sure both branches work as intended. This is a very important concept in unit testing - here we are checking things in an intuitive way and it's fine!

Now there are 2904809248 ways one could organise this if decision...but it's not important! As long as the WHOLE function satisfies its contract with the caller, don't waste time in optimising such small things. It's readable, it does its job, and it's not like you are generating million of emails per second where a nanosecond will matter. An alternative with a ternary operator because you know you want it:

// I can live with it
const userName = lastName
    ? `${firstName.toLowerCase()}.${lastName.toLowerCase()}`
    : firstName.toLowerCase();

Now for the domain part: the challenge is to remove the www. part from the passed website. For simplicity's sake we will assume that all the test cases will start with www, i.e. no http, no strings that are different than www.{something}.{somethingElse}.

function getEmail(user, website) {
    const { firstName, lastName } = user;
    let userName;
    if (lastName) {
        userName = `${firstName.toLowerCase()}.${lastName.toLowerCase()}`;
    } else {
        userName = firstName.toLowerCase();
    }

    const domain = website.replace('www.', '');
    return `${userName}@${domain}`;
}

console.log(
    getEmail({ firstName: 'Jane' }, 'www.flamingos.com')
); // logs 'jane@flamingos.com'

console.log(
    getEmail({ firstName: 'Jane', lastName: 'Doe' }, 'www.flamingos.com')
); // logs 'jane.doe@flamingos.com'

Now this function is not huge, but it's still clearly doing to different things inside it. It would be great to move these two different things in two different functions:

function getUsername({ firstName, lastName }) {
    let userName;
    if (lastName) {
        userName = `${firstName.toLowerCase()}.${lastName.toLowerCase()}`;
    } else {
        userName = firstName.toLowerCase();
    } 
    return userName;
}

function getDomain(website) {
   return website.replace('www.', ''); 
}

function getEmail(user, website) {
    const userName = getUsername(user);
    const domain = getDomain(website);
    return `${userName}@${domain}`;
}

Observe how after this process the behaviour of the function stays the same, but the code is more organised. This process is called refactoring, and usually you don't have time to do that because your project manager wants you to add more features instead.

For instance, if you want to remove the http:// part from the website now, you can do so by working in the getDomain function only:

function getDomain(website) {
    // important: there are better js functions that parse URLs
    // and give you the protocol, the domain, the query string, etc...
    // this is just an example!
    return website
        .replace('http://', '')
        .replace('www.', ''); 
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment