Functional techniques built from closures and higher-order functions — and staple interview implementations.
Currying
function curry(fn) {
return function curried(...args) {
return args.length >= fn.length
? fn.apply(this, args)
: (...more) => curried.apply(this, [...args, ...more]);
};
}
const volume = (l, w, h) => l * w * h;
const cv = curry(volume);
console.log(cv(2)(3)(4), cv(2, 3)(4), cv(2)(3, 4), cv(2, 3, 4));
const log = curry((level, scope, msg) => `[${level}] ${scope}: ${msg}`);
const warnDb = log("WARN")("db");
console.log(warnDb("slow query"));24 24 24 24
[WARN] db: slow queryInfinite currying: sum(1)(2)(3)()
const sum = (a) => (b) => (b === undefined ? a : sum(a + b));
console.log(sum(1)(2)(3)(4)());10Composition and pipe
const compose = (...fns) => (x) => fns.reduceRight((acc, f) => f(acc), x);
const pipe = (...fns) => (x) => fns.reduce((acc, f) => f(acc), x);
const trim = (s) => s.trim();
const lower = (s) => s.toLowerCase();
const slugify = (s) => s.replace(/\s+/g, "-");
console.log(pipe(trim, lower, slugify)(" Hello Big World "));
console.log(compose(slugify, lower, trim)(" Hello Big World "));hello-big-world
hello-big-worldMemoization
function memoize(fn, keyFn = (...args) => JSON.stringify(args)) {
const cache = new Map();
return function (...args) {
const key = keyFn(...args);
if (cache.has(key)) return cache.get(key);
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
let calls = 0;
const slowSquare = (n) => { calls++; return n * n; };
const fastSquare = memoize(slowSquare);
console.log(fastSquare(9), fastSquare(9), fastSquare(3), "calls:", calls);
const fib = memoize((n) => (n < 2 ? n : fib(n - 1) + fib(n - 2)));
console.log(fib(90));81 81 9 calls: 2
2880067194370816000once, partial and friends
const partial = (fn, ...preset) => (...later) => fn(...preset, ...later);
const greet = (greeting, name) => `${greeting}, ${name}!`;
console.log(partial(greet, "Welcome")("Mei"));
const not = (pred) => (...args) => !pred(...args);
const isEven = (n) => n % 2 === 0;
console.log([1, 2, 3, 4].filter(not(isEven)));
const tap = (fn) => (x) => { fn(x); return x; };
console.log([1, 2].map(tap((x) => console.log("saw", x))));Welcome, Mei!
[ 1, 3 ]
saw 1
saw 2
[ 1, 2 ]Implement a curry function.
What is memoization?