JavaScript coding question
Medium

1. implement curry() with placeholder support

please implement curry() which also supports placeholder.

Here is an example

const  join = (a, b, c) => {
   return `${a}_${b}_${c}`
}
 
const curriedJoin = curry(join)
const _ = curry.placeholder
 
curriedJoin(1, 2, 3) // '1_2_3'
 
curriedJoin(_, 2)(1, 3) // '1_2_3'
 
curriedJoin(_, _, _)(1)(_, 3)(2) // '1_2_3'

solutions:

curry visualized explanation
  1. Let’s first determine the base case complete
  2. if arg is equal tocurry.placeholder, wo only need to args2.shift(), and don't care about args2.shift() value, because of recursion
  3. if arg isn't equal to curry.placeholder, return arg

You can see my github file js/medium/curry.js (opens in a new tab)

function curry(fn) {
  return function curryInner(...args) {
    const complete = args.length >= fn.length && !args.includes(curry.placeholder);
    if(complete) return fn(...args);
    return (...args2) => {
      const res = args.map(arg => arg === curry.placeholder && args2.length ? args2.shift() : arg);
      return curryInner(...res, ...args2)
    };
  }
}

2. implement basic throttle()

Throttling is a common technique used in Web Application, in most cases using lodash solution would be a good choice.

could you implement your own version of basic throttle()?

In case you forgot, throttle(func, delay) will return a throttled function, which will invoke the func at a max frequency no matter how throttled one is called.

Something like below will be used to do the test.

let currentTime = 0
 
const run = (input) => {
  currentTime = 0
  const calls = []
 
  const func = (arg) => {
     calls.push(`${arg}@${currentTime}`)
  }
 
  const throttled = throttle(func, 3)
  input.forEach((call) => {
     const [arg, time] = call.split('@')
     setTimeout(() => throttled(arg), time)
  })
  return calls
}
 
expect(run(['A@0', 'B@2', 'C@3'])).toEqual(['A@0', 'C@3'])

solutions:

  1. If timeout is null, prove that no func is waiting to be executed, so execute func func.apply(this, args)
  2. Assign a setTimeout to timeout, if lastArgs is not null, execute func func.apply(this, lastArgs), and reset timeout and lastArgs.
function throttle(func, wait) {
  let timeout = null;
  let lastArgs = null;
  return (...args) => {
    if (!timeout) {
      func.apply(this, args);
      timeout = setTimeout(() => {
        lastArgs && func.apply(this, lastArgs)
        timeout = null;
        lastArgs = null;
      }, wait);
    } else {
      lastArgs = args;
    }
  }
}