Weekend JavaScript Newsletter

JavaScript Interview Exercises With Solutions 2019

> 5 months ago

I find it extremely beneficial to ask a job seeker to solve some of the following exercises. It helps to understand how good is a person you're dealing with in JavaScript and also shows how he or she thinks about the problems in general.

In this article, I'd like to share some of those exercises.

Just putting them here in no particular order. Feel free to use any of them in your next interview.

What is reduce? Please implement the map and filter functions with reduce.

SOLUTION: Array.proptotype.reduce is a function on an array, which allows to iterate through the array and collect a result to return (which can be modified on each iteration). It takes a reducer function and an initial value for the accumulator.

const arr = [1, 2, 3]
arr.reduce((acc, el) => {
  return el + acc
}, 0) // => 15

Here's how we can implement map:

function map(arr, mapper) {
  return arr.reduce((acc, el) => {
    return [...acc, mapper(el)]
  }, [])
}
map([1, 2, 3], x => x * x) // => [1, 4, 9]

And here's filter:

function filter(arr, f) {
  return arr.reduce((acc, el) => (f(el) ? [...acc, el] : acc), [])
}
filter([-1, 0, 1, 2], x => x > 0) // => [1, 2]

Write the uniq function that removes repeated elements from the array.

SOLUTION:

function uniq(arr) {
  return arr.reduce(
    (acc, el) => (acc.indexOf(el) > -1 ? acc : [...acc, el]),
    [],
  )
}
uniq([1, 2, 3, 1, 2]) // => [1, 2, 3]

An even shorter solution is possible if we utilize Set which can\t have non-unique members:

const uniq = arr => [...new Set(arr)]

Flatten is a function that puts elements from the inner arrays into the top array.

Can you implement deepFlatten so that

deepFlatten([1, [2], [[3], [4, [5]]]]) // => [1, 2, 3, 4, 5]

SOLUTION:

function deepFlatten(arr) {
  return arr.reduce(
    (acc, el) =>
      Array.isArray(el) ? [...acc, ...deepFlatten(el)] : [...acc, el],
    [],
  )
}

How many users per hobby?

Assuming we have an array of users

const users = [
  { name: 'John', hobbies: ['singing', 'walking', 'playing guitar'] },
  { name: 'Terry', hobbies: ['swimming', 'playing guitar'] },
  { name: 'Anna', hobbies: ['walking', 'swimming', 'playing guitar'] },
  { name: 'Paul', hobbies: ['swimming', 'singing'] },
]

For each hobby, count the number of users occupied with it.

SOLUTION:

const hobbies = uniq(flatten(users.map(u => u.hobbies)))
const hasHobby = (h) => (u) => u.hobbies.indexOf(h) > -1
const counts hobbies.map( h => ({
  hobby: h,
  count: users.filter(hasHobby(h)).length,
})) // => [ {hobby: 'singing', count: 2}, ... ]

SOLUTION 2 (Imperative):

const counts = {}

users.forEach(u => {
  u.hobbies.forEach(h => {
    counts[h] = (counts[h] || 0) + 1
  })
})

Write a sample(arr, n) function that takes n random elements from an array.

SOLUTION:

function sample(arr, n) {
  const length = arr.length
  const indices = []
  while(indices.length < n){
    const r = Math.floor(Math.random() * length);
    if (indices.indexOf(r) === -1) indices.push(r);
  }
  return indices.map(i => arr[i])
}

sample([1, 2, 3, 4, 5, 6, 7, 8, 9], 3) // => [7, 9, 6]

Write a memoize function

It wraps a given function and only runs it once for a list of parameters, saves the result in inner cache and return it the next time.

SOLUTION (Basic):

function memoize(fn) {
  const cache = {}
  return function(...args) {
    const key = args.join('-')
    if (!cache[key]) {
      cache[key] = fn(...args)
    }
    return cache[key]
  }
}

This solution is very simple. But it doesn’t work for some cases. We can make it more complex by asking: what if a function returns a boolean, what if a function is type-unsafe ( 1 vs “1” ), …

Can you implement a function that will get a value from an object by logical path "a.b.c"?

...no matter how deep, and will return null if no value is there without throwing an exception.

Example:

safeGet({ a: { b: 5 } }, 'a.b') // => 5

SOLUTION:

function safeGet(obj, path) {
  const keys = path.split('.')
  return keys.reduce((acc, key) => {
    return (acc || {})[key] ? acc[key] : null
  }, obj)
}

safeGet({ a: { b: 5 } }, 'a.b') //=> 5

safeGet({ a: { b: 5 } }, 'a.b.c.d') //=> null

Currying is when you can call a function with only some arguments

...and it returns a new function which you can use with the rest of the arguments. Write the curry function.

Example:

sum(1, 2) // => 3
const addOne = sum(1)
addOne(2) // => 3

SOLUTION:

function curry(fn, arity) {
  arity = arity || fn.length
  return function(...args) {
    if (args.length < arity) {
      function f(...lefts) {
        return fn(...args, ...lefts)
      }
      return curry(f, arity - args.length)
    } else {
      return fn(...args)
    }
  }
}

const sum = curry((a, b, c) => a + b + c)

sum(1, 2, 3) //=> 6
sum(1)(2, 3) //=> 6
sum(1, 2)(3) //=> 6
sum(1)(2)(3) //=> 6

Weekend JavaScript

Get all the best JavaScript articles in your inbox every Friday. Plus occassional articles like this one. It's Free!
1008 have already joined.