Last active
March 6, 2022 20:41
-
-
Save tokdaniel/bc23f7141868da0518832169102b0b32 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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