From 50f302a66c896d9393c7d9544c7eaa2e6a7d3179 Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Thu, 14 Dec 2017 01:17:10 +0200 Subject: [PATCH] Styleguide --- .gitignore | 2 + CONTRIBUTING.md | 79 ++++++++++-- currentSnippet.js | 3 - semi-snippets.js | 242 ----------------------------------- snippet-template.md | 4 +- snippets.js | 302 -------------------------------------------- 6 files changed, 74 insertions(+), 558 deletions(-) delete mode 100644 currentSnippet.js delete mode 100644 semi-snippets.js delete mode 100644 snippets.js diff --git a/.gitignore b/.gitignore index c2658d7d1..bc23f6a88 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ node_modules/ + +currentSnippet\.js diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7e90497d5..09e28829b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,74 @@ ## Contributing -You can contribute to **30 seconds of code** by sending pull requests for snippets that you find useful, reporting issues with current snippets or suggesting changes and/or additions. +**30 seconds of code** is a community effort, so feel free to contribute in any way you can. Every contribution helps! -### Guidelines for new snippets +Here's what you can do to help: -- Snippets must be short. Usually anything above 10 lines would be considered too long, but you can still submit it as it might be possible to shorten it or it might still prove useful regardless of its length. -- Snippets must be explained to a certain extent in the description above them. Make sure to include what functions you are using and why. -- Snippets must solve real-world problems and should be abstract enough to use in different scenarios. This is highly subjective, so send them in anyways. -- Snippets *should* be written in ES6 if possible. -- Snippet files must follow the anchor name conventions of [GitHub Flavored Markdown](https://github.github.com/gfm/), so that the `builder.js` can build the links for the list. -- Use the [template](snippet-template.md) to format your snippets. -- If possible, provide test cases in your Pull Request (link or comment), so that it's easier to verify that each snippet is working. +- [Open issues](https://github.com/Chalarangelo/30-seconds-of-code/issues/new) for things you want to see added or modified. +- Be part of the discussion by helping out with [existing issues](https://github.com/Chalarangelo/30-seconds-of-code/issues) or talking on our [gitter channel](https://gitter.im/30-seconds-of-code/Lobby). +- Submit [pull requests](https://github.com/Chalarangelo/30-seconds-of-code/pulls) with snippets you have created (see below for guidelines). +- Fix typos in existing snippets or run `npm run lint "snippet-name.md"` on unlinted snippets (yes, this is something we actually want help with). + +### Snippet submission and Pull request guidelines + +- **DO NOT MODIFY THE README.md FILE!** Make changes to individual snippet files. You can optionally run `npm run build-list` to update the README.md file automatically, based on the changes you have made. +- **Snippet filenames** must correspond to the title of the snippet. For example if your snippet is titled `### Awesome snippet` the filename should be `awesome-snippet.md`. + - Use `kebab-case`, not `snake_case`. + - Avoid capitalization of words, except if the whole word is capitalized (e.g `URL` should be capitalized in the filename and the snippet title). + - If there are parentheses in the title, add them to the filename (e.g. `awesome-snippet-(extra-awesome).md` if your snippet's title is `Awesome snippet (extra awesome)`). +- **Snippet titles** should have only the first letter of the first word capitalized. Certain words can be in capitals (e.g. `URL`, `RGB`), but this is on a per-snippet basis. + - All snippet titles must be prefixed with `###` and be at the very first line of your snippet. + - Snippet titles must be unique (although if you cannot find a better title, just add some placeholder at the end of the filename and title and we will figure it out). + - Follow snippet titles with an empty line. +- **Snippet descriptions** must be short and to the point. Try to explain *how* the snippet works and what Javascript features are used. Remember to include what functions you are using and why. + - Follow snippet descriptions with an empty line. +- **Snippet code** must be enclosed inside ` ```js ` and ` ``` `. + - Remember to start your snippet's code on a new line below the opening backticks. + - Use ES6 notation to define your function. For example `const myFunction = arg1, arg2 => { }`. + - Try to keep your snippets' code short and to the point. Use modern techniques and features. Make sure to test your code before submitting. + - All snippets must be followed by one (more if necessary) test case after the code, on a new line, in the form of a comment, along with the expected output. The syntax for this is `myFunction('testInput') -> 'testOutput'`. Use multiline comments only if necessary. + - Try to make your function name unique, so that it does not conflict with existing snippets. +- Snippets should be short (usually below 10 lines). If your snippet is longer than that, you can still submit it and we can help you shorten it or figure out ways to improve it. +- Snippets *should* solve real-world problems, no matter how simple. +- Snippets *should* be abstract enough to be applied to different scenarios. +- It is not mandatory, but highly appreciated if you provide **test cases** and/or performance tests (we recommend using [jsPerf](https://jsperf.com/)). +- You can start creating a new snippet, by using the [snippet template](snippet-template.md) to format your snippets. + +### Additional guidelines and conventions regarding snippets + +- When describing snippets, refer to methods, using their full name. For example, use `Array.reduce()`, instead of `reduce()`. +- If your snippet contains argument with default parameters, explain what happens if they are ommited when calling the function and what the default case is. +- If your snippet uses recursion, explain the base cases. +- Always use `const functionName` for function definitions. +- Use variables only when necessary. Prefer `const` when the values are not altered after assignment, otherwise use `let`. Avoid using `var`. +- Use `camelCase` for function and variable names if they consist of more than one word. +- Try to give meaningful names to variables. For example use `letter`, instead of `lt`. Some exceptions by convention are: + - `arr` for arrays (usually as the snippet function's argument). + - `str` for strings. + - `val` or `v` for value (usually when iterating a list, mapping, sorting etc.). + - `acc` for accumulators in `Array.reduce()`. + - `(a,b)` for the two values compared when using `Array.sort()`. + - `i` for indexes. + - `func` for function arguments. + - `nums` for arrays of numbers. +- Use `_` if your function takes no arguments or if an argument inside some function (e.g. `Array.reduce()`) is not used anywhere in your code. +- Specify default parameters for arguments, if necessary. It is preferred to put default parameters last, unless you have pretty good reason not to. +- If your snippet's function takes variadic arguments, use `..args` (although in certain cases, it might be needed to use a different name). +- If your snippet function's body is a single statement, omit the `return` keyword and use an expression instead. +- Always use soft tabs (2 spaces), never hard tabs. +- Omit curly braces (`{` and `}`) whenever possible. +- Always use single quotes for string literals. Use template literals, instead, if necessary. +- If your snippet's code is short enough (around 80 characters), you can make it a single-line function (although not mandatory). Otherwise, use multiple lines. +- Prefer using `Array` methods whenever possible. +- Prefer `Array.concat()` instead of `Array.push()` when working with `Array.reduce()`. +- Use strict equality checking (`===` and `!==` instead of `==` and `!=`), unless you specificly have reason not to. +- Prefer using the ternary operator (`condition ? trueResult : falseResult`) instead of `if else` statements whenever possible. +- Avoid nesting ternary operators (but you can do it if you feel like you should). +- You should define multiple variables on the same line (e.g. `const x = 0, y = 0`) on the same line whenever possible. +- Do not use trailing or leading underscores in variable names. +- Use dot notation (`object.property`) for object properties, when possible. Use bracket notation (`object[variable]`) when accessing object properties using a variable. +- Use arrow functions as much as possible, except when you can't. +- Use semicolons whenever necessary. If your snippet function's body is a single statement, return an expression and add a semicolon at the end. +- Leave a single space after a comma (`,`) character. +- Try to strike a balance between readability, brevity and performance. +- Never use `eval()`. Your snippet will be disqualified immediately. diff --git a/currentSnippet.js b/currentSnippet.js deleted file mode 100644 index 544a140b9..000000000 --- a/currentSnippet.js +++ /dev/null @@ -1,3 +0,0 @@ - -const valueOrDefault = (value, d) => value || d; -// valueOrDefault(NaN, 30) -> 30 diff --git a/semi-snippets.js b/semi-snippets.js deleted file mode 100644 index c35e9d6b8..000000000 --- a/semi-snippets.js +++ /dev/null @@ -1,242 +0,0 @@ - -const anagrams = str => { - if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str]; - return str.split('').reduce((acc, letter, i) => - acc.concat(anagrams(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val)), []); -}; -// anagrams('abc') -> ['abc','acb','bac','bca','cab','cba'] - -const average = arr => - arr.reduce((acc, val) => acc + val, 0) / arr.length; -// average([1,2,3]) -> 2 - -const bottomVisible = _ => - document.documentElement.clientHeight + window.scrollY >= document.documentElement.scrollHeight || document.documentElement.clientHeight; -// bottomVisible() -> true - -const capitalizeEveryWord = str => str.replace(/\b[a-z]/g, char => char.toUpperCase()); -// capitalizeEveryWord('hello world!') -> 'Hello World!' - -const capitalize = (str, lowerRest = false) => - str.slice(0, 1).toUpperCase() + (lowerRest ? str.slice(1).toLowerCase() : str.slice(1)); -// capitalize('myName', true) -> 'Myname' - -const chainAsync = fns => { let curr = 0; const next = () => fns[curr++](next); next(); }; -/* -chainAsync([ - next => { console.log('0 seconds'); setTimeout(next, 1000); }, - next => { console.log('1 second'); setTimeout(next, 1000); }, - next => { console.log('2 seconds'); } -]) -*/ - -const palindrome = str => - str.toLowerCase().replace(/[\W_]/g, '').split('').reverse().join('') === str.toLowerCase().replace(/[\W_]/g, ''); -// palindrome('taco cat') -> true - -const chunk = (arr, size) => - Array.apply(null, {length: Math.ceil(arr.length / size)}).map((v, i) => arr.slice(i * size, i * size + size)); -// chunk([1,2,3,4,5], 2) -> [[1,2],[3,4],5] - -const countOccurrences = (arr, value) => arr.reduce((a, v) => v === value ? a + 1 : a + 0, 0); -// countOccurrences([1,1,2,1,2,3], 1) -> 3 - -const currentUrl = _ => window.location.href; -// currentUrl() -> 'https://google.com' - -const curry = (f, arity = f.length, next) => - (next = prevArgs => - nextArg => { - const args = [ ...prevArgs, nextArg ]; - return args.length >= arity ? f(...args) : next(args); - } - )([]); -// curry(Math.pow)(2)(10) -> 1024 -// curry(Math.min, 3)(10)(50)(2) -> 2 - -const deepFlatten = arr => - arr.reduce((a, v) => a.concat(Array.isArray(v) ? deepFlatten(v) : v), []); -// deepFlatten([1,[2],[[3],4],5]) -> [1,2,3,4,5] - -const difference = (arr, values) => arr.filter(v => !values.includes(v)); -// difference([1,2,3], [1,2]) -> [3] - -const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0); -// distance(1,1, 2,3) -> 2.23606797749979 - -const isDivisible = (dividend, divisor) => dividend % divisor === 0; -// isDivisible(6,3) -> true - -const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); -// escapeRegExp('(test)') -> \\(test\\) - -const isEven = num => Math.abs(num) % 2 === 0; -// isEven(3) -> false - -const factorial = n => n <= 1 ? 1 : n * factorial(n - 1); -// factorial(6) -> 720 - -const fibonacci = n => - Array(n).fill(0).reduce((acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i), []); -// fibonacci(5) -> [0,1,1,2,3] - -const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i)); -// filterNonUnique([1,2,2,3,4,4,5]) -> [1,3,5] - -const flatten = arr => arr.reduce((a, v) => a.concat(v), []); -// flatten([1,[2],3,4]) -> [1,2,3,4] - -const arrayMax = arr => Math.max(...arr); -// arrayMax([10, 1, 5]) -> 10 - -const arrayMin = arr => Math.min(...arr); -// arrayMin([10, 1, 5]) -> 1 - -const getType = v => - v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name.toLowerCase(); -// getType(new Set([1,2,3])) -> "set" - -const getScrollPos = (el = window) => - ({x: (el.pageXOffset !== undefined) ? el.pageXOffset : el.scrollLeft, - y: (el.pageYOffset !== undefined) ? el.pageYOffset : el.scrollTop}); -// getScrollPos() -> {x: 0, y: 200} - -const gcd = (x, y) => !y ? x : gcd(y, x % y); -// gcd (8, 36) -> 4 - -const hammingDistance = (num1, num2) => - ((num1 ^ num2).toString(2).match(/1/g) || '').length; -// hammingDistance(2,3) -> 1 - -const head = arr => arr[0]; -// head([1,2,3]) -> 1 - -const initial = arr => arr.slice(0, -1); -// initial([1,2,3]) -> [1,2] - -const initializeArrayRange = (end, start = 0) => - Array.apply(null, Array(end - start)).map((v, i) => i + start); -// initializeArrayRange(5) -> [0,1,2,3,4] - -const initializeArray = (n, value = 0) => Array(n).fill(value); -// initializeArray(5, 2) -> [2,2,2,2,2] - -const last = arr => arr.slice(-1)[0]; -// last([1,2,3]) -> 3 - -const timeTaken = callback => { - const t0 = performance.now(), r = callback(); - console.log(performance.now() - t0); - return r; -}; -// timeTaken(() => Math.pow(2, 10)) -> 1024 (0.010000000009313226 logged in console) - -const median = arr => { - const mid = Math.floor(arr.length / 2), nums = arr.sort((a, b) => a - b); - return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2; -}; -// median([5,6,50,1,-5]) -> 5 -// median([0,10,-2,7]) -> 3.5 - -const objectFromPairs = arr => arr.reduce((a, v) => (a[v[0]] = v[1], a), {}); -// objectFromPairs([['a',1],['b',2]]) -> {a: 1, b: 2} - -const percentile = (arr, val) => - 100 * arr.reduce((acc, v) => acc + (v < val ? 1 : 0) + (v === val ? 0.5 : 0), 0) / arr.length; -// percentile([1,2,3,4,5,6,7,8,9,10], 6) -> 55 - -const pipe = (...funcs) => arg => funcs.reduce((acc, func) => func(acc), arg); -// pipe(btoa, x => x.toUpperCase())("Test") -> "VGVZDA==" - -const powerset = arr => - arr.reduce((a, v) => a.concat(a.map(r => [v].concat(r))), [[]]); -// powerset([1,2]) -> [[], [1], [2], [2,1]] - -const promisify = func => - (...args) => - new Promise((resolve, reject) => - func(...args, (err, result) => - err ? reject(err) : resolve(result)) - ); -// const delay = promisify((d, cb) => setTimeout(cb, d)) -// delay(2000).then(() => console.log('Hi!')) -> Promise resolves after 2s - -const randomIntegerInRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; -// randomIntegerInRange(0, 5) -> 2 - -const randomInRange = (min, max) => Math.random() * (max - min) + min; -// randomInRange(2,10) -> 6.0211363285087005 - -const randomizeOrder = arr => arr.sort((a, b) => Math.random() >= 0.5 ? -1 : 1); -// randomizeOrder([1,2,3]) -> [1,3,2] - -const redirect = (url, asLink = true) => - asLink ? window.location.href = url : window.location.replace(url); -// redirect('https://google.com') - -const reverseString = str => [...str].reverse().join(''); -// reverseString('foobar') -> 'raboof' - -const rgbToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0'); -// rgbToHex(255, 165, 1) -> 'ffa501' - -const series = ps => ps.reduce((p, next) => p.then(next), Promise.resolve()); -// const delay = (d) => new Promise(r => setTimeout(r, d)) -// series([() => delay(1000), () => delay(2000)]) -> executes each promise sequentially, taking a total of 3 seconds to complete - -const scrollToTop = _ => { - const c = document.documentElement.scrollTop || document.body.scrollTop; - if (c > 0) { - window.requestAnimationFrame(scrollToTop); - window.scrollTo(0, c - c / 8); - } -}; -// scrollToTop() - -const shuffle = arr => { - let r = arr.map(Math.random); - return arr.sort((a, b) => r[a] - r[b]); -}; -// shuffle([1,2,3]) -> [2, 1, 3] - -const similarity = (arr, values) => arr.filter(v => values.includes(v)); -// similarity([1,2,3], [1,2,4]) -> [1,2] - -const sortCharactersInString = str => - str.split('').sort((a, b) => a.localeCompare(b)).join(''); -// sortCharactersInString('cabbage') -> 'aabbceg' - -const sum = arr => arr.reduce((acc, val) => acc + val, 0); -// sum([1,2,3,4]) -> 10 - -[varA, varB] = [varB, varA]; -// [x, y] = [y, x] - -const tail = arr => arr.length > 1 ? arr.slice(1) : arr; -// tail([1,2,3]) -> [2,3] -// tail([1]) -> [1] - -const truncate = (str, num) => - str.length > num ? str.slice(0, num > 3 ? num - 3 : num) + '...' : str; -// truncate('boomerang', 7) -> 'boom...' - -const unique = arr => [...new Set(arr)]; -// unique([1,2,2,3,4,4,5]) -> [1,2,3,4,5] - -const getUrlParameters = url => - url.match(/([^?=&]+)(=([^&]*))?/g).reduce( - (a, v) => (a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1), a), {} - ); -// getUrlParameters('http://url.com/page?name=Adam&surname=Smith') -> {name: 'Adam', surname: 'Smith'} - -const uuid = _ => - ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => - (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) - ); -// uuid() -> '7982fcfe-5721-4632-bede-6000885be57d' - -const validateNumber = n => !isNaN(parseFloat(n)) && isFinite(n); -// validateNumber('10') -> true - -const valueOrDefault = (value, d) => value || d; -// valueOrDefault(NaN, 30) -> 30 diff --git a/snippet-template.md b/snippet-template.md index e258fe079..83e820965 100644 --- a/snippet-template.md +++ b/snippet-template.md @@ -1,9 +1,9 @@ ### Snippet title -Explain briefly how the snippet works +Explain briefly how the snippet works. ```js -const functionName = arguments => +const functionName = arguments => {functionBody} // functionName(sampleInput) -> sampleOutput ``` diff --git a/snippets.js b/snippets.js deleted file mode 100644 index d754bf389..000000000 --- a/snippets.js +++ /dev/null @@ -1,302 +0,0 @@ - -const anagrams = str => { - if(str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str]; - return str.split('').reduce( (acc, letter, i) => - acc.concat(anagrams(str.slice(0, i) + str.slice(i + 1)).map( val => letter + val )), []); -} -// anagrams('abc') -> ['abc','acb','bac','bca','cab','cba'] - - -const average = arr => - arr.reduce( (acc , val) => acc + val, 0) / arr.length; -// average([1,2,3]) -> 2 - - -const bottomVisible = _ => - document.documentElement.clientHeight + window.scrollY >= document.documentElement.scrollHeight || document.documentElement.clientHeight; -// bottomVisible() -> true - - -const capitalizeEveryWord = str => str.replace(/\b[a-z]/g, char => char.toUpperCase()); -// capitalizeEveryWord('hello world!') -> 'Hello World!' - - -const capitalize = (str, lowerRest = false) => - str.slice(0, 1).toUpperCase() + (lowerRest? str.slice(1).toLowerCase() : str.slice(1)); -// capitalize('myName', true) -> 'Myname' - - -const chainAsync = fns => { let curr = 0; const next = () => fns[curr++](next); next(); } -/* -chainAsync([ - next => { console.log('0 seconds'); setTimeout(next, 1000); }, - next => { console.log('1 second'); setTimeout(next, 1000); }, - next => { console.log('2 seconds'); } -]) -*/ - - -const palindrome = str => - str.toLowerCase().replace(/[\W_]/g,'').split('').reverse().join('') === str.toLowerCase().replace(/[\W_]/g,''); -// palindrome('taco cat') -> true - - -const chunk = (arr, size) => - Array.apply(null, {length: Math.ceil(arr.length/size)}).map((v, i) => arr.slice(i*size, i*size+size)); -// chunk([1,2,3,4,5], 2) -> [[1,2],[3,4],5] - - -const countOccurrences = (arr, value) => arr.reduce((a, v) => v === value ? a + 1 : a + 0, 0); -// countOccurrences([1,1,2,1,2,3], 1) -> 3 - - -const currentUrl = _ => window.location.href; -// currentUrl() -> 'https://google.com' - - -const curry = (f, arity = f.length, next) => - (next = prevArgs => - nextArg => { - const args = [ ...prevArgs, nextArg ]; - return args.length >= arity ? f(...args) : next(args); - } - )([]); -// curry(Math.pow)(2)(10) -> 1024 -// curry(Math.min, 3)(10)(50)(2) -> 2 - - -const deepFlatten = arr => - arr.reduce( (a, v) => a.concat( Array.isArray(v) ? deepFlatten(v) : v ), []); -// deepFlatten([1,[2],[[3],4],5]) -> [1,2,3,4,5] - - -const difference = (arr, values) => arr.filter(v => !values.includes(v)); -// difference([1,2,3], [1,2]) -> [3] - - -const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0); -// distance(1,1, 2,3) -> 2.23606797749979 - - -const isDivisible = (dividend, divisor) => dividend % divisor === 0; -// isDivisible(6,3) -> true - - -const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); -// escapeRegExp('(test)') -> \\(test\\) - - -const isEven = num => Math.abs(num) % 2 === 0; -// isEven(3) -> false - - -const factorial = n => n <= 1 ? 1 : n * factorial(n - 1); -// factorial(6) -> 720 - - -const fibonacci = n => - Array(n).fill(0).reduce((acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i),[]); -// fibonacci(5) -> [0,1,1,2,3] - - -const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i)); -// filterNonUnique([1,2,2,3,4,4,5]) -> [1,3,5] - - -const flatten = arr => arr.reduce( (a, v) => a.concat(v), []); -// flatten([1,[2],3,4]) -> [1,2,3,4] - - -const arrayMax = arr => Math.max(...arr); -// arrayMax([10, 1, 5]) -> 10 - - -const arrayMin = arr => Math.min(...arr); -// arrayMin([10, 1, 5]) -> 1 - - -const getType = v => - v === undefined ? "undefined" : v === null ? "null" : v.constructor.name.toLowerCase(); -// getType(new Set([1,2,3])) -> "set" - - -const getScrollPos = (el = window) => - ( {x: (el.pageXOffset !== undefined) ? el.pageXOffset : el.scrollLeft, - y: (el.pageYOffset !== undefined) ? el.pageYOffset : el.scrollTop} ); -// getScrollPos() -> {x: 0, y: 200} - - -const gcd = (x , y) => !y ? x : gcd(y, x % y); -// gcd (8, 36) -> 4 - - -const hammingDistance = (num1, num2) => - ((num1^num2).toString(2).match(/1/g) || '').length; -// hammingDistance(2,3) -> 1 - - -const head = arr => arr[0]; -// head([1,2,3]) -> 1 - - -const initial = arr => arr.slice(0,-1); -// initial([1,2,3]) -> [1,2] - - -const initializeArrayRange = (end, start = 0) => - Array.apply(null, Array(end-start)).map( (v,i) => i + start ); -// initializeArrayRange(5) -> [0,1,2,3,4] - - -const initializeArray = (n, value = 0) => Array(n).fill(value); -// initializeArray(5, 2) -> [2,2,2,2,2] - - -const last = arr => arr.slice(-1)[0]; -// last([1,2,3]) -> 3 - - -const timeTaken = callback => { - const t0 = performance.now(), r = callback(); - console.log(performance.now() - t0); - return r; -} -// timeTaken(() => Math.pow(2, 10)) -> 1024 (0.010000000009313226 logged in console) - - -const median = arr => { - const mid = Math.floor(arr.length / 2), nums = arr.sort((a,b) => a - b); - return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2; -} -// median([5,6,50,1,-5]) -> 5 -// median([0,10,-2,7]) -> 3.5 - - -const objectFromPairs = arr => arr.reduce((a,v) => (a[v[0]] = v[1], a), {}); -// objectFromPairs([['a',1],['b',2]]) -> {a: 1, b: 2} - - -const percentile = (arr, val) => - 100 * arr.reduce((acc,v) => acc + (v < val ? 1 : 0) + (v === val ? 0.5 : 0), 0) / arr.length; -// percentile([1,2,3,4,5,6,7,8,9,10], 6) -> 55 - - -const pipe = (...funcs) => arg => funcs.reduce((acc, func) => func(acc), arg); -// pipe(btoa, x => x.toUpperCase())("Test") -> "VGVZDA==" - - -const powerset = arr => - arr.reduce( (a,v) => a.concat(a.map( r => [v].concat(r) )), [[]]); -// powerset([1,2]) -> [[], [1], [2], [2,1]] - - -const promisify = func => - (...args) => - new Promise((resolve, reject) => - func(...args, (err, result) => - err ? reject(err) : resolve(result)) - ); -// const delay = promisify((d, cb) => setTimeout(cb, d)) -// delay(2000).then(() => console.log('Hi!')) -> Promise resolves after 2s - - -const randomIntegerInRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; -// randomIntegerInRange(0, 5) -> 2 - - -const randomInRange = (min, max) => Math.random() * (max - min) + min; -// randomInRange(2,10) -> 6.0211363285087005 - - -const randomizeOrder = arr => arr.sort( (a,b) => Math.random() >= 0.5 ? -1 : 1); -// randomizeOrder([1,2,3]) -> [1,3,2] - - -const redirect = (url, asLink = true) => - asLink ? window.location.href = url : window.location.replace(url); -// redirect('https://google.com') - - -const reverseString = str => [...str].reverse().join(''); -// reverseString('foobar') -> 'raboof' - - -const rgbToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0'); -// rgbToHex(255, 165, 1) -> 'ffa501' - - -const series = ps => ps.reduce((p, next) => p.then(next), Promise.resolve()); -// const delay = (d) => new Promise(r => setTimeout(r, d)) -// series([() => delay(1000), () => delay(2000)]) -> executes each promise sequentially, taking a total of 3 seconds to complete - - -const scrollToTop = _ => { - const c = document.documentElement.scrollTop || document.body.scrollTop; - if(c > 0) { - window.requestAnimationFrame(scrollToTop); - window.scrollTo(0, c - c/8); - } -} -// scrollToTop() - - -const shuffle = arr => { - let r = arr.map(Math.random); - return arr.sort((a,b) => r[a] - r[b]); -} -// shuffle([1,2,3]) -> [2, 1, 3] - - -const similarity = (arr, values) => arr.filter(v => values.includes(v)); -// similarity([1,2,3], [1,2,4]) -> [1,2] - - -const sortCharactersInString = str => - str.split('').sort( (a,b) => a.localeCompare(b) ).join(''); -// sortCharactersInString('cabbage') -> 'aabbceg' - - -const sum = arr => arr.reduce( (acc , val) => acc + val, 0); -// sum([1,2,3,4]) -> 10 - - -[varA, varB] = [varB, varA]; -// [x, y] = [y, x] - - -const tail = arr => arr.length > 1 ? arr.slice(1) : arr; -// tail([1,2,3]) -> [2,3] -// tail([1]) -> [1] - - -const truncate = (str, num) => - str.length > num ? str.slice(0, num > 3 ? num-3 : num) + '...' : str; -// truncate('boomerang', 7) -> 'boom...' - - -const unique = arr => [...new Set(arr)]; -// unique([1,2,2,3,4,4,5]) -> [1,2,3,4,5] - - -const getUrlParameters = url => - url.match(/([^?=&]+)(=([^&]*))?/g).reduce( - (a,v) => (a[v.slice(0,v.indexOf('='))] = v.slice(v.indexOf('=')+1), a), {} - ); -// getUrlParameters('http://url.com/page?name=Adam&surname=Smith') -> {name: 'Adam', surname: 'Smith'} - - -const uuid = _ => - ( [1e7]+-1e3+-4e3+-8e3+-1e11 ).replace( /[018]/g, c => - (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) - ); -// uuid() -> '7982fcfe-5721-4632-bede-6000885be57d' - - -const validateNumber = n => !isNaN(parseFloat(n)) && isFinite(n); -// validateNumber('10') -> true - - -const valueOrDefault = (value, d) => value || d; -// valueOrDefault(NaN, 30) -> 30 - -