Duplicate Function Coding Interview Question | Skilled.dev
Interview Question

Duplicate Function

Course launching August 2020

Follow me on YouTube for free coding interview videos.

Users who sign up for the email list will receive an exclusive 75% discount at launch.

Your subscription could not be saved. Please try again.
Your subscription was successful! 🤓
This lesson requires a subscription >> Upgrade

Write a function duplicate(numDuplicates)(array) where it will duplicate an array's content a certain number of times inside a the returned array.

duplicate(2)(['h', 'e', 'l', 'l', 'o']);
// Output: ['h', 'e', 'l', 'l', 'o', 'h', 'e', 'l', 'l', 'o'];

If you have seen syntax like this, it may be confusing at first. We have a function that looks like it is executing twice duplicate()().

This is something known as function currying. It's not actually executing duplicate twice, but it means that duplicate must return another function. Then the section execution is actually executing this returned function. This is in contrast to using a single function that takes two parameters duplicate(numDuplicates, array).

With this, we know our function needs to look something like the following:

function duplicate(numDuplicates) {
  // Returning a function from the function
  return function(array) {
    // ... duplicate `array` by `numDuplicates` copies
  }
}

Now we need the logic to duplicate an array. If we knew before hand the number of times it would duplicate (say two), we could simply do something like:

const duplicate = (array) => [...array, ...array];

Instead, we need to handle a general case where we duplicate the array an arbitrary number of times.

The first step we need to perform is create a new Array(numDuplicates). Then there are possible paths: 1. We can use a reduce to spread the array in for each spot, or 2. Create nested arrays and flatten the solution afterward.

// Solution 1
function duplicate(numDuplicates) {
  return function(array) {
    return new Array(numDuplicates).fill(array).flat();
  }
}

// Solution 2
function duplicate(numDuplicates) {
  return function(array) {
    return new Array(numDuplicates)
      .fill(null)
      .reduce((acc) => [...acc, ...array], []);
  }
}

// Solution 3
function duplicate(numDuplicates) {
  return function(array) {
    const solution = [];

    for (let i = 0; i < numDuplicates; i++) {
      solution.push(...array);
    }

    return solution;
  }
}

Now that we have our logic and understand the higher-order function, we can rewrite this very succinctly using arrow functions. Since arrow functions implicitely return, the outer function is return the inner automatically.

const duplicate = (numDuplicates) => (array) =>
  new Array(numDuplicates).fill(array).flat();

Currying is a functional programming technique where instead of taking multiple arguments, you take a succession of single arguments. It is a higher order function because it returns a function. In more complex cases, it allows to create different versions of a function that share core functionality without duplication but can then be applied in different instances. Using the function we created, we could generate functions that duplicate a a specified number of times.

const duplicateTwice = duplicate(2);
duplicateTwice(['h', 'e', 'l', 'l', 'o']);
// ['h', 'e', 'l', 'l', 'o', 'h', 'e', 'l', 'l', 'o']

const tenDupes = duplicate(10);
tenDupes([0]);
// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

How is numDuplicates remembered after the first invocation? Our function has formed a closure over it, so it is persisted in the function that is returned. This was actually happening previously, but it likely wasn't apparent since we were executing the returned function immediately

TLDR learning outcomes: closure, higher order functions, currying, spread syntax, array methods

Prev
Fix `this`
Next
Array Methods
This lesson requires a subscription >> Upgrade

Table of Contents