Skip to content

Instantly share code, notes, and snippets.

@tokdaniel
Last active March 6, 2022 20:41
Show Gist options
  • Save tokdaniel/bc23f7141868da0518832169102b0b32 to your computer and use it in GitHub Desktop.
Save tokdaniel/bc23f7141868da0518832169102b0b32 to your computer and use it in GitHub Desktop.
// These are some nice-to-have utilies which js doesn't provide by default
// creates an array of numbers as [m..n]
const range = (m: number, n: number): number[] => Array.from(Array(n - m + 1).keys()).map(n => n + m)
// combines functions from right to left (opposite of pipe)
const compose = <R>(fn1: (a: R) => R, ...fns: Array<(a: R) => R>) =>
fns.reduce((prevFn, nextFn) => value => prevFn(nextFn(value)), fn1);
// if `value` is anything other than a number this is an identity, and modulus otherwise
type SafeMod = (value: number | any, divider: number) => number | any
const safeMod: SafeMod = (value, divider) => value.constructor === Number ? value % divider : value
// ------------------------------ The actual implementation starts below ------------------------------
type IsDivisibleBy = (divider: number) => (replacer: string) => (value: number | string) => number | string
const isDivisibleBy: IsDivisibleBy = divider => replacer => value => safeMod(value, divider) === 0 ? replacer : value
// these expressions return functions waiting for the actual value
// since we provide the first 2 parameters (by currying)
// `value => safeMod(value, 3) === 0 ? "Fizz" : value`
const fizz = isDivisibleBy(/*divider*/3)(/*replacer*/ "Fizz")
// `value => safeMod(value, 5) === 0 ? "Buzz" : value`
const buzz = isDivisibleBy(5)("Buzz");
// `value => safeMod(value, 15) === 0 ? "FizzBuzz" : value`
const fizzBuzz = isDivisibleBy(15)("FizzBuzz")
const magic = compose(fizz, buzz, fizzBuzz)
range(1, 100).map(magic)
// ----------------------------- ES6 COMPILED VERSION -----------------------------
const range = (m, n) => Array.from(Array(n - m + 1).keys()).map(n => n + m);
const compose = (fn1, ...fns) => fns.reduce((prevFn, nextFn) => value => prevFn(nextFn(value)), fn1);
const safeMod = (value, divider) => value.constructor === Number ? value % divider : value;
const isDivisibleBy = divider => replacer => value => safeMod(value, divider) === 0 ? replacer : value;
const fizz = isDivisibleBy(3)("Fizz");
const buzz = isDivisibleBy(5)("Buzz");
const fizzBuzz = isDivisibleBy(15)("FizzBuzz");
const magic = compose(fizz, buzz, fizzBuzz);
range(1, 100).map(magic);
// ----------------------------- HACKY BUT SHORT VERSION -----------------------------
// In this case the implementation of `isDivisibleBy` would introduce occurences of for instance
// "Fizz" % 5 === 0 which evaluates to `false` so we return "Fizz"
// "string" % number evaluates to `NaN`, `NaN` === anything is `false`
// This works because javascript, but it's an awful thing to exploit
// safeMod exists to solve this issue
const range = (m, n) => Array.from(Array(n - m + 1).keys()).map(n => n + m);
const isDivisibleBy = divider => replacer => value => value % divider === 0 ? replacer : value;
const fizz = isDivisibleBy(3)("Fizz");
const buzz = isDivisibleBy(5)("Buzz");
const fizzBuzz = isDivisibleBy(15)("FizzBuzz");
range(1, 100).map((value) => fizz(buzz(fizzBuzz(value))));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment