Callbacks - Code Exercises

Written by David Abram

It's really hard to test your programming knowledge after you have completed a tutorial or some lecture. We have prepared some exercises to help out the beginner devs to solidify their understanding of callbacks. Every exercise has a brief description of the problem, starting code, links to relevant MDN docs, and expected results. Try to solve the problems without taking a peek at the solution.

If you need some additional help, you can check out our Functions video from #lockdown learning series in which we discuss functions and callbacks or contact the author of the article directly.




Callbacks are a critical concept in JavaScript and you won't get very far without knowing how to use them. They are often as confusing to experienced developers as to beginners. A callback is nothing more than a function that is passed into another function as an argument to be executed later.

They are so common in JavaScript, you certainly used callbacks without knowing they're called that. You probably wrote a similar snippet multiple times:

const button = document.querySelector('button');

button.addEventListener('click', (e) => {
  this.classList.add('clicked')
});

The second parameter passed into addEventListener is a callback. I have compiled some exercises to improve your understanding of callbacks. Try not to skip them as some code is reused in later exercises.



Exercises



Is this number odd?

Write a function that returns true or false for a given number. We will reuse this function, so make it reusable.

Helpful MDN Docs links

Function calls

  isOdd(4);
  isOdd(5);

Result

  false
  true

Solution

(click to show)
const isOdd = (num) => num % 2 === 1;

Exclaim

Write a function that returns the given string with a concated exclamation ! at the end. We will reuse this function, so make it reusable.

Helpful MDN Docs links

Function calls

  exclaim('Adrian');
  exclaim(exclaim('Adrian'));

Result

  "Adrian!"
  "Adrian!!"

Solution

(click to show)
  const exclaim = (str) => `${str}!`;

  exclaim('Adrian');
  exclaim(exclaim('Adrian'));

Double the chars!

Write a function that duplicates each char in a string. If I pass 'abc' to the function, it should return 'aabbcc'. We will reuse this function, so make it reusable.

Helpful MDN Docs links

Function calls

  doubleChars('Adrian');
  doubleChars('ssssnake');

Result

  "AAddrriiaann"
  "ssssssssnnaakkee"

Solution

(click to show)
  const doubleChars = (str) => str.split('').map(c => `${c}${c}`).join('');

  doubleChars('Adrian');
  doubleChars('ssssnake');

At least two elements

Write a function called atLeastTwo that receives an array and a callback as its arguments. If at least two elements from the array return a truthy value when passed as an argument to the callback, atLeastTwo should return true. If there aren't at least two elements from the array that return a truthy value when passed as arguments to the callback, atLeastTwo should return false.

Helpful MDN Docs links

Function calls

  atLeastTwo([1, 2, 3, 4, 5], isOdd);
  atLeastTwo([2, 4, 6], isOdd);
  atLeastTwo([1, 2, 3, 4, 5], t => t > 3)

Result

  true
  false
  true

Solution

(click to show)
    const isOdd = (num) => {
      return num % 2 === 1;
    };

    const atLeastTwo = (array, callback) => array
    .filter(callback).length >= 2;
    
    atLeastTwo([1, 2, 3, 4, 5], isOdd);

    atLeastTwo([2, 4, 6], isOdd);

    atLeastTwo([1, 2, 3, 4, 5], t => t > 3);

Group By

Write a function groupBy that groups elements from an array by the returned value from callback when an element from the array is passed as an argument.

This is a really common pattern when doing array manipulation.

Helpful MDN Docs links

Function calls

  const input = [
    {
      name: 'John',
      yearOfBirth: 1988,
      placeOfBirth: 'New York',
    },
    {
      name: 'Nancy',
      yearOfBirth: 1963,
      placeOfBirth: 'New York',
    },
    {
      name: 'John',
      yearOfBirth: 1980,
      placeOfBirth: 'Toronto',
    },
  ];

  // 1
  groupBy(input, t => t.name);

  // 2
  groupBy(input, t => isOdd(t.yearOfBirth));

Result

  // 1
  {
    John: [
      {
        name: 'John',
        yearOfBirth: 1988,
        placeOfBirth: 'New York',
      },
      {
        name: 'John',
        yearOfBirth: 1980,
        placeOfBirth: 'Toronto',
      }
    ],
    Nancy: [
      {
        name: 'Nancy',
        yearOfBirth: 1963,
        placeOfBirth: 'New York',
      }
    ]
  }

  // 2
  {
    false: [
      {
        name: 'John',
        yearOfBirth: 1988,
        placeOfBirth: 'New York',
      },
      {
        name: 'John',
        yearOfBirth: 1980,
        placeOfBirth: 'Toronto',
      }
    ],
    true: [
      {
        name: 'Nancy',
        yearOfBirth: 1963,
        placeOfBirth: 'New York',
      }
    ]
  }

Solution

(click to show)
     const input = [
      {
        name: 'John',
        yearOfBirth: 1988,
        placeOfBirth: 'New York',
      },
      {
        name: 'Nancy',
        yearOfBirth: 1963,
        placeOfBirth: 'New York',
      },
      {
        name: 'John',
        yearOfBirth: 1980,
        placeOfBirth: 'Toronto',
      },
    ];

    const isOdd = (num) => {
      return num % 2 === 1;
    };

    const groupBy = (array, callback) => array.reduce(
      (accumulator, currentValue) => {
        const key = callback(currentValue);
        
        if(accumulator[key]) {
          accumulator[key].push(currentValue);
        } else {
          accumulator[key] = [currentValue];
        }

        return accumulator;
      },
      {},
    );

    groupBy(input, t => t.name);

    groupBy(input, t => isOdd(t.yearOfBirth));

Repeat the function x times

Write a function repeat that receives 3 arguments: a string, number of repetitions, and a callback that will be repeated. The repeat function should pass the string to the callback as an argument and repeat x times the callback with the result of the previous repetition as an argument.

Helpful MDN Docs links

Function calls

  repeat('infinite power', 3, exclaim);
  repeat('triple', 2, doubleChars);
  repeat('trogdor', 3, t => `°${t}°`);

Result

  "infinite power!!!"
  "tttrrriiipppllleee"
  "°°°trogdor°°°"

Solution

(click to show)
  const repeatRecursive = (input, num, callback) => {
    if(num === 0) return input;
    return repeatRecursive(callback(input), num-1, callback);
  }

  const repeat = (input, num, callback) => {
    let result = input;
    for (i = 0; i < num; i++) {
      result = callback(result);
    }
    return result;
  }

  repeat('infinite power', 3, exclaim);
  repeat('triple', 2, doubleChars);
  repeat('trogdor', 3, t => `°${t}°`);
David Abram
Spends his time untangling software architectures and doing DevOps. Likes to build stuff. Connect with him on Twitter and LinkedIn