JavaScript Interview Exercises With Solutions 2019
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.
reduce
? Please implement the map
and filter
functions with reduce
.
What is 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]
uniq
function that removes repeated elements from the array.
Write the 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]
memoize
function
Write a 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