From edfca9c17c9abe0f1ef8eb0790a3cbbbe615fe91 Mon Sep 17 00:00:00 2001 From: 30secondsofcode <30secondsofcode@gmail.com> Date: Sat, 8 Sep 2018 13:59:38 +0000 Subject: [PATCH] Travis build: 411 --- README.md | 40 ++++++++++++++++++++-------------------- docs/adapter.html | 2 +- docs/browser.html | 26 +++++++++++++------------- docs/date.html | 2 +- docs/function.html | 10 +++++----- docs/index.html | 38 +++++++++++++++++++------------------- docs/math.html | 28 ++++++++++++++-------------- docs/node.html | 8 ++++---- docs/object.html | 8 ++++---- docs/string.html | 22 +++++++++++----------- docs/style.css | 2 +- docs/type.html | 26 +++++++++++++------------- docs/utility.html | 14 +++++++------- 13 files changed, 113 insertions(+), 113 deletions(-) diff --git a/README.md b/README.md index 895fc481f..b32ab8e08 100644 --- a/README.md +++ b/README.md @@ -155,10 +155,10 @@ average(1, 2, 3); * [`nthElement`](#nthelement) * [`offset`](#offset) * [`partition`](#partition) -* [`permutations`](#permutations) +* [`permutations`](#permutations-) * [`pull`](#pull) -* [`pullAtIndex`](#pullatindex) -* [`pullAtValue`](#pullatvalue) +* [`pullAtIndex`](#pullatindex-) +* [`pullAtValue`](#pullatvalue-) * [`pullBy`](#pullby-) * [`reducedFilter`](#reducedfilter) * [`reduceSuccessive`](#reducesuccessive) @@ -214,7 +214,7 @@ average(1, 2, 3); * [`currentURL`](#currenturl) * [`detectDeviceType`](#detectdevicetype) * [`elementContains`](#elementcontains) -* [`elementIsVisibleInViewport`](#elementisvisibleinviewport) +* [`elementIsVisibleInViewport`](#elementisvisibleinviewport-) * [`getScrollPosition`](#getscrollposition) * [`getStyle`](#getstyle) * [`hasClass`](#hasclass) @@ -274,14 +274,14 @@ average(1, 2, 3); * [`delay`](#delay) * [`functionName`](#functionname) * [`hz`](#hz) -* [`memoize`](#memoize) +* [`memoize`](#memoize-) * [`negate`](#negate) * [`once`](#once) * [`partial`](#partial) * [`partialRight`](#partialright) * [`runPromisesInSeries`](#runpromisesinseries) * [`sleep`](#sleep) -* [`throttle`](#throttle) +* [`throttle`](#throttle-) * [`times`](#times) * [`uncurry`](#uncurry) * [`unfold`](#unfold) @@ -313,7 +313,7 @@ average(1, 2, 3); * [`isEven`](#iseven) * [`isPrime`](#isprime) * [`lcm`](#lcm) -* [`luhnCheck`](#luhncheck) +* [`luhnCheck`](#luhncheck-) * [`maxBy`](#maxby) * [`median`](#median) * [`minBy`](#minby) @@ -421,14 +421,14 @@ average(1, 2, 3); * [`reverseString`](#reversestring) * [`sortCharactersInString`](#sortcharactersinstring) * [`splitLines`](#splitlines) -* [`stringPermutations`](#stringpermutations) +* [`stringPermutations`](#stringpermutations-) * [`stripHTMLTags`](#striphtmltags) * [`toCamelCase`](#tocamelcase) * [`toKebabCase`](#tokebabcase) * [`toSnakeCase`](#tosnakecase) * [`truncateString`](#truncatestring) * [`unescapeHTML`](#unescapehtml) -* [`URLJoin`](#urljoin) +* [`URLJoin`](#urljoin-) * [`words`](#words) @@ -477,7 +477,7 @@ average(1, 2, 3); * [`mostPerformant`](#mostperformant) * [`nthArg`](#ntharg) * [`parseCookie`](#parsecookie) -* [`prettyBytes`](#prettybytes) +* [`prettyBytes`](#prettybytes-) * [`randomHexColorCode`](#randomhexcolorcode) * [`RGBToHex`](#rgbtohex) * [`serializeCookie`](#serializecookie) @@ -2057,7 +2057,7 @@ partition(users, o => o.active); // [[{ 'user': 'fred', 'age': 40, 'active':
[⬆ Back to top](#table-of-contents) -### permutations +### permutations ![advanced](/advanced.svg) ⚠️ **WARNING**: This function's execution time increases exponentially with each array element. Anything more than 8 to 10 entries will cause your browser to hang as it tries to solve all the different combinations. @@ -2124,7 +2124,7 @@ pull(myArray, 'a', 'c'); // myArray = [ 'b', 'b' ]
[⬆ Back to top](#table-of-contents) -### pullAtIndex +### pullAtIndex ![advanced](/advanced.svg) Mutates the original array to filter out the values at the specified indexes. @@ -2157,7 +2157,7 @@ let pulled = pullAtIndex(myArray, [1, 3]); // myArray = [ 'a', 'c' ] , pulled =
[⬆ Back to top](#table-of-contents) -### pullAtValue +### pullAtValue ![advanced](/advanced.svg) Mutates the original array to filter out the values specified. Returns the removed elements. @@ -3543,7 +3543,7 @@ elementContains(document.querySelector('body'), document.querySelector('body'));
[⬆ Back to top](#table-of-contents) -### elementIsVisibleInViewport +### elementIsVisibleInViewport ![advanced](/advanced.svg) Returns `true` if the element specified is visible in the viewport, `false` otherwise. @@ -4819,7 +4819,7 @@ Math.round(hz(sumForLoop)); // 4784
[⬆ Back to top](#table-of-contents) -### memoize +### memoize ![advanced](/advanced.svg) Returns the memoized (cached) function. @@ -5006,7 +5006,7 @@ async function sleepyWork() {
[⬆ Back to top](#table-of-contents) -### throttle +### throttle ![advanced](/advanced.svg) Creates a throttled function that only invokes the provided function at most once per every `wait` milliseconds @@ -5689,7 +5689,7 @@ lcm(...[1, 3, 4, 5]); // 60
[⬆ Back to top](#table-of-contents) -### luhnCheck +### luhnCheck ![advanced](/advanced.svg) Implementation of the [Luhn Algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm) used to validate a variety of identification numbers, such as credit card numbers, IMEI numbers, National Provider Identifier numbers etc. @@ -8021,7 +8021,7 @@ splitLines('This\nis a\nmultiline\nstring.\n'); // ['This', 'is a', 'multiline',
[⬆ Back to top](#table-of-contents) -### stringPermutations +### stringPermutations ![advanced](/advanced.svg) ⚠️ **WARNING**: This function's execution time increases exponentially with each character. Anything more than 8 to 10 characters will cause your browser to hang as it tries to solve all the different combinations. @@ -8231,7 +8231,7 @@ unescapeHTML('<a href="#">Me & you</a>'); // '[⬆ Back to top](#table-of-contents) -### URLJoin +### URLJoin ![advanced](/advanced.svg) Joins all given URL segments together, then normalizes the resulting URL. @@ -9163,7 +9163,7 @@ parseCookie('foo=bar; equation=E%3Dmc%5E2'); // { foo: 'bar', equation: 'E=mc^2'
[⬆ Back to top](#table-of-contents) -### prettyBytes +### prettyBytes ![advanced](/advanced.svg) Converts a number in bytes to a human-readable string. diff --git a/docs/adapter.html b/docs/adapter.html index dedae7a1f..0ff2e5edd 100644 --- a/docs/adapter.html +++ b/docs/adapter.html @@ -73,7 +73,7 @@ },1700); } }, false); - }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Adapter

intermediate

ary

Creates a function that accepts up to n arguments, ignoring any additional arguments.

Call the provided function, fn, with up to n arguments, using Array.slice(0,n) and the spread operator (...).

const ary = (fn, n) => (...args) => fn(...args.slice(0, n));
+      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Adapter

intermediate

ary

Creates a function that accepts up to n arguments, ignoring any additional arguments.

Call the provided function, fn, with up to n arguments, using Array.slice(0,n) and the spread operator (...).

const ary = (fn, n) => (...args) => fn(...args.slice(0, n));
 
const firstTwoMax = ary(Math.max, 2);
 [[2, 6, 'a'], [8, 4, 6], [10]].map(x => firstTwoMax(...x)); // [6, 8, 10]
 
intermediate

call

Given a key and a set of arguments, call them when given a context. Primarily useful in composition.

Use a closure to call a stored key with stored arguments.

const call = (key, ...args) => context => context[key](...args);
diff --git a/docs/browser.html b/docs/browser.html
index 4ce2da36f..6ac0ffd16 100644
--- a/docs/browser.html
+++ b/docs/browser.html
@@ -73,7 +73,7 @@
             },1700);
           }
         }, false);
-      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Browser

intermediate

arrayToHtmlList

Converts the given array elements into <li> tags and appends them to the list of the given id.

Use Array.map(), document.querySelector(), and an anonymous inner closure to create a list of html tags.

const arrayToHtmlList = (arr, listID) =>
+      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Browser

intermediate

arrayToHtmlList

Converts the given array elements into <li> tags and appends them to the list of the given id.

Use Array.map(), document.querySelector(), and an anonymous inner closure to create a list of html tags.

const arrayToHtmlList = (arr, listID) =>
   (el => (
     (el = document.querySelector('#' + listID)),
     (el.innerHTML += arr.map(item => `<li>${item}</li>`).join(''))
@@ -113,7 +113,7 @@
   return timer;
 };
 
counter('#my-id', 1, 1000, 5, 2000); // Creates a 2-second timer for the element with id="my-id"
-
intermediate

createElement

Creates an element from a string (without appending it to the document). If the given string contains multiple elements, only the first one will be returned.

Use document.createElement() to create a new element. Set its innerHTML to the string supplied as the argument. Use ParentNode.firstElementChild to return the element version of the string.

const createElement = str => {
+
beginner

createElement

Creates an element from a string (without appending it to the document). If the given string contains multiple elements, only the first one will be returned.

Use document.createElement() to create a new element. Set its innerHTML to the string supplied as the argument. Use ParentNode.firstElementChild to return the element version of the string.

const createElement = str => {
   const el = document.createElement('div');
   el.innerHTML = str;
   return el.firstElementChild;
@@ -164,7 +164,7 @@ hub.off
intermediate

elementContains

Returns true if the parent element contains the child element, false otherwise.

Check that parent is not the same element as child, use parent.contains(child) to check if the parent element contains the child element.

const elementContains = (parent, child) => parent !== child && parent.contains(child);
 
elementContains(document.querySelector('head'), document.querySelector('title')); // true
 elementContains(document.querySelector('body'), document.querySelector('body')); // false
-
intermediate

elementIsVisibleInViewport

Returns true if the element specified is visible in the viewport, false otherwise.

Use Element.getBoundingClientRect() and the window.inner(Width|Height) values to determine if a given element is visible in the viewport. Omit the second argument to determine if the element is entirely visible, or specify true to determine if it is partially visible.

const elementIsVisibleInViewport = (el, partiallyVisible = false) => {
+
advanced

elementIsVisibleInViewport

Returns true if the element specified is visible in the viewport, false otherwise.

Use Element.getBoundingClientRect() and the window.inner(Width|Height) values to determine if a given element is visible in the viewport. Omit the second argument to determine if the element is entirely visible, or specify true to determine if it is partially visible.

const elementIsVisibleInViewport = (el, partiallyVisible = false) => {
   const { top, left, bottom, right } = el.getBoundingClientRect();
   const { innerHeight, innerWidth } = window;
   return partiallyVisible
@@ -180,7 +180,7 @@ hub.off: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop
 });
 
getScrollPosition(); // {x: 0, y: 200}
-
intermediate

getStyle

Returns the value of a CSS rule for the specified element.

Use Window.getComputedStyle() to get the value of the CSS rule for the specified element.

const getStyle = (el, ruleName) => getComputedStyle(el)[ruleName];
+
beginner

getStyle

Returns the value of a CSS rule for the specified element.

Use Window.getComputedStyle() to get the value of the CSS rule for the specified element.

const getStyle = (el, ruleName) => getComputedStyle(el)[ruleName];
 
getStyle(document.querySelector('p'), 'font-size'); // '16px'
 
beginner

hasClass

Returns true if the element has the specified class, false otherwise.

Use element.classList.contains() to check if the element has the specified class.

const hasClass = (el, className) => el.classList.contains(className);
 
hasClass(document.querySelector('p.special'), 'special'); // true
@@ -193,19 +193,19 @@ hub.offreturn hexes.join('');
   });
 
hashBrowser(JSON.stringify({ a: 'a', b: [1, 2, 3, 4], foo: { c: 'bar' } })).then(console.log); // '04aa106279f5977f59f9067fa9712afc4aedc6f5862a8defc34552d8c7206393'
-
intermediate

hide

Hides all the elements specified.

Use NodeList.prototype.forEach() to apply display: none to each element specified.

const hide = els => els.forEach(e => (e.style.display = 'none'));
+
beginner

hide

Hides all the elements specified.

Use NodeList.prototype.forEach() to apply display: none to each element specified.

const hide = els => els.forEach(e => (e.style.display = 'none'));
 
hide(document.querySelectorAll('img')); // Hides all <img> elements on the page
 
intermediate

httpsRedirect

Redirects the page to HTTPS if its currently in HTTP. Also, pressing the back button doesn't take it back to the HTTP page as its replaced in the history.

Use location.protocol to get the protocol currently being used. If it's not HTTPS, use location.replace() to replace the existing page with the HTTPS version of the page. Use location.href to get the full address, split it with String.split() and remove the protocol part of the URL.

const httpsRedirect = () => {
   if (location.protocol !== 'https:') location.replace('https://' + location.href.split('//')[1]);
 };
 
httpsRedirect(); // If you are on http://mydomain.com, you are redirected to https://mydomain.com
-
intermediate

insertAfter

Inserts an HTML string after the end of the specified element.

Use el.insertAdjacentHTML() with a position of 'afterend' to parse htmlString and insert it after the end of el.

const insertAfter = (el, htmlString) => el.insertAdjacentHTML('afterend', htmlString);
+
beginner

insertAfter

Inserts an HTML string after the end of the specified element.

Use el.insertAdjacentHTML() with a position of 'afterend' to parse htmlString and insert it after the end of el.

const insertAfter = (el, htmlString) => el.insertAdjacentHTML('afterend', htmlString);
 
insertAfter(document.getElementById('myId'), '<p>after</p>'); // <div id="myId">...</div> <p>after</p>
-
intermediate

insertBefore

Inserts an HTML string before the start of the specified element.

Use el.insertAdjacentHTML() with a position of 'beforebegin' to parse htmlString and insert it before the start of el.

const insertBefore = (el, htmlString) => el.insertAdjacentHTML('beforebegin', htmlString);
+
beginner

insertBefore

Inserts an HTML string before the start of the specified element.

Use el.insertAdjacentHTML() with a position of 'beforebegin' to parse htmlString and insert it before the start of el.

const insertBefore = (el, htmlString) => el.insertAdjacentHTML('beforebegin', htmlString);
 
insertBefore(document.getElementById('myId'), '<p>before</p>'); // <p>before</p> <div id="myId">...</div>
-
intermediate

isBrowserTabFocused

Returns true if the browser tab of the page is focused, false otherwise.

Use the Document.hidden property, introduced by the Page Visibility API to check if the browser tab of the page is visible or hidden.

const isBrowserTabFocused = () => !document.hidden;
+
beginner

isBrowserTabFocused

Returns true if the browser tab of the page is focused, false otherwise.

Use the Document.hidden property, introduced by the Page Visibility API to check if the browser tab of the page is visible or hidden.

const isBrowserTabFocused = () => !document.hidden;
 
isBrowserTabFocused(); // true
-
intermediate

nodeListToArray

Converts a NodeList to an array.

Use spread operator inside new array to convert a NodeList to an array.

const nodeListToArray = nodeList => [...nodeList];
+
beginner

nodeListToArray

Converts a NodeList to an array.

Use spread operator inside new array to convert a NodeList to an array.

const nodeListToArray = nodeList => [...nodeList];
 
nodeListToArray(document.childNodes); // [ <!DOCTYPE html>, html ]
 
advanced

observeMutations

Returns a new MutationObserver and runs the provided callback for each mutation on the specified element.

Use a MutationObserver to observe mutations on the given element. Use Array.forEach() to run the callback for each mutation that is observed. Omit the third argument, options, to use the default options (all true).

const observeMutations = (element, callback, options) => {
   const observer = new MutationObserver(mutations => mutations.forEach(m => callback(m)));
@@ -291,7 +291,7 @@ document.body.stop(); // stops logging
 recorder.start(); // starts again
 const recorder2 = recordAnimationFrames(cb, false); // `start` needs to be explicitly called to begin recording frames
-
intermediate

redirect

Redirects to a specified URL.

Use window.location.href or window.location.replace() to redirect to url. Pass a second argument to simulate a link click (true - default) or an HTTP redirect (false).

const redirect = (url, asLink = true) =>
+
beginner

redirect

Redirects to a specified URL.

Use window.location.href or window.location.replace() to redirect to url. Pass a second argument to simulate a link click (true - default) or an HTTP redirect (false).

const redirect = (url, asLink = true) =>
   asLink ? (window.location.href = url) : window.location.replace(url);
 
redirect('https://google.com');
 
advanced

runAsync

Runs a function in a separate thread by using a Web Worker, allowing long running functions to not block the UI.

Create a new Worker using a Blob object URL, the contents of which should be the stringified version of the supplied function. Immediately post the return value of calling the function back. Return a promise, listening for onmessage and onerror events and resolving the data posted back from the worker, or throwing an error.

const runAsync = fn => {
@@ -337,9 +337,9 @@ recorder.sta
   }
 };
 
scrollToTop();
-
intermediate

setStyle

Sets the value of a CSS rule for the specified element.

Use element.style to set the value of the CSS rule for the specified element to val.

const setStyle = (el, ruleName, val) => (el.style[ruleName] = val);
+
beginner

setStyle

Sets the value of a CSS rule for the specified element.

Use element.style to set the value of the CSS rule for the specified element to val.

const setStyle = (el, ruleName, val) => (el.style[ruleName] = val);
 
setStyle(document.querySelector('p'), 'font-size', '20px'); // The first <p> element on the page will have a font-size of 20px
-
intermediate

show

Shows all the elements specified.

Use the spread operator (...) and Array.forEach() to clear the display property for each element specified.

const show = (...el) => [...el].forEach(e => (e.style.display = ''));
+
beginner

show

Shows all the elements specified.

Use the spread operator (...) and Array.forEach() to clear the display property for each element specified.

const show = (...el) => [...el].forEach(e => (e.style.display = ''));
 
show(...document.querySelectorAll('img')); // Shows all <img> elements on the page
 
intermediate

smoothScroll

Smoothly scrolls the element on which it's called into the visible area of the browser window.

Use .scrollIntoView method to scroll the element. Pass { behavior: 'smooth' } to .scrollIntoView so it scrolls smoothly.

const smoothScroll = element =>
   document.querySelector(element).scrollIntoView({
@@ -347,7 +347,7 @@ recorder.sta
   });
 
smoothScroll('#fooBar'); // scrolls smoothly to the element with the id fooBar
 smoothScroll('.fooBar'); // scrolls smoothly to the first element with a class of fooBar
-
intermediate

toggleClass

Toggle a class for an element.

Use element.classList.toggle() to toggle the specified class for the element.

const toggleClass = (el, className) => el.classList.toggle(className);
+
beginner

toggleClass

Toggle a class for an element.

Use element.classList.toggle() to toggle the specified class for the element.

const toggleClass = (el, className) => el.classList.toggle(className);
 
toggleClass(document.querySelector('p.special'), 'special'); // The paragraph will not have the 'special' class anymore
 
intermediate

triggerEvent

Triggers a specific event on a given element, optionally passing custom data.

Use new CustomEvent() to create an event from the specified eventType and details. Use el.dispatchEvent() to trigger the newly created event on the given element. Omit the third argument, detail, if you do not want to pass custom data to the triggered event.

const triggerEvent = (el, eventType, detail = undefined) =>
   el.dispatchEvent(new CustomEvent(eventType, { detail: detail }));
diff --git a/docs/date.html b/docs/date.html
index 97d3b1983..4bf3f826e 100644
--- a/docs/date.html
+++ b/docs/date.html
@@ -73,7 +73,7 @@
             },1700);
           }
         }, false);
-      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Date

intermediate

formatDuration

Returns the human readable format of the given number of milliseconds.

Divide ms with the appropriate values to obtain the appropriate values for day, hour, minute, second and millisecond. Use Object.entries() with Array.filter() to keep only non-zero values. Use Array.map() to create the string for each value, pluralizing appropriately. Use String.join(', ') to combine the values into a string.

const formatDuration = ms => {
+      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Date

intermediate

formatDuration

Returns the human readable format of the given number of milliseconds.

Divide ms with the appropriate values to obtain the appropriate values for day, hour, minute, second and millisecond. Use Object.entries() with Array.filter() to keep only non-zero values. Use Array.map() to create the string for each value, pluralizing appropriately. Use String.join(', ') to combine the values into a string.

const formatDuration = ms => {
   if (ms < 0) ms = -ms;
   const time = {
     day: Math.floor(ms / 86400000),
diff --git a/docs/function.html b/docs/function.html
index 088695248..c0896a347 100644
--- a/docs/function.html
+++ b/docs/function.html
@@ -73,7 +73,7 @@
             },1700);
           }
         }, false);
-      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Function

intermediate

attempt

Attempts to invoke a function with the provided arguments, returning either the result or the caught error object.

Use a try... catch block to return either the result of the function or an appropriate error.

const attempt = (fn, ...args) => {
+      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Function

intermediate

attempt

Attempts to invoke a function with the provided arguments, returning either the result or the caught error object.

Use a try... catch block to return either the result of the function or an appropriate error.

const attempt = (fn, ...args) => {
   try {
     return fn(...args);
   } catch (e) {
@@ -168,7 +168,7 @@ document.que
   1000,
   'later'
 ); // Logs 'later' after one second.
-
intermediate

functionName

Logs the name of a function.

Use console.debug() and the name property of the passed method to log the method's name to the debug channel of the console.

const functionName = fn => (console.debug(fn.name), fn);
+
beginner

functionName

Logs the name of a function.

Use console.debug() and the name property of the passed method to log the method's name to the debug channel of the console.

const functionName = fn => (console.debug(fn.name), fn);
 
functionName(Math.max); // max (logged in debug channel of console)
 
intermediate

hz

Returns the number of times a function executed per second. hz is the unit for hertz, the unit of frequency defined as one cycle per second.

Use performance.now() to get the difference in milliseconds before and after the iteration loop to calculate the time elapsed executing the function iterations times. Return the number of cycles per second by converting milliseconds to seconds and dividing it by the time elapsed. Omit the second argument, iterations, to use the default of 100 iterations.

const hz = (fn, iterations = 100) => {
   const before = performance.now();
@@ -191,7 +191,7 @@ document.que
 // `sumForLoop` is nearly 10 times faster
 Math.round(hz(sumReduce)); // 572
 Math.round(hz(sumForLoop)); // 4784
-
intermediate

memoize

Returns the memoized (cached) function.

Create an empty cache by instantiating a new Map object. Return a function which takes a single argument to be supplied to the memoized function by first checking if the function's output for that specific input value is already cached, or store and return it if not. The function keyword must be used in order to allow the memoized function to have its this context changed if necessary. Allow access to the cache by setting it as a property on the returned function.

const memoize = fn => {
+
advanced

memoize

Returns the memoized (cached) function.

Create an empty cache by instantiating a new Map object. Return a function which takes a single argument to be supplied to the memoized function by first checking if the function's output for that specific input value is already cached, or store and return it if not. The function keyword must be used in order to allow the memoized function to have its this context changed if necessary. Allow access to the cache by setting it as a property on the returned function.

const memoize = fn => {
   const cache = new Map();
   const cached = function(val) {
     return cache.has(val) ? cache.get(val) : cache.set(val, fn.call(this, val)) && cache.get(val);
@@ -204,7 +204,7 @@ Math.roundanagramsCached('javascript'); // takes a long time
 anagramsCached('javascript'); // returns virtually instantly since it's now cached
 console.log(anagramsCached.cache); // The cached anagrams map
-
intermediate

negate

Negates a predicate function.

Take a predicate function and apply the not operator (!) to it with its arguments.

const negate = func => (...args) => !func(...args);
+
beginner

negate

Negates a predicate function.

Take a predicate function and apply the not operator (!) to it with its arguments.

const negate = func => (...args) => !func(...args);
 
[1, 2, 3, 4, 5, 6].filter(negate(n => n % 2 === 0)); // [ 1, 3, 5 ]
 
intermediate

once

Ensures a function is called only once.

Utilizing a closure, use a flag, called, and set it to true once the function is called for the first time, preventing it from being called again. In order to allow the function to have its this context changed (such as in an event listener), the function keyword must be used, and the supplied function must have the context applied. Allow the function to be supplied with an arbitrary number of arguments using the rest/spread (...) operator.

const once = fn => {
   let called = false;
@@ -235,7 +235,7 @@ document.bodyawait sleep(1000);
   console.log('I woke up after 1 second.');
 }
-
intermediate

throttle

Creates a throttled function that only invokes the provided function at most once per every wait milliseconds

Use setTimeout() and clearTimeout() to throttle the given method, fn. Use Function.apply() to apply the this context to the function and provide the necessary arguments. Use Date.now() to keep track of the last time the throttled function was invoked. Omit the second argument, wait, to set the timeout at a default of 0 ms.

const throttle = (fn, wait) => {
+
advanced

throttle

Creates a throttled function that only invokes the provided function at most once per every wait milliseconds

Use setTimeout() and clearTimeout() to throttle the given method, fn. Use Function.apply() to apply the this context to the function and provide the necessary arguments. Use Date.now() to keep track of the last time the throttled function was invoked. Omit the second argument, wait, to set the timeout at a default of 0 ms.

const throttle = (fn, wait) => {
   let inThrottle, lastFn, lastTime;
   return function() {
     const context = this,
diff --git a/docs/index.html b/docs/index.html
index 7a5f2cfc4..2057a85f1 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -73,13 +73,13 @@
             },1700);
           }
         }, false);
-      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Array

intermediate

all

Returns true if the provided predicate function returns true for all elements in a collection, false otherwise.

Use Array.every() to test if all elements in the collection return true based on fn. Omit the second argument, fn, to use Boolean as a default.

const all = (arr, fn = Boolean) => arr.every(fn);
+      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Array

beginner

all

Returns true if the provided predicate function returns true for all elements in a collection, false otherwise.

Use Array.every() to test if all elements in the collection return true based on fn. Omit the second argument, fn, to use Boolean as a default.

const all = (arr, fn = Boolean) => arr.every(fn);
 
all([4, 2, 3], x => x > 1); // true
 all([1, 2, 3]); // true
 
beginner

allEqual

Check if all elements are equal

Use Array.every() to check if all the elements of the array are the same as the first one.

const allEqual = arr => arr.every(val => val === arr[0]);
 
allEqual([1, 2, 3, 4, 5, 6]); // false
 allEqual([1, 1, 1, 1]); // true
-
intermediate

any

Returns true if the provided predicate function returns true for at least one element in a collection, false otherwise.

Use Array.some() to test if any elements in the collection return true based on fn. Omit the second argument, fn, to use Boolean as a default.

const any = (arr, fn = Boolean) => arr.some(fn);
+
beginner

any

Returns true if the provided predicate function returns true for at least one element in a collection, false otherwise.

Use Array.some() to test if any elements in the collection return true based on fn. Omit the second argument, fn, to use Boolean as a default.

const any = (arr, fn = Boolean) => arr.some(fn);
 
any([0, 1, 2, 0], x => x >= 2); // true
 any([0, 0, 1, 0]); // true
 
intermediate

arrayToCSV

Converts a 2D array to a comma-separated values (CSV) string.

Use Array.map() and Array.join(delimiter) to combine individual 1D arrays (rows) into strings. Use Array.join('\n') to combine all rows into a CSV string, separating each row with a newline. Omit the second argument, delimiter, to use a default delimiter of ,.

const arrayToCSV = (arr, delimiter = ',') =>
@@ -97,7 +97,7 @@
     arr.slice(i * size, i * size + size)
   );
 
chunk([1, 2, 3, 4, 5], 2); // [[1,2],[3,4],[5]]
-
intermediate

compact

Removes falsey values from an array.

Use Array.filter() to filter out falsey values (false, null, 0, "", undefined, and NaN).

const compact = arr => arr.filter(Boolean);
+
beginner

compact

Removes falsey values from an array.

Use Array.filter() to filter out falsey values (false, null, 0, "", undefined, and NaN).

const compact = arr => arr.filter(Boolean);
 
compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34]); // [ 1, 2, 3, 'a', 's', 34 ]
 
intermediate

countBy

Groups the elements of an array based on the given function and returns the count of elements in each group.

Use Array.map() to map the values of an array to a function or property name. Use Array.reduce() to create an object, where the keys are produced from the mapped results.

const countBy = (arr, fn) =>
   arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => {
@@ -110,7 +110,7 @@
 
countOccurrences([1, 1, 2, 1, 2, 3], 1); // 3
 
intermediate

deepFlatten

Deep flattens an array.

Use recursion. Use Array.concat() with an empty array ([]) and the spread operator (...) to flatten an array. Recursively flatten each element that is an array.

const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)));
 
deepFlatten([1, [2], [[3], 4], 5]); // [1,2,3,4,5]
-
intermediate

difference

Returns the difference between two arrays.

Create a Set from b, then use Array.filter() on a to only keep values not contained in b.

const difference = (a, b) => {
+
beginner

difference

Returns the difference between two arrays.

Create a Set from b, then use Array.filter() on a to only keep values not contained in b.

const difference = (a, b) => {
   const s = new Set(b);
   return a.filter(x => !s.has(x));
 };
@@ -123,11 +123,11 @@
 differenceBy([{ x: 2 }, { x: 1 }], [{ x: 1 }], v => v.x); // [ { x: 2 } ]
 
intermediate

differenceWith

Filters out all values from an array for which the comparator function does not return true.

Use Array.filter() and Array.findIndex() to find the appropriate values.

const differenceWith = (arr, val, comp) => arr.filter(a => val.findIndex(b => comp(a, b)) === -1);
 
differenceWith([1, 1.2, 1.5, 3, 0], [1.9, 3, 0], (a, b) => Math.round(a) === Math.round(b)); // [1, 1.2]
-
intermediate

drop

Returns a new array with n elements removed from the left.

Use Array.slice() to slice the remove the specified number of elements from the left.

const drop = (arr, n = 1) => arr.slice(n);
+
beginner

drop

Returns a new array with n elements removed from the left.

Use Array.slice() to slice the remove the specified number of elements from the left.

const drop = (arr, n = 1) => arr.slice(n);
 
drop([1, 2, 3]); // [2,3]
 drop([1, 2, 3], 2); // [3]
 drop([1, 2, 3], 42); // []
-
intermediate

dropRight

Returns a new array with n elements removed from the right.

Use Array.slice() to slice the remove the specified number of elements from the right.

const dropRight = (arr, n = 1) => arr.slice(0, -n);
+
beginner

dropRight

Returns a new array with n elements removed from the right.

Use Array.slice() to slice the remove the specified number of elements from the right.

const dropRight = (arr, n = 1) => arr.slice(0, -n);
 
dropRight([1, 2, 3]); // [1,2]
 dropRight([1, 2, 3], 2); // [1]
 dropRight([1, 2, 3], 42); // []
@@ -157,7 +157,7 @@
   ],
   (a, b) => a.id == b.id
 ); // [ { id: 2, value: 'c' } ]
-
intermediate

findLast

Returns the last element for which the provided function returns a truthy value.

Use Array.filter() to remove elements for which fn returns falsey values, Array.pop() to get the last one.

const findLast = (arr, fn) => arr.filter(fn).pop();
+
beginner

findLast

Returns the last element for which the provided function returns a truthy value.

Use Array.filter() to remove elements for which fn returns falsey values, Array.pop() to get the last one.

const findLast = (arr, fn) => arr.filter(fn).pop();
 
findLast([1, 2, 3, 4], n => n % 2 === 1); // 3
 
intermediate

findLastIndex

Returns the index of the last element for which the provided function returns a truthy value.

Use Array.map() to map each element to an array with its index and value. Use Array.filter() to remove elements for which fn returns falsey values, Array.pop() to get the last one.

const findLastIndex = (arr, fn) =>
   arr
@@ -182,12 +182,12 @@
   }, {});
 
groupBy([6.1, 4.2, 6.3], Math.floor); // {4: [4.2], 6: [6.1, 6.3]}
 groupBy(['one', 'two', 'three'], 'length'); // {3: ['one', 'two'], 5: ['three']}
-
intermediate

Returns the head of a list.

Use arr[0] to return the first element of the passed array.

const head = arr => arr[0];
+
beginner

Returns the head of a list.

Use arr[0] to return the first element of the passed array.

const head = arr => arr[0];
 
head([1, 2, 3]); // 1
 
intermediate

indexOfAll

Returns all indices of val in an array. If val never occurs, returns [].

Use Array.reduce() to loop over elements and store indices for matching elements. Return the array of indices.

const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []);
 
indexOfAll([1, 2, 3, 1, 2, 3], 1); // [0,3]
 indexOfAll([1, 2, 3], 4); // []
-
intermediate

initial

Returns all the elements of an array except the last one.

Use arr.slice(0,-1) to return all but the last element of the array.

const initial = arr => arr.slice(0, -1);
+
beginner

initial

Returns all the elements of an array except the last one.

Use arr.slice(0,-1) to return all but the last element of the array.

const initial = arr => arr.slice(0, -1);
 
initial([1, 2, 3]); // [1,2]
 
intermediate

initialize2DArray

Initializes a 2D array of given width and height and value.

Use Array.map() to generate h rows where each is a new array of size w initialize with value. If the value is not provided, default to null.

const initialize2DArray = (w, h, val = null) =>
   Array.from({ length: h }).map(() => Array.from({ length: w }).fill(val));
@@ -278,10 +278,10 @@
 
beginner

maxN

Returns the n maximum elements from the provided array. If n is greater than or equal to the provided array's length, then return the original array(sorted in descending order).

Use Array.sort() combined with the spread operator (...) to create a shallow clone of the array and sort it in descending order. Use Array.slice() to get the specified number of elements. Omit the second argument, n, to get a one-element array.

const maxN = (arr, n = 1) => [...arr].sort((a, b) => b - a).slice(0, n);
 
maxN([1, 2, 3]); // [3]
 maxN([1, 2, 3], 2); // [3,2]
-
intermediate

minN

Returns the n minimum elements from the provided array. If n is greater than or equal to the provided array's length, then return the original array(sorted in ascending order).

Use Array.sort() combined with the spread operator (...) to create a shallow clone of the array and sort it in ascending order. Use Array.slice() to get the specified number of elements. Omit the second argument, n, to get a one-element array.

const minN = (arr, n = 1) => [...arr].sort((a, b) => a - b).slice(0, n);
+
beginner

minN

Returns the n minimum elements from the provided array. If n is greater than or equal to the provided array's length, then return the original array(sorted in ascending order).

Use Array.sort() combined with the spread operator (...) to create a shallow clone of the array and sort it in ascending order. Use Array.slice() to get the specified number of elements. Omit the second argument, n, to get a one-element array.

const minN = (arr, n = 1) => [...arr].sort((a, b) => a - b).slice(0, n);
 
minN([1, 2, 3]); // [1]
 minN([1, 2, 3], 2); // [1,2]
-
intermediate

none

Returns true if the provided predicate function returns false for all elements in a collection, false otherwise.

Use Array.some() to test if any elements in the collection return true based on fn. Omit the second argument, fn, to use Boolean as a default.

const none = (arr, fn = Boolean) => !arr.some(fn);
+
beginner

none

Returns true if the provided predicate function returns false for all elements in a collection, false otherwise.

Use Array.some() to test if any elements in the collection return true based on fn. Omit the second argument, fn, to use Boolean as a default.

const none = (arr, fn = Boolean) => !arr.some(fn);
 
none([0, 1, 3, 0], x => x == 2); // true
 none([0, 0, 0]); // true
 
beginner

nthElement

Returns the nth element of an array.

Use Array.slice() to get an array containing the nth element at the first place. If the index is out of bounds, return undefined. Omit the second argument, n, to get the first element of the array.

const nthElement = (arr, n = 0) => (n === -1 ? arr.slice(n) : arr.slice(n, n + 1))[0];
@@ -300,7 +300,7 @@
   );
 
const users = [{ user: 'barney', age: 36, active: false }, { user: 'fred', age: 40, active: true }];
 partition(users, o => o.active); // [[{ 'user': 'fred',    'age': 40, 'active': true }],[{ 'user': 'barney',  'age': 36, 'active': false }]]
-
intermediate

permutations

⚠️ WARNING: This function's execution time increases exponentially with each array element. Anything more than 8 to 10 entries will cause your browser to hang as it tries to solve all the different combinations.

Generates all permutations of an array's elements (contains duplicates).

Use recursion. For each element in the given array, create all the partial permutations for the rest of its elements. Use Array.map() to combine the element with each partial permutation, then Array.reduce() to combine all permutations in one array. Base cases are for array length equal to 2 or 1.

const permutations = arr => {
+
advanced

permutations

⚠️ WARNING: This function's execution time increases exponentially with each array element. Anything more than 8 to 10 entries will cause your browser to hang as it tries to solve all the different combinations.

Generates all permutations of an array's elements (contains duplicates).

Use recursion. For each element in the given array, create all the partial permutations for the rest of its elements. Use Array.map() to combine the element with each partial permutation, then Array.reduce() to combine all permutations in one array. Base cases are for array length equal to 2 or 1.

const permutations = arr => {
   if (arr.length <= 2) return arr.length === 2 ? [arr, [arr[1], arr[0]]] : arr;
   return arr.reduce(
     (acc, item, i) =>
@@ -319,7 +319,7 @@
 };
 
let myArray = ['a', 'b', 'c', 'a', 'b', 'c'];
 pull(myArray, 'a', 'c'); // myArray = [ 'b', 'b' ]
-
intermediate

pullAtIndex

Mutates the original array to filter out the values at the specified indexes.

Use Array.filter() and Array.includes() to pull out the values that are not needed. Use Array.length = 0 to mutate the passed in an array by resetting it's length to zero and Array.push() to re-populate it with only the pulled values. Use Array.push() to keep track of pulled values

const pullAtIndex = (arr, pullArr) => {
+
advanced

pullAtIndex

Mutates the original array to filter out the values at the specified indexes.

Use Array.filter() and Array.includes() to pull out the values that are not needed. Use Array.length = 0 to mutate the passed in an array by resetting it's length to zero and Array.push() to re-populate it with only the pulled values. Use Array.push() to keep track of pulled values

const pullAtIndex = (arr, pullArr) => {
   let removed = [];
   let pulled = arr
     .map((v, i) => (pullArr.includes(i) ? removed.push(v) : v))
@@ -330,7 +330,7 @@
 };
 
let myArray = ['a', 'b', 'c', 'd'];
 let pulled = pullAtIndex(myArray, [1, 3]); // myArray = [ 'a', 'c' ] , pulled = [ 'b', 'd' ]
-
intermediate

pullAtValue

Mutates the original array to filter out the values specified. Returns the removed elements.

Use Array.filter() and Array.includes() to pull out the values that are not needed. Use Array.length = 0 to mutate the passed in an array by resetting it's length to zero and Array.push() to re-populate it with only the pulled values. Use Array.push() to keep track of pulled values

const pullAtValue = (arr, pullArr) => {
+
advanced

pullAtValue

Mutates the original array to filter out the values specified. Returns the removed elements.

Use Array.filter() and Array.includes() to pull out the values that are not needed. Use Array.length = 0 to mutate the passed in an array by resetting it's length to zero and Array.push() to re-populate it with only the pulled values. Use Array.push() to keep track of pulled values

const pullAtValue = (arr, pullArr) => {
   let removed = [],
     pushToRemove = arr.forEach((v, i) => (pullArr.includes(v) ? removed.push(v) : v)),
     mutateTo = arr.filter((v, i) => !pullArr.includes(v));
@@ -383,7 +383,7 @@
   [{ name: 'Tom', age: 12 }, { name: 'Jack', age: 18 }, { name: 'Lucy', age: 9 }],
   (a, b) => a.age - b.age
 ); // {name: "Lucy", age: 9}
-
intermediate

reject

Takes a predicate and array, like Array.filter(), but only keeps x if pred(x) === false.

const reject = (pred, array) => array.filter((...args) => !pred(...args));
+
beginner

reject

Takes a predicate and array, like Array.filter(), but only keeps x if pred(x) === false.

const reject = (pred, array) => array.filter((...args) => !pred(...args));
 
reject(x => x % 2 === 0, [1, 2, 3, 4, 5]); // [1, 3, 5]
 reject(word => word.length > 4, ['Apple', 'Pear', 'Kiwi', 'Banana']); // ['Pear', 'Kiwi']
 
intermediate

remove

Removes elements from an array for which the given function returns false.

Use Array.filter() to find array elements that return truthy values and Array.reduce() to remove elements using Array.splice(). The func is invoked with three arguments (value, index, array).

const remove = (arr, func) =>
@@ -480,7 +480,7 @@
 
beginner

tail

Returns all elements in an array except for the first one.

Return Array.slice(1) if the array's length is more than 1, otherwise, return the whole array.

const tail = arr => (arr.length > 1 ? arr.slice(1) : arr);
 
tail([1, 2, 3]); // [2,3]
 tail([1]); // [1]
-
intermediate

take

Returns an array with n elements removed from the beginning.

Use Array.slice() to create a slice of the array with n elements taken from the beginning.

const take = (arr, n = 1) => arr.slice(0, n);
+
beginner

take

Returns an array with n elements removed from the beginning.

Use Array.slice() to create a slice of the array with n elements taken from the beginning.

const take = (arr, n = 1) => arr.slice(0, n);
 
take([1, 2, 3], 5); // [1, 2, 3]
 take([1, 2, 3], 0); // []
 
intermediate

takeRight

Returns an array with n elements removed from the end.

Use Array.slice() to create a slice of the array with n elements taken from the end.

const takeRight = (arr, n = 1) => arr.slice(arr.length - n, arr.length);
@@ -516,7 +516,7 @@ managers.for
     }, toHash(users, 'id')))
 );
 managers; // [ { manager:1, employees: [ { id: 2, first: "Joe" }, { id: 3, first: "Moe" } ] } ]
-
intermediate

union

Returns every element that exists in any of the two arrays once.

Create a Set with all values of a and b and convert to an array.

const union = (a, b) => Array.from(new Set([...a, ...b]));
+
beginner

union

Returns every element that exists in any of the two arrays once.

Create a Set with all values of a and b and convert to an array.

const union = (a, b) => Array.from(new Set([...a, ...b]));
 
union([1, 2, 3], [4, 3, 2]); // [1,2,3,4]
 
intermediate

unionBy

Returns every element that exists in any of the two arrays once, after applying the provided function to each array element of both.

Create a Set by applying all fn to all values of a. Create a Set from a and all elements in b whose value, after applying fn does not match a value in the previously created set. Return the last set converted to an array.

const unionBy = (a, b, fn) => {
   const s = new Set(a.map(v => fn(v)));
@@ -526,7 +526,7 @@ managers; //
 
intermediate

unionWith

Returns every element that exists in any of the two arrays once, using a provided comparator function.

Create a Set with all values of a and values in b for which the comparator finds no matches in a, using Array.findIndex().

const unionWith = (a, b, comp) =>
   Array.from(new Set([...a, ...b.filter(x => a.findIndex(y => comp(x, y)) === -1)]));
 
unionWith([1, 1.2, 1.5, 3, 0], [1.9, 3, 0, 3.9], (a, b) => Math.round(a) === Math.round(b)); // [1, 1.2, 1.5, 3, 0, 3.9]
-
intermediate

uniqueElements

Returns all unique values of an array.

Use ES6 Set and the ...rest operator to discard all duplicated values.

const uniqueElements = arr => [...new Set(arr)];
+
beginner

uniqueElements

Returns all unique values of an array.

Use ES6 Set and the ...rest operator to discard all duplicated values.

const uniqueElements = arr => [...new Set(arr)];
 
uniqueElements([1, 2, 2, 3, 4, 4, 5]); // [1,2,3,4,5]
 
intermediate

uniqueElementsBy

Returns all unique values of an array, based on a provided comparator function.

Use Array.reduce() and Array.some() for an array containing only the first unique occurence of each value, based on the comparator function, fn. The comparator function takes two arguments: the values of the two elements being compared.

const uniqueElementsBy = (arr, fn) =>
   arr.reduce((acc, v) => {
@@ -582,7 +582,7 @@ managers; //
     )
     .map(val => fn(...val));
 
unzipWith([[1, 10, 100], [2, 20, 200]], (...args) => args.reduce((acc, v) => acc + v, 0)); // [3, 30, 300]
-
intermediate

without

Filters out the elements of an array, that have one of the specified values.

Use Array.filter() to create an array excluding(using !Array.includes()) all given values.

(For a snippet that mutates the original array see pull)

const without = (arr, ...args) => arr.filter(v => !args.includes(v));
+
beginner

without

Filters out the elements of an array, that have one of the specified values.

Use Array.filter() to create an array excluding(using !Array.includes()) all given values.

(For a snippet that mutates the original array see pull)

const without = (arr, ...args) => arr.filter(v => !args.includes(v));
 
without([2, 1, 2, 3], 1, 2); // [3]
 
intermediate

xProd

Creates a new array out of the two supplied by creating each possible pair from the arrays.

Use Array.reduce(), Array.map() and Array.concat() to produce every possible pair from the elements of the two arrays and save them in an array.

const xProd = (a, b) => a.reduce((acc, x) => acc.concat(b.map(y => [x, y])), []);
 
xProd([1, 2], ['a', 'b']); // [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]
diff --git a/docs/math.html b/docs/math.html
index 1991adea0..64280419e 100644
--- a/docs/math.html
+++ b/docs/math.html
@@ -73,9 +73,9 @@
             },1700);
           }
         }, false);
-      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Math

intermediate

approximatelyEqual

Checks if two numbers are approximately equal to each other.

Use Math.abs() to compare the absolute difference of the two values to epsilon. Omit the third parameter, epsilon, to use a default value of 0.001.

const approximatelyEqual = (v1, v2, epsilon = 0.001) => Math.abs(v1 - v2) < epsilon;
+      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Math

beginner

approximatelyEqual

Checks if two numbers are approximately equal to each other.

Use Math.abs() to compare the absolute difference of the two values to epsilon. Omit the third parameter, epsilon, to use a default value of 0.001.

const approximatelyEqual = (v1, v2, epsilon = 0.001) => Math.abs(v1 - v2) < epsilon;
 
approximatelyEqual(Math.PI / 2.0, 1.5708); // true
-
intermediate

average

Returns the average of two or more numbers.

Use Array.reduce() to add each value to an accumulator, initialized with a value of 0, divide by the length of the array.

const average = (...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length;
+
beginner

average

Returns the average of two or more numbers.

Use Array.reduce() to add each value to an accumulator, initialized with a value of 0, divide by the length of the array.

const average = (...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length;
 
average(...[1, 2, 3]); // 2
 average(1, 2, 3); // 2
 
intermediate

averageBy

Returns the average of an array, after mapping each element to a value using the provided function.

Use Array.map() to map each element to the value returned by fn, Array.reduce() to add each value to an accumulator, initialized with a value of 0, divide by the length of the array.

const averageBy = (arr, fn) =>
@@ -94,14 +94,14 @@
   return Math.round(res);
 };
 
binomialCoefficient(8, 2); // 28
-
intermediate

clampNumber

Clamps num within the inclusive range specified by the boundary values a and b.

If num falls within the range, return num. Otherwise, return the nearest number in the range.

const clampNumber = (num, a, b) => Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b));
+
beginner

clampNumber

Clamps num within the inclusive range specified by the boundary values a and b.

If num falls within the range, return num. Otherwise, return the nearest number in the range.

const clampNumber = (num, a, b) => Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b));
 
clampNumber(2, 3, 5); // 3
 clampNumber(1, -1, -5); // -1
-
intermediate

degreesToRads

Converts an angle from degrees to radians.

Use Math.PI and the degree to radian formula to convert the angle from degrees to radians.

const degreesToRads = deg => (deg * Math.PI) / 180.0;
+
beginner

degreesToRads

Converts an angle from degrees to radians.

Use Math.PI and the degree to radian formula to convert the angle from degrees to radians.

const degreesToRads = deg => (deg * Math.PI) / 180.0;
 
degreesToRads(90.0); // ~1.5708
-
intermediate

digitize

Converts a number to an array of digits.

Convert the number to a string, using the spread operator (...) to build an array. Use Array.map() and parseInt() to transform each value to an integer.

const digitize = n => [...`${n}`].map(i => parseInt(i));
+
beginner

digitize

Converts a number to an array of digits.

Convert the number to a string, using the spread operator (...) to build an array. Use Array.map() and parseInt() to transform each value to an integer.

const digitize = n => [...`${n}`].map(i => parseInt(i));
 
digitize(123); // [1, 2, 3]
-
intermediate

distance

Returns the distance between two points.

Use Math.hypot() to calculate the Euclidean distance between two points.

const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0);
+
beginner

distance

Returns the distance between two points.

Use Math.hypot() to calculate the Euclidean distance between two points.

const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0);
 
distance(1, 1, 2, 3); // 2.23606797749979
 
advanced

elo

Computes the new ratings between two or more opponents using the Elo rating system. It takes an array of pre-ratings and returns an array containing post-ratings. The array should be ordered from best performer to worst performer (winner -> loser).

Use the exponent ** operator and math operators to compute the expected score (chance of winning). of each opponent and compute the new rating for each. Loop through the ratings, using each permutation to compute the post-Elo rating for each player in a pairwise fashion. Omit the second argument to use the default kFactor of 32.

const elo = ([...ratings], kFactor = 32, selfRating) => {
   const [a, b] = ratings;
@@ -158,9 +158,9 @@ own individual rating by supplying it as the third argument.
 
geometricProgression(256); // [1, 2, 4, 8, 16, 32, 64, 128, 256]
 geometricProgression(256, 3); // [3, 6, 12, 24, 48, 96, 192]
 geometricProgression(256, 1, 4); // [1, 4, 16, 64, 256]
-
intermediate

hammingDistance

Calculates the Hamming distance between two values.

Use XOR operator (^) to find the bit difference between the two numbers, convert to a binary string using toString(2). Count and return the number of 1s in the string, using match(/1/g).

const hammingDistance = (num1, num2) => ((num1 ^ num2).toString(2).match(/1/g) || '').length;
+
beginner

hammingDistance

Calculates the Hamming distance between two values.

Use XOR operator (^) to find the bit difference between the two numbers, convert to a binary string using toString(2). Count and return the number of 1s in the string, using match(/1/g).

const hammingDistance = (num1, num2) => ((num1 ^ num2).toString(2).match(/1/g) || '').length;
 
hammingDistance(2, 3); // 1
-
intermediate

inRange

Checks if the given number falls within the given range.

Use arithmetic comparison to check if the given number is in the specified range. If the second parameter, end, is not specified, the range is considered to be from 0 to start.

const inRange = (n, start, end = null) => {
+
beginner

inRange

Checks if the given number falls within the given range.

Use arithmetic comparison to check if the given number is in the specified range. If the second parameter, end, is not specified, the range is considered to be from 0 to start.

const inRange = (n, start, end = null) => {
   if (end && start > end) [end, start] = [start, end];
   return end == null ? n >= 0 && n < start : n >= start && n < end;
 };
@@ -185,7 +185,7 @@ own individual rating by supplying it as the third argument.
 };
 
lcm(12, 7); // 84
 lcm(...[1, 3, 4, 5]); // 60
-
intermediate

luhnCheck

Implementation of the Luhn Algorithm used to validate a variety of identification numbers, such as credit card numbers, IMEI numbers, National Provider Identifier numbers etc.

Use String.split(''), Array.reverse() and Array.map() in combination with parseInt() to obtain an array of digits. Use Array.splice(0,1) to obtain the last digit. Use Array.reduce() to implement the Luhn Algorithm. Return true if sum is divisible by 10, false otherwise.

const luhnCheck = num => {
+
advanced

luhnCheck

Implementation of the Luhn Algorithm used to validate a variety of identification numbers, such as credit card numbers, IMEI numbers, National Provider Identifier numbers etc.

Use String.split(''), Array.reverse() and Array.map() in combination with parseInt() to obtain an array of digits. Use Array.splice(0,1) to obtain the last digit. Use Array.reduce() to implement the Luhn Algorithm. Return true if sum is divisible by 10, false otherwise.

const luhnCheck = num => {
   let arr = (num + '')
     .split('')
     .reverse()
@@ -198,7 +198,7 @@ own individual rating by supplying it as the third argument.
 
luhnCheck('4485275742308327'); // true
 luhnCheck(6011329933655299); //  false
 luhnCheck(123456789); // false
-
intermediate

maxBy

Returns the maximum value of an array, after mapping each element to a value using the provided function.

Use Array.map() to map each element to the value returned by fn, Math.max() to get the maximum value.

const maxBy = (arr, fn) => Math.max(...arr.map(typeof fn === 'function' ? fn : val => val[fn]));
+
beginner

maxBy

Returns the maximum value of an array, after mapping each element to a value using the provided function.

Use Array.map() to map each element to the value returned by fn, Math.max() to get the maximum value.

const maxBy = (arr, fn) => Math.max(...arr.map(typeof fn === 'function' ? fn : val => val[fn]));
 
maxBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], o => o.n); // 8
 maxBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], 'n'); // 8
 
intermediate

median

Returns the median of an array of numbers.

Find the middle of the array, use Array.sort() to sort the values. Return the number at the midpoint if length is odd, otherwise the average of the two middle numbers.

const median = arr => {
@@ -213,7 +213,7 @@ own individual rating by supplying it as the third argument.
 
intermediate

percentile

Uses the percentile formula to calculate how many numbers in the given array are less or equal to the given value.

Use Array.reduce() to calculate how many numbers are below the value and how many are the same value and apply the percentile formula.

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
-
intermediate

powerset

Returns the powerset of a given array of numbers.

Use Array.reduce() combined with Array.map() to iterate over elements and combine into an array containing all combinations.

const powerset = arr => arr.reduce((a, v) => a.concat(a.map(r => [v].concat(r))), [[]]);
+
beginner

powerset

Returns the powerset of a given array of numbers.

Use Array.reduce() combined with Array.map() to iterate over elements and combine into an array containing all combinations.

const powerset = arr => arr.reduce((a, v) => a.concat(a.map(r => [v].concat(r))), [[]]);
 
powerset([1, 2]); // [[], [1], [2], [2,1]]
 
intermediate

primes

Generates primes up to a given number, using the Sieve of Eratosthenes.

Generate an array from 2 to the given number. Use Array.filter() to filter out the values divisible by any number from 2 to the square root of the provided number.

const primes = num => {
   let arr = Array.from({ length: num - 1 }).map((x, i) => i + 2),
@@ -223,14 +223,14 @@ own individual rating by supplying it as the third argument.
   return arr;
 };
 
primes(10); // [2,3,5,7]
-
intermediate

radsToDegrees

Converts an angle from radians to degrees.

Use Math.PI and the radian to degree formula to convert the angle from radians to degrees.

const radsToDegrees = rad => (rad * 180.0) / Math.PI;
+
beginner

radsToDegrees

Converts an angle from radians to degrees.

Use Math.PI and the radian to degree formula to convert the angle from radians to degrees.

const radsToDegrees = rad => (rad * 180.0) / Math.PI;
 
radsToDegrees(Math.PI / 2); // 90
 
intermediate

randomIntArrayInRange

Returns an array of n random integers in the specified range.

Use Array.from() to create an empty array of the specific length, Math.random() to generate a random number and map it to the desired range, using Math.floor() to make it an integer.

const randomIntArrayInRange = (min, max, n = 1) =>
   Array.from({ length: n }, () => Math.floor(Math.random() * (max - min + 1)) + min);
 
randomIntArrayInRange(12, 35, 10); // [ 34, 14, 27, 17, 30, 27, 20, 26, 21, 14 ]
 
beginner

randomIntegerInRange

Returns a random integer in the specified range.

Use Math.random() to generate a random number and map it to the desired range, using Math.floor() to make it an integer.

const randomIntegerInRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
 
randomIntegerInRange(0, 5); // 2
-
intermediate

randomNumberInRange

Returns a random number in the specified range.

Use Math.random() to generate a random value, map it to the desired range using multiplication.

const randomNumberInRange = (min, max) => Math.random() * (max - min) + min;
+
beginner

randomNumberInRange

Returns a random number in the specified range.

Use Math.random() to generate a random value, map it to the desired range using multiplication.

const randomNumberInRange = (min, max) => Math.random() * (max - min) + min;
 
randomNumberInRange(2, 10); // 6.0211363285087005
 
intermediate

round

Rounds a number to a specified amount of digits.

Use Math.round() and template literals to round the number to the specified number of digits. Omit the second argument, decimals to round to an integer.

const round = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`);
 
round(1.005, 2); // 1.01
@@ -266,7 +266,7 @@ own individual rating by supplying it as the third argument.
 
sumPower(10); // 385
 sumPower(10, 3); //3025
 sumPower(10, 3, 5); //2925
-
intermediate

toSafeInteger

Converts a value to a safe integer.

Use Math.max() and Math.min() to find the closest safe value. Use Math.round() to convert to an integer.

const toSafeInteger = num =>
+
beginner

toSafeInteger

Converts a value to a safe integer.

Use Math.max() and Math.min() to find the closest safe value. Use Math.round() to convert to an integer.

const toSafeInteger = num =>
   Math.round(Math.max(Math.min(num, Number.MAX_SAFE_INTEGER), Number.MIN_SAFE_INTEGER));
 
toSafeInteger('3.2'); // 3
 toSafeInteger(Infinity); // 9007199254740991
diff --git a/docs/node.html b/docs/node.html
index ba8922451..8822f54b6 100644
--- a/docs/node.html
+++ b/docs/node.html
@@ -73,9 +73,9 @@
             },1700);
           }
         }, false);
-      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Node

intermediate

atob

Decodes a string of data which has been encoded using base-64 encoding.

Create a Buffer for the given string with base-64 encoding and use Buffer.toString('binary') to return the decoded string.

const atob = str => new Buffer(str, 'base64').toString('binary');
+      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Node

beginner

atob

Decodes a string of data which has been encoded using base-64 encoding.

Create a Buffer for the given string with base-64 encoding and use Buffer.toString('binary') to return the decoded string.

const atob = str => new Buffer(str, 'base64').toString('binary');
 
atob('Zm9vYmFy'); // 'foobar'
-
intermediate

btoa

Creates a base-64 encoded ASCII string from a String object in which each character in the string is treated as a byte of binary data.

Create a Buffer for the given string with binary encoding and use Buffer.toString('base64') to return the encoded string.

const btoa = str => new Buffer(str, 'binary').toString('base64');
+
beginner

btoa

Creates a base-64 encoded ASCII string from a String object in which each character in the string is treated as a byte of binary data.

Create a Buffer for the given string with binary encoding and use Buffer.toString('base64') to return the encoded string.

const btoa = str => new Buffer(str, 'binary').toString('base64');
 
btoa('foobar'); // 'Zm9vYmFy'
 
intermediate

colorize

Add special characters to text to print in color in the console (combined with console.log()).

Use template literals and special characters to add the appropriate color code to the string output. For background colors, add a special character that resets the background color at the end of the string.

const colorize = (...args) => ({
   black: `\x1b[30m${args.join(' ')}`,
@@ -125,7 +125,7 @@ console.log<
 const JSONToFile = (obj, filename) =>
   fs.writeFile(`${filename}.json`, JSON.stringify(obj, null, 2));
 
JSONToFile({ test: 'is passed' }, 'testJsonFile'); // writes the object to 'testJsonFile.json'
-
intermediate

readFileLines

Returns an array of lines from the specified file.

Use readFileSync function in fs node package to create a Buffer from a file. convert buffer to string using toString(encoding) function. creating an array from contents of file by spliting file content line by line (each \n).

const fs = require('fs');
+
beginner

readFileLines

Returns an array of lines from the specified file.

Use readFileSync function in fs node package to create a Buffer from a file. convert buffer to string using toString(encoding) function. creating an array from contents of file by spliting file content line by line (each \n).

const fs = require('fs');
 const readFileLines = filename =>
   fs
     .readFileSync(filename)
@@ -140,7 +140,7 @@ contents of test.txt :
 */
 let arr = readFileLines('test.txt');
 console.log(arr); // ['line1', 'line2', 'line3']
-
intermediate

untildify

Converts a tilde path to an absolute path.

Use String.replace() with a regular expression and OS.homedir() to replace the ~ in the start of the path with the home directory.

const untildify = str => str.replace(/^~($|\/|\\)/, `${require('os').homedir()}$1`);
+
beginner

untildify

Converts a tilde path to an absolute path.

Use String.replace() with a regular expression and OS.homedir() to replace the ~ in the start of the path with the home directory.

const untildify = str => str.replace(/^~($|\/|\\)/, `${require('os').homedir()}$1`);
 
untildify('~/node'); // '/Users/aUser/node'
 
intermediate

UUIDGeneratorNode

Generates a UUID in Node.JS.

Use crypto API to generate a UUID, compliant with RFC4122 version 4.

const crypto = require('crypto');
 const UUIDGeneratorNode = () =>
diff --git a/docs/object.html b/docs/object.html
index 7f630b3a4..e0ca15295 100644
--- a/docs/object.html
+++ b/docs/object.html
@@ -73,7 +73,7 @@
             },1700);
           }
         }, false);
-      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Object

intermediate

bindAll

Binds methods of an object to the object itself, overwriting the existing method.

Use Array.forEach() to return a function that uses Function.apply() to apply the given context (obj) to fn for each function specified.

const bindAll = (obj, ...fns) =>
+      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Object

intermediate

bindAll

Binds methods of an object to the object itself, overwriting the existing method.

Use Array.forEach() to return a function that uses Function.apply() to apply the given context (obj) to fn for each function specified.

const bindAll = (obj, ...fns) =>
   fns.forEach(
     fn => (
       (f = obj[fn]),
@@ -278,9 +278,9 @@ Foo.prototype: 5, parent_id: 4 }
 ];
 const nestedComments = nest(comments); // [{ id: 1, parent_id: null, children: [...] }]
-
intermediate

objectFromPairs

Creates an object from the given key-value pairs.

Use Array.reduce() to create and combine key-value pairs.

const objectFromPairs = arr => arr.reduce((a, v) => ((a[v[0]] = v[1]), a), {});
+
beginner

objectFromPairs

Creates an object from the given key-value pairs.

Use Array.reduce() to create and combine key-value pairs.

const objectFromPairs = arr => arr.reduce((a, v) => ((a[v[0]] = v[1]), a), {});
 
objectFromPairs([['a', 1], ['b', 2]]); // {a: 1, b: 2}
-
intermediate

objectToPairs

Creates an array of key-value pair arrays from an object.

Use Object.keys() and Array.map() to iterate over the object's keys and produce an array with key-value pairs.

const objectToPairs = obj => Object.keys(obj).map(k => [k, obj[k]]);
+
beginner

objectToPairs

Creates an array of key-value pair arrays from an object.

Use Object.keys() and Array.map() to iterate over the object's keys and produce an array with key-value pairs.

const objectToPairs = obj => Object.keys(obj).map(k => [k, obj[k]]);
 
objectToPairs({ a: 1, b: 2 }); // [['a',1],['b',2]]
 
intermediate

omit

Omits the key-value pairs corresponding to the given keys from an object.

Use Object.keys(obj), Array.filter() and Array.includes() to remove the provided keys. Use Array.reduce() to convert the filtered keys back to an object with the corresponding key-value pairs.

const omit = (obj, arr) =>
   Object.keys(obj)
@@ -323,7 +323,7 @@ Foo.prototypeexamples
const obj = { name: 'Bobo', job: 'Front-End Master', shoeSize: 100 };
 renameKeys({ name: 'firstName', job: 'passion' }, obj); // { firstName: 'Bobo', passion: 'Front-End Master', shoeSize: 100 }
-
intermediate

shallowClone

Creates a shallow clone of an object.

Use Object.assign() and an empty object ({}) to create a shallow clone of the original.

const shallowClone = obj => Object.assign({}, obj);
+
beginner

shallowClone

Creates a shallow clone of an object.

Use Object.assign() and an empty object ({}) to create a shallow clone of the original.

const shallowClone = obj => Object.assign({}, obj);
 
const a = { x: true, y: 1 };
 const b = shallowClone(a); // a !== b
 
intermediate

size

Get size of arrays, objects or strings.

Get type of val (array, object or string). Use length property for arrays. Use length or size value if available or number of keys for objects. Use size of a Blob object created from val for strings.

Split strings into array of characters with split('') and return its length.

const size = val =>
diff --git a/docs/string.html b/docs/string.html
index 7184f01bf..358c4affa 100644
--- a/docs/string.html
+++ b/docs/string.html
@@ -73,7 +73,7 @@
             },1700);
           }
         }, false);
-      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



String

intermediate

byteSize

Returns the length of a string in bytes.

Convert a given string to a Blob Object and find its size.

const byteSize = str => new Blob([str]).size;
+      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



String

beginner

byteSize

Returns the length of a string in bytes.

Convert a given string to a Blob Object and find its size.

const byteSize = str => new Blob([str]).size;
 
byteSize('😀'); // 4
 byteSize('Hello World'); // 11
 
intermediate

capitalize

Capitalizes the first letter of a string.

Use array destructuring and String.toUpperCase() to capitalize first letter, ...rest to get array of characters after first letter and then Array.join('') to make it a string again. Omit the lowerRest parameter to keep the rest of the string intact, or set it to true to convert to lowercase.

const capitalize = ([first, ...rest], lowerRest = false) =>
@@ -144,15 +144,15 @@
   return normalize(str1) === normalize(str2);
 };
 
isAnagram('iceman', 'cinema'); // true
-
intermediate

isLowerCase

Checks if a string is lower case.

Convert the given string to lower case, using String.toLowerCase() and compare it to the original.

const isLowerCase = str => str === str.toLowerCase();
+
beginner

isLowerCase

Checks if a string is lower case.

Convert the given string to lower case, using String.toLowerCase() and compare it to the original.

const isLowerCase = str => str === str.toLowerCase();
 
isLowerCase('abc'); // true
 isLowerCase('a3@$'); // true
 isLowerCase('Ab4'); // false
-
intermediate

isUpperCase

Checks if a string is upper case.

Convert the given string to upper case, using String.toUpperCase() and compare it to the original.

const isUpperCase = str => str === str.toUpperCase();
+
beginner

isUpperCase

Checks if a string is upper case.

Convert the given string to upper case, using String.toUpperCase() and compare it to the original.

const isUpperCase = str => str === str.toUpperCase();
 
isUpperCase('ABC'); // true
 isLowerCase('A3@$'); // true
 isLowerCase('aB4'); // false
-
intermediate

mapString

Creates a new string with the results of calling a provided function on every character in the calling string.

Use String.split('') and Array.map() to call the provided function, fn, for each character in str. Use Array.join('') to recombine the array of characters into a string. The callback function, fn, takes three arguments (the current character, the index of the current character and the string mapString was called upon).

const mapString = (str, fn) =>
+
beginner

mapString

Creates a new string with the results of calling a provided function on every character in the calling string.

Use String.split('') and Array.map() to call the provided function, fn, for each character in str. Use Array.join('') to recombine the array of characters into a string. The callback function, fn, takes three arguments (the current character, the index of the current character and the string mapString was called upon).

const mapString = (str, fn) =>
   str
     .split('')
     .map((c, i) => fn(c, i, str))
@@ -163,7 +163,7 @@
 
mask(1234567890); // '******7890'
 mask(1234567890, 3); // '*******890'
 mask(1234567890, -4, '$'); // '$$$$567890'
-
intermediate

pad

Pads a string on both sides with the specified character, if it's shorter than the specified length.

Use String.padStart() and String.padEnd() to pad both sides of the given string. Omit the third argument, char, to use the whitespace character as the default padding character.

const pad = (str, length, char = ' ') =>
+
beginner

pad

Pads a string on both sides with the specified character, if it's shorter than the specified length.

Use String.padStart() and String.padEnd() to pad both sides of the given string. Omit the third argument, char, to use the whitespace character as the default padding character.

const pad = (str, length, char = ' ') =>
   str.padStart((str.length + length) / 2, char).padEnd(length, char);
 
pad('cat', 8); // '  cat   '
 pad(String(42), 6, '0'); // '004200'
@@ -194,11 +194,11 @@
 
removeNonASCII('äÄçÇéÉêlorem-ipsumöÖÐþúÚ'); // 'lorem-ipsum'
 
beginner

reverseString

Reverses a string.

Use the spread operator (...) and Array.reverse() to reverse the order of the characters in the string. Combine characters to get a string using String.join('').

const reverseString = str => [...str].reverse().join('');
 
reverseString('foobar'); // 'raboof'
-
intermediate

sortCharactersInString

Alphabetically sorts the characters in a string.

Use the spread operator (...), Array.sort() and String.localeCompare() to sort the characters in str, recombine using String.join('').

const sortCharactersInString = str => [...str].sort((a, b) => a.localeCompare(b)).join('');
+
beginner

sortCharactersInString

Alphabetically sorts the characters in a string.

Use the spread operator (...), Array.sort() and String.localeCompare() to sort the characters in str, recombine using String.join('').

const sortCharactersInString = str => [...str].sort((a, b) => a.localeCompare(b)).join('');
 
sortCharactersInString('cabbage'); // 'aabbceg'
-
intermediate

splitLines

Splits a multiline string into an array of lines.

Use String.split() and a regular expression to match line breaks and create an array.

const splitLines = str => str.split(/\r?\n/);
+
beginner

splitLines

Splits a multiline string into an array of lines.

Use String.split() and a regular expression to match line breaks and create an array.

const splitLines = str => str.split(/\r?\n/);
 
splitLines('This\nis a\nmultiline\nstring.\n'); // ['This', 'is a', 'multiline', 'string.' , '']
-
intermediate

stringPermutations

⚠️ WARNING: This function's execution time increases exponentially with each character. Anything more than 8 to 10 characters will cause your browser to hang as it tries to solve all the different combinations.

Generates all permutations of a string (contains duplicates).

Use recursion. For each letter in the given string, create all the partial permutations for the rest of its letters. Use Array.map() to combine the letter with each partial permutation, then Array.reduce() to combine all permutations in one array. Base cases are for string length equal to 2 or 1.

const stringPermutations = str => {
+
advanced

stringPermutations

⚠️ WARNING: This function's execution time increases exponentially with each character. Anything more than 8 to 10 characters will cause your browser to hang as it tries to solve all the different combinations.

Generates all permutations of a string (contains duplicates).

Use recursion. For each letter in the given string, create all the partial permutations for the rest of its letters. Use Array.map() to combine the letter with each partial permutation, then Array.reduce() to combine all permutations in one array. Base cases are for string length equal to 2 or 1.

const stringPermutations = str => {
   if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str];
   return str
     .split('')
@@ -209,7 +209,7 @@
     );
 };
 
stringPermutations('abc'); // ['abc','acb','bac','bca','cab','cba']
-
intermediate

stripHTMLTags

Removes HTML/XML tags from string.

Use a regular expression to remove HTML/XML tags from a string.

const stripHTMLTags = str => str.replace(/<[^>]*>/g, '');
+
beginner

stripHTMLTags

Removes HTML/XML tags from string.

Use a regular expression to remove HTML/XML tags from a string.

const stripHTMLTags = str => str.replace(/<[^>]*>/g, '');
 
stripHTMLTags('<p><em>lorem</em> <strong>ipsum</strong></p>'); // 'lorem ipsum'
 
intermediate

toCamelCase

Converts a string to camelcase.

Break the string into words and combine them capitalizing the first letter of each word, using a regexp.

const toCamelCase = str => {
   let s =
@@ -249,7 +249,7 @@
 
beginner

truncateString

Truncates a string up to a specified length.

Determine if the string's length is greater than num. Return the string truncated to the desired length, with '...' appended to the end or the original string.

const truncateString = (str, num) =>
   str.length > num ? str.slice(0, num > 3 ? num - 3 : num) + '...' : str;
 
truncateString('boomerang', 7); // 'boom...'
-
intermediate

unescapeHTML

Unescapes escaped HTML characters.

Use String.replace() with a regex that matches the characters that need to be unescaped, using a callback function to replace each escaped character instance with its associated unescaped character using a dictionary (object).

const unescapeHTML = str =>
+
beginner

unescapeHTML

Unescapes escaped HTML characters.

Use String.replace() with a regex that matches the characters that need to be unescaped, using a callback function to replace each escaped character instance with its associated unescaped character using a dictionary (object).

const unescapeHTML = str =>
   str.replace(
     /&amp;|&lt;|&gt;|&#39;|&quot;/g,
     tag =>
@@ -262,7 +262,7 @@
       }[tag] || tag)
   );
 
unescapeHTML('&lt;a href=&quot;#&quot;&gt;Me &amp; you&lt;/a&gt;'); // '<a href="#">Me & you</a>'
-
intermediate

URLJoin

Joins all given URL segments together, then normalizes the resulting URL.

Use String.join('/') to combine URL segments, then a series of String.replace() calls with various regexps to normalize the resulting URL (remove double slashes, add proper slashes for protocol, remove slashes before parameters, combine parameters with '&' and normalize first parameter delimiter).

const URLJoin = (...args) =>
+
advanced

URLJoin

Joins all given URL segments together, then normalizes the resulting URL.

Use String.join('/') to combine URL segments, then a series of String.replace() calls with various regexps to normalize the resulting URL (remove double slashes, add proper slashes for protocol, remove slashes before parameters, combine parameters with '&' and normalize first parameter delimiter).

const URLJoin = (...args) =>
   args
     .join('/')
     .replace(/[\/]+/g, '/')
diff --git a/docs/style.css b/docs/style.css
index 625c5bd47..096ce23da 100644
--- a/docs/style.css
+++ b/docs/style.css
@@ -1 +1 @@
-@font-face{font-family:'Roboto';font-style:normal;font-weight:300;src:local("Roboto Light"),local("Roboto-Light"),url(https://fonts.gstatic.com/s/roboto/v18/KFOlCnqEu92Fr1MmSU5fBBc4.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;font-display:swap}@font-face{font-family:'Roboto';font-style:italic;font-weight:300;src:local("Roboto Light Italic"),local("Roboto-LightItalic"),url(https://fonts.gstatic.com/s/roboto/v18/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;font-display:swap}@font-face{font-family:'Roboto';font-style:normal;font-weight:500;src:local("Roboto Medium"),local("Roboto-Medium"),url(https://fonts.gstatic.com/s/roboto/v18/KFOlCnqEu92Fr1MmEU9fBBc4.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;font-display:swap}@font-face{font-family:'Roboto Mono';font-style:normal;font-weight:300;src:local("Roboto Mono Light"),local("RobotoMono-Light"),url(https://fonts.gstatic.com/s/robotomono/v5/L0xkDF4xlVMF-BfR8bXMIjDgiWqxf78.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;font-display:swap}@font-face{font-family:'Roboto Mono';font-style:italic;font-weight:300;src:local("Roboto Mono Light Italic"),local("RobotoMono-LightItalic"),url(https://fonts.gstatic.com/s/robotomono/v5/L0xmDF4xlVMF-BfR8bXMIjhOk9a0T72jBg.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;font-display:swap}@font-face{font-family:'Roboto Mono';font-style:normal;font-weight:500;src:local("Roboto Mono Medium"),local("RobotoMono-Medium"),url(https://fonts.gstatic.com/s/robotomono/v5/L0xkDF4xlVMF-BfR8bXMIjC4iGqxf78.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;font-display:swap}:root{--fore-color:#212121;--back-color:#fff;--card-page-back-color:#eee;--border-color:#eee;--universal-margin:.5rem;--universal-padding:.5rem;--universal-border-radius:.125rem;--a-link-color:#0277bd;--a-visited-color:#01579b;--code-fore-color:#8e24aa;--code-back-color:#f0f0f0;--code-selected-color:#37474f;--pre-fore-color:#e57373;--pre-back-color:#263238;--token-color-a:#7f99a5;--token-color-b:#bdbdbd;--token-color-c:#64b5f6;--token-color-d:#ff8f00;--token-color-e:#c5e1a5;--token-color-f:#ce93d8;--token-color-g:#26c6da;--token-color-h:#e57373;--collapse-color:#607d8b;--copy-button-color:#1e88e5;--copy-button-hover-color:#2196f3;--scrolltop-button-color:#26a69a;--scrolltop-button-hover-color:#4db6ac;--beginner-color:#7cb342;--intermediate-color:#ffb300;--advanced-color:#e53935;--header-fore-color:#fff;--header-back-color:#202124;--nav-fore-color:#f0f0f0;--nav-back-color:#202124;--footer-fore-color:#616161;--footer-back-color:#e0e0e0;--nav-link-fore-color:#e0e0e0;--nav-link-border-color:#455a64;--nav-link-hover-color:#2b2c30;--search-fore-color:#fafafa;--search-back-color:#111;--search-border-color:#9e9e9e;--search-hover-border-color:#26a69a}html{font-size:16px;scroll-behavior:smooth}html,*{font-family:Roboto, Helvetica, sans-serif;line-height:1.5;-webkit-text-size-adjust:100%}*{font-size:1rem;font-weight:300}a,b,del,em,i,ins,q,span,strong,u{font-size:1em}body{margin:0;color:var(--fore-color);background:var(--back-color);overflow-x:hidden}body.card-page{background:var(--card-page-back-color)}details{display:block}summary{display:list-item}abbr[title]{border-bottom:none;text-decoration:underline dotted}input{overflow:visible}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}img{max-width:100%;height:auto}h1,h2,h3,h4,h5,h6{line-height:1.2;margin:calc(1.5 * var(--universal-margin)) var(--universal-margin)}h1{font-size:6rem}h2{font-size:3.75rem}h3{font-size:3rem}h4{font-size:2.125rem}h5{font-size:1.5rem}h6{font-size:1.25rem}p{margin:var(--universal-margin)}ol,ul{margin:var(--universal-margin);padding-left:calc(2 * var(--universal-margin))}b,strong{font-weight:500}hr{box-sizing:content-box;border:0;line-height:1.25em;margin:var(--universal-margin);height:.0625rem;background:linear-gradient(to right, transparent, var(--border-color) 20%, var(--border-color) 80%, transparent)}code,kbd,pre{font-size:.875em}code,kbd,pre,code *,pre *,kbd *,code[class*="language-"],pre[class*="language-"]{font-family:Roboto Mono, Menlo, Consolas, monospace}sup,sub,code,kbd{line-height:0;position:relative;vertical-align:baseline}code{background:var(--code-back-color);color:var(--code-fore-color);padding:calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2);border-radius:var(--universal-border-radius)}pre{overflow:auto;background:var(--pre-back-color);color:var(--pre-fore-color);padding:calc(1.5 * var(--universal-padding));margin:var(--universal-margin);border:0}code[class*="language-"],pre[class*="language-"]{color:var(--pre-fore-color);text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.8;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hypens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*="language-"]{padding:calc(2 * var(--universal-padding));overflow:auto;margin:var(--universal-margin) 0;white-space:pre-wrap}pre[class*="language-"]::-moz-selection,pre[class*="language-"] ::-moz-selection,code[class*="language-"]::-moz-selection,code[class*="language-"] ::-moz-selection{background:var(--code-selected-color)}pre[class*="language-"]::selection,pre[class*="language-"] ::selection,code[class*="language-"]::selection,code[class*="language-"] ::selection{background:var(--code-selected-color)}:not(pre)>code[class*="language-"]{padding:.1em;border-radius:.3em;white-space:normal}.namespace{opacity:.7}.token.comment,.token.prolog,.token.doctype,.token.cdata{color:var(--token-color-a)}.token.punctuation{color:var(--token-color-b)}.token.property,.token.tag,.token.boolean,.token.constant,.token.symbol,.token.deleted,.token.function{color:var(--token-color-c)}.token.number,.token.class-name{color:var(--token-color-d)}.token.selector,.token.attr-name,.token.string,.token.char,.token.builtin,.token.inserted{color:var(--token-color-e)}.token.operator,.token.entity,.token.url,.token.atrule,.token.attr-value,.token.keyword,.token.interpolation-punctuation{color:var(--token-color-f)}.token.regex{color:var(--token-color-g)}.token.important,.token.variable{color:var(--token-color-h)}.token.italic,.token.comment{font-style:italic}.token.important,.token.bold{font-weight:500}.token.entity{cursor:help}.language-css .token.string,.style .token.string{color:var(--token-color-f)}a{text-decoration:none}a:link{color:var(--a-link-color)}a:visited{color:var(--a-visited-color)}a:hover,a:focus{text-decoration:underline}.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width: 500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}.container{display:grid;grid-template-columns:repeat(12, 1fr);grid-column-gap:calc(0.5 * var(--universal-margin))}.container.card-container{position:absolute;padding-top:3.5rem;height:calc(100vh - 3.5rem)}.col-centered{grid-column:span 12;max-width:100%}@media screen and (min-width: 768px){.col-centered{grid-column:2/12}}@media screen and (min-width: 1280px){.col-centered{grid-column:3/11}}.col-quarter{grid-column:span 3}.col-full-width{grid-column:span 12}.flex-row{display:flex;flex:0 1 auto;flex-flow:row wrap}.flex-row .flex-item{flex:0 0 auto;max-width:50%;flex-basis:50%}@media screen and (min-width: 768px){.flex-row .flex-item{max-width:25%;flex-basis:25%}}@media screen and (min-width: 1280px){.flex-row .flex-item{max-width:100%;flex-grow:1;flex-basis:0}}h2.category-name{text-align:center}.card{overflow:hidden;position:relative;margin:var(--universal-margin);border-radius:calc(4 * var(--universal-border-radius));box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2)}.card h4{text-align:center;padding-bottom:calc(0.75 * var(--universal-padding));margin-bottom:calc(2 * var(--universal-margin));border-bottom:.0625rem solid var(--border-color)}.card.code-card{margin-top:calc(5 * var(--universal-margin));background:var(--pre-back-color)}.card.code-card .section.card-content{background:var(--back-color);padding:calc(1.5 * var(--universal-padding))}.card.code-card .collapse{display:block;font-size:0.75rem;font-family:Roboto Mono, Menlo, Consolas, monospace;text-transform:uppercase;background:var(--pre-back-color);color:var(--collapse-color);padding:calc(1.5 * var(--universal-padding)) calc(1.5 * var(--universal-padding)) calc(2 * var(--universal-padding)) calc(2.25 * var(--universal-padding));cursor:pointer;background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23607D8B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-plus-square'%3E%3Crect x='3' y='3' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='12' y1='8' x2='12' y2='16'%3E%3C/line%3E%3Cline x1='8' y1='12' x2='16' y2='12'%3E%3C/line%3E%3C/svg%3E");background-position:0.25rem 0.9375rem;background-repeat:no-repeat}.card.code-card .collapse+pre.card-examples{position:absolute;transform:scaleY(0);transform-origin:top;transition:transform 0.3s ease}.card.code-card .collapse.toggled{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23607D8B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-minus-square'%3E%3Crect x='3' y='3' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='8' y1='12' x2='16' y2='12'%3E%3C/line%3E%3C/svg%3E");padding-bottom:calc(0.125 * var(--universal-padding))}.card.code-card .collapse.toggled+pre.card-examples{position:relative;transform:scaleY(1)}.card.code-card pre.section.card-code{position:relative;margin-bottom:0;padding-bottom:0}.card.code-card pre.section.card-examples{margin-top:0;margin-bottom:0;border-radius:0 0 calc(4 * var(--universal-border-radius)) calc(4 * var(--universal-border-radius));padding-top:0}.card.code-card pre.section.card-examples:before{content:'';display:block;position:absolute;top:0;left:0.5625rem;border-left:.0625rem solid var(--collapse-color);height:calc(100% - 18px)}.card.code-card .copy-button-container{position:relative;z-index:2}.card.code-card .copy-button-container .copy-button{background:var(--copy-button-color);box-sizing:border-box;position:absolute;top:-1.75rem;right:0;margin:calc(2 * var(--universal-margin));padding:calc(2.5 * var(--universal-padding));border-radius:50%;border:0;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);transition:all 0.3s ease;cursor:pointer;background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23f0f0f0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-clipboard'%3E%3Cpath d='M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2'%3E%3C/path%3E%3Crect x='8' y='2' width='8' height='4' rx='1' ry='1'%3E%3C/rect%3E%3C/svg%3E");background-position:center center;background-repeat:no-repeat}.card.code-card .copy-button-container .copy-button:hover,.card.code-card .copy-button-container .copy-button:focus{background-color:var(--copy-button-hover-color);box-shadow:0 3px 3px 0 rgba(0,0,0,0.14),0 1px 7px 0 rgba(0,0,0,0.12),0 3px 1px -1px rgba(0,0,0,0.2)}.card.code-card .copy-button-container .copy-button:before{background:var(--back-color);position:absolute;top:-0.25rem;right:-0.25rem;content:'';display:block;box-sizing:border-box;padding:calc(2.5 * var(--universal-padding));border-radius:50%;border:0.25rem solid var(--back-color);z-index:-1}.card.code-card .corner{box-sizing:border-box;position:absolute;top:-0.5rem;right:-2.125rem;width:6.5rem;height:3.25rem;padding-top:2rem;transform:rotate(45deg);text-align:center;font-size:0.75rem;font-weight:500;color:var(--back-color);box-shadow:0 2px 2px 0 rgba(0,0,0,0.07),0 3px 1px -2px rgba(0,0,0,0.06),0 1px 5px 0 rgba(0,0,0,0.1)}.card.code-card .corner.beginner{background:var(--beginner-color)}.card.code-card .corner.intermediate{background:var(--intermediate-color)}.card.code-card .corner.advanced{background:var(--advanced-color)}.toast{position:fixed;bottom:calc(var(--universal-margin) * 2);margin-bottom:0;font-size:0.8125rem;left:50%;transform:translate(-50%, -50%);z-index:1111;color:var(--back-color);background:var(--fore-color);border-radius:calc(var(--universal-border-radius) * 16);padding:var(--universal-padding) calc(var(--universal-padding) * 2);box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);transition:all 0.3s ease}header{box-sizing:border-box;overflow:hidden;height:3.5rem;position:fixed;width:110%;top:0;left:-5%;box-shadow:0 2px 4px rgba(0,0,0,0.5);z-index:5;background:var(--header-back-color);transition:top 0.3s ease}header h1.logo{position:relative;top:0;margin-top:0;font-size:1.625rem;text-align:center}header h1 a,header h1 a:link,header h1 a:visited{color:var(--header-fore-color)}header h1 a:hover,header h1 a:focus,header h1 a:link:hover,header h1 a:link:focus,header h1 a:visited:hover,header h1 a:visited:focus{text-decoration:none}header h1 small{display:block;font-size:0.875rem;color:var(--header-back-color);margin-top:0.75rem}header img{height:3.5rem;padding:0.375rem;box-sizing:border-box}header #title{position:relative;top:-1.125rem}@media screen and (max-width: 768px){header #title{display:none}}nav{position:fixed;top:3.5rem;left:-320px;width:320px;transition:left 0.3s ease;z-index:1100;height:calc(100vh - 3.5rem);box-sizing:border-box;display:block;background:var(--nav-back-color);border:0;overflow-y:auto}@media screen and (max-width: 320px){nav{width:100%}}@media screen and (min-width: 768px){nav{width:33vw;left:-33vw}}@media screen and (min-width: 1280px){nav{width:25vw;left:-25vw}}nav.col-nav{box-shadow:0 8px 17px 2px rgba(0,0,0,0.14),0 3px 14px 2px rgba(0,0,0,0.12),0 5px 5px -3px rgba(0,0,0,0.2);left:0}@media screen and (min-width: 768px){nav.col-nav+main.col-centered,nav.col-nav+main.col-centered+footer.col-full-width{grid-column:5/13}}@media screen and (min-width: 1280px){nav.col-nav+main.col-centered{grid-column:4/12;padding-left:8vw}nav.col-nav+main.col-centered+footer.col-full-width{grid-column:4/13}}nav h4{margin:var(--universal-margin);padding:var(--universal-padding) calc(1.5 * var(--universal-padding));padding-left:0;color:var(--nav-fore-color)}nav a{display:block;margin:calc(0.5 * var(--universal-margin));margin-left:var(--universal-margin);margin-bottom:0;padding:calc(2 * var(--universal-padding)) calc(1.5 * var(--universal-padding));border-left:.0625rem solid var(--nav-link-border-color)}nav a:hover{text-decoration:none;background:var(--nav-link-hover-color)}nav a+a{margin-top:0}nav a:link,nav a:visited{color:var(--nav-link-fore-color)}[type="search"]{color:var(--search-fore-color);background:var(--search-back-color);outline:none;box-sizing:border-box;border:none;border-bottom:.0625rem solid var(--search-border-color);width:100%;margin-bottom:var(--universal-margin);padding:calc(2 * var(--universal-padding)) calc(1.5 * var(--universal-padding)) var(--universal-padding) calc(1.5 * var(--universal-padding));transition:all 0.3s ease}[type="search"]:hover,[type="search"]:focus{border-bottom:.0625rem solid var(--search-hover-border-color)}[type="search"]:focus{box-shadow:0 .0625rem 0 0 var(--search-hover-border-color)}.menu-button{position:fixed;top:0;left:0;z-index:1000;box-sizing:border-box;height:3.5rem;width:3.5rem;border:0;background:transparent;background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23fafafa' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-more-horizontal'%3E%3Ccircle cx='12' cy='12' r='1'%3E%3C/circle%3E%3Ccircle cx='19' cy='12' r='1'%3E%3C/circle%3E%3Ccircle cx='5' cy='12' r='1'%3E%3C/circle%3E%3C/svg%3E");background-repeat:no-repeat;background-position:0.875rem 0.875rem;cursor:pointer;transition:all 0.3s ease}.menu-button:hover{background-color:rgba(255,255,255,0.08)}.menu-button.toggled{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23fafafa' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-more-vertical'%3E%3Ccircle cx='12' cy='12' r='1'%3E%3C/circle%3E%3Ccircle cx='12' cy='5' r='1'%3E%3C/circle%3E%3Ccircle cx='12' cy='19' r='1'%3E%3C/circle%3E%3C/svg%3E")}footer{color:var(--footer-fore-color);background:var(--footer-back-color);padding-top:calc(2 * var(--universal-padding));padding-bottom:calc(3 * var(--universal-padding));margin-top:calc(6 * var(--universal-margin))}footer *{font-size:0.875rem}footer a,footer a:link,footer a:visited{color:var(--fore-color)}.scroll-to-top{position:fixed;bottom:1rem;right:1.3125rem;box-sizing:border-box;z-index:1100;height:2.75rem;width:2.75rem;border:0;border-radius:100%;background:var(--scrolltop-button-color);background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23f0f0f0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-arrow-up'%3E%3Cline x1='12' y1='19' x2='12' y2='5'%3E%3C/line%3E%3Cpolyline points='5 12 12 5 19 12'%3E%3C/polyline%3E%3C/svg%3E");background-repeat:no-repeat;background-position:center center;cursor:pointer;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);transition:all 0.3s ease}.scroll-to-top:hover,.scroll-to-top:focus{background-color:var(--scrolltop-button-hover-color);box-shadow:0 3px 3px 0 rgba(0,0,0,0.14),0 1px 7px 0 rgba(0,0,0,0.12),0 3px 1px -1px rgba(0,0,0,0.2)}.card.contributor>.section.button{font-size:1rem;font-weight:500;text-align:center;display:block;transition:all 0.3s ease}.card.contributor>.section.button:link,.card.contributor>.section.button:visited{color:var(--fore-color)}.card.contributor>.section.button:link:hover,.card.contributor>.section.button:visited:hover{color:var(--a-link-color);text-decoration:none}
+@font-face{font-family:'Roboto';font-style:normal;font-weight:300;src:local("Roboto Light"),local("Roboto-Light"),url(https://fonts.gstatic.com/s/roboto/v18/KFOlCnqEu92Fr1MmSU5fBBc4.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;font-display:swap}@font-face{font-family:'Roboto';font-style:italic;font-weight:300;src:local("Roboto Light Italic"),local("Roboto-LightItalic"),url(https://fonts.gstatic.com/s/roboto/v18/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;font-display:swap}@font-face{font-family:'Roboto';font-style:normal;font-weight:500;src:local("Roboto Medium"),local("Roboto-Medium"),url(https://fonts.gstatic.com/s/roboto/v18/KFOlCnqEu92Fr1MmEU9fBBc4.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;font-display:swap}@font-face{font-family:'Roboto Mono';font-style:normal;font-weight:300;src:local("Roboto Mono Light"),local("RobotoMono-Light"),url(https://fonts.gstatic.com/s/robotomono/v5/L0xkDF4xlVMF-BfR8bXMIjDgiWqxf78.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;font-display:swap}@font-face{font-family:'Roboto Mono';font-style:italic;font-weight:300;src:local("Roboto Mono Light Italic"),local("RobotoMono-LightItalic"),url(https://fonts.gstatic.com/s/robotomono/v5/L0xmDF4xlVMF-BfR8bXMIjhOk9a0T72jBg.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;font-display:swap}@font-face{font-family:'Roboto Mono';font-style:normal;font-weight:500;src:local("Roboto Mono Medium"),local("RobotoMono-Medium"),url(https://fonts.gstatic.com/s/robotomono/v5/L0xkDF4xlVMF-BfR8bXMIjC4iGqxf78.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;font-display:swap}:root{--fore-color:#212121;--back-color:#fff;--card-page-back-color:#eee;--border-color:#eee;--universal-margin:.5rem;--universal-padding:.5rem;--universal-border-radius:.125rem;--a-link-color:#0277bd;--a-visited-color:#01579b;--code-fore-color:#8e24aa;--code-back-color:#f0f0f0;--code-selected-color:#37474f;--pre-fore-color:#e57373;--pre-back-color:#263238;--token-color-a:#7f99a5;--token-color-b:#bdbdbd;--token-color-c:#64b5f6;--token-color-d:#ff8f00;--token-color-e:#c5e1a5;--token-color-f:#ce93d8;--token-color-g:#26c6da;--token-color-h:#e57373;--collapse-color:#607d8b;--copy-button-color:#1e88e5;--copy-button-hover-color:#2196f3;--scrolltop-button-color:#26a69a;--scrolltop-button-hover-color:#4db6ac;--beginner-color:#7cb342;--intermediate-color:#ffb300;--advanced-color:#e53935;--header-fore-color:#fff;--header-back-color:#202124;--nav-fore-color:#f0f0f0;--nav-back-color:#202124;--footer-fore-color:#616161;--footer-back-color:#e0e0e0;--nav-link-fore-color:#e0e0e0;--nav-link-border-color:#455a64;--nav-link-hover-color:#2b2c30;--search-fore-color:#fafafa;--search-back-color:#111;--search-border-color:#9e9e9e;--search-hover-border-color:#26a69a}html{font-size:16px;scroll-behavior:smooth}html,*{font-family:Roboto, Helvetica, sans-serif;line-height:1.5;-webkit-text-size-adjust:100%}*{font-size:1rem;font-weight:300}a,b,del,em,i,ins,q,span,strong,u{font-size:1em}body{margin:0;color:var(--fore-color);background:var(--back-color);overflow-x:hidden}body.card-page{background:var(--card-page-back-color)}details{display:block}summary{display:list-item}abbr[title]{border-bottom:none;text-decoration:underline dotted}input{overflow:visible}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}img{max-width:100%;height:auto}h1,h2,h3,h4,h5,h6{line-height:1.2;margin:calc(1.5 * var(--universal-margin)) var(--universal-margin)}h1{font-size:6rem}h2{font-size:3.75rem}h3{font-size:3rem}h4{font-size:2.125rem}h5{font-size:1.5rem}h6{font-size:1.25rem}p{margin:var(--universal-margin)}ol,ul{margin:var(--universal-margin);padding-left:calc(2 * var(--universal-margin))}b,strong{font-weight:500}hr{box-sizing:content-box;border:0;line-height:1.25em;margin:var(--universal-margin);height:.0625rem;background:linear-gradient(to right, transparent, var(--border-color) 20%, var(--border-color) 80%, transparent)}code,kbd,pre{font-size:.875em}code,kbd,pre,code *,pre *,kbd *,code[class*="language-"],pre[class*="language-"]{font-family:Roboto Mono, Menlo, Consolas, monospace}sup,sub,code,kbd{line-height:0;position:relative;vertical-align:baseline}code{background:var(--code-back-color);color:var(--code-fore-color);padding:calc(var(--universal-padding) / 4) calc(var(--universal-padding) / 2);border-radius:var(--universal-border-radius)}pre{overflow:auto;background:var(--pre-back-color);color:var(--pre-fore-color);padding:calc(1.5 * var(--universal-padding));margin:var(--universal-margin);border:0}code[class*="language-"],pre[class*="language-"]{color:var(--pre-fore-color);text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.8;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-hypens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*="language-"]{padding:calc(2 * var(--universal-padding));overflow:auto;margin:var(--universal-margin) 0;white-space:pre-wrap}pre[class*="language-"]::-moz-selection,pre[class*="language-"] ::-moz-selection,code[class*="language-"]::-moz-selection,code[class*="language-"] ::-moz-selection{background:var(--code-selected-color)}pre[class*="language-"]::selection,pre[class*="language-"] ::selection,code[class*="language-"]::selection,code[class*="language-"] ::selection{background:var(--code-selected-color)}:not(pre)>code[class*="language-"]{padding:.1em;border-radius:.3em;white-space:normal}.namespace{opacity:.7}.token.comment,.token.prolog,.token.doctype,.token.cdata{color:var(--token-color-a)}.token.punctuation{color:var(--token-color-b)}.token.property,.token.tag,.token.boolean,.token.constant,.token.symbol,.token.deleted,.token.function{color:var(--token-color-c)}.token.number,.token.class-name{color:var(--token-color-d)}.token.selector,.token.attr-name,.token.string,.token.char,.token.builtin,.token.inserted{color:var(--token-color-e)}.token.operator,.token.entity,.token.url,.token.atrule,.token.attr-value,.token.keyword,.token.interpolation-punctuation{color:var(--token-color-f)}.token.regex{color:var(--token-color-g)}.token.important,.token.variable{color:var(--token-color-h)}.token.italic,.token.comment{font-style:italic}.token.important,.token.bold{font-weight:500}.token.entity{cursor:help}.language-css .token.string,.style .token.string{color:var(--token-color-f)}a{text-decoration:none}a:link{color:var(--a-link-color)}a:visited{color:var(--a-visited-color)}a:hover,a:focus{text-decoration:underline}.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width: 500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}.container{display:grid;grid-template-columns:repeat(12, 1fr);grid-column-gap:calc(0.5 * var(--universal-margin))}.container.card-container{position:absolute;padding-top:3.5rem;height:calc(100vh - 3.5rem)}.col-centered{grid-column:span 12;max-width:100%}@media screen and (min-width: 768px){.col-centered{grid-column:2/12}}@media screen and (min-width: 1280px){.col-centered{grid-column:3/11}}.col-quarter{grid-column:span 3}.col-full-width{grid-column:span 12}.flex-row{display:flex;flex:0 1 auto;flex-flow:row wrap}.flex-row .flex-item{flex:0 0 auto;max-width:50%;flex-basis:50%}@media screen and (min-width: 768px){.flex-row .flex-item{max-width:25%;flex-basis:25%}}@media screen and (min-width: 1280px){.flex-row .flex-item{max-width:100%;flex-grow:1;flex-basis:0}}h2.category-name{text-align:center}.card{overflow:hidden;position:relative;margin:var(--universal-margin);border-radius:calc(4 * var(--universal-border-radius));box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2)}.card h4{text-align:center;padding-bottom:calc(0.75 * var(--universal-padding));margin-bottom:calc(2 * var(--universal-margin));border-bottom:.0625rem solid var(--border-color)}.card.code-card{margin-top:calc(5 * var(--universal-margin));background:var(--pre-back-color)}.card.code-card .section.card-content{background:var(--back-color);padding:calc(1.5 * var(--universal-padding))}.card.code-card .collapse{display:block;font-size:0.75rem;font-family:Roboto Mono, Menlo, Consolas, monospace;text-transform:uppercase;background:var(--pre-back-color);color:var(--collapse-color);padding:calc(1.5 * var(--universal-padding)) calc(1.5 * var(--universal-padding)) calc(2 * var(--universal-padding)) calc(2.25 * var(--universal-padding));cursor:pointer;background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23607D8B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-plus-square'%3E%3Crect x='3' y='3' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='12' y1='8' x2='12' y2='16'%3E%3C/line%3E%3Cline x1='8' y1='12' x2='16' y2='12'%3E%3C/line%3E%3C/svg%3E");background-position:0.25rem 0.9375rem;background-repeat:no-repeat}.card.code-card .collapse+pre.card-examples{position:absolute;transform:scaleY(0);transform-origin:top;transition:transform 0.3s ease}.card.code-card .collapse.toggled{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23607D8B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-minus-square'%3E%3Crect x='3' y='3' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='8' y1='12' x2='16' y2='12'%3E%3C/line%3E%3C/svg%3E");padding-bottom:calc(0.125 * var(--universal-padding))}.card.code-card .collapse.toggled+pre.card-examples{position:relative;transform:scaleY(1)}.card.code-card pre.section.card-code{position:relative;margin-bottom:0;padding-bottom:0;padding-top:calc(3 * var(--universal-padding))}.card.code-card pre.section.card-examples{margin-top:0;margin-bottom:0;border-radius:0 0 calc(4 * var(--universal-border-radius)) calc(4 * var(--universal-border-radius));padding-top:0}.card.code-card pre.section.card-examples:before{content:'';display:block;position:absolute;top:0;left:0.5625rem;border-left:.0625rem solid var(--collapse-color);height:calc(100% - 18px)}.card.code-card .copy-button-container{position:relative;z-index:2}.card.code-card .copy-button-container .copy-button{background:var(--copy-button-color);box-sizing:border-box;position:absolute;top:-1.75rem;right:0;margin:calc(2 * var(--universal-margin));padding:calc(2.5 * var(--universal-padding));border-radius:50%;border:0;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);transition:all 0.3s ease;cursor:pointer;background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23f0f0f0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-clipboard'%3E%3Cpath d='M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2'%3E%3C/path%3E%3Crect x='8' y='2' width='8' height='4' rx='1' ry='1'%3E%3C/rect%3E%3C/svg%3E");background-position:center center;background-repeat:no-repeat}.card.code-card .copy-button-container .copy-button:hover,.card.code-card .copy-button-container .copy-button:focus{background-color:var(--copy-button-hover-color);box-shadow:0 3px 3px 0 rgba(0,0,0,0.14),0 1px 7px 0 rgba(0,0,0,0.12),0 3px 1px -1px rgba(0,0,0,0.2)}.card.code-card .copy-button-container .copy-button:before{background:var(--back-color);position:absolute;top:-0.25rem;right:-0.25rem;content:'';display:block;box-sizing:border-box;padding:calc(2.5 * var(--universal-padding));border-radius:50%;border:0.25rem solid var(--back-color);z-index:-1}.card.code-card .corner{box-sizing:border-box;position:absolute;top:-0.5rem;right:-2.125rem;width:6.5rem;height:3.25rem;padding-top:2rem;transform:rotate(45deg);text-align:center;font-size:0.75rem;font-weight:500;color:var(--back-color);box-shadow:0 2px 2px 0 rgba(0,0,0,0.07),0 3px 1px -2px rgba(0,0,0,0.06),0 1px 5px 0 rgba(0,0,0,0.1)}.card.code-card .corner.beginner{background:var(--beginner-color)}.card.code-card .corner.intermediate{background:var(--intermediate-color)}.card.code-card .corner.advanced{background:var(--advanced-color)}.toast{position:fixed;bottom:calc(var(--universal-margin) * 2);margin-bottom:0;font-size:0.8125rem;left:50%;transform:translate(-50%, -50%);z-index:1111;color:var(--back-color);background:var(--fore-color);border-radius:calc(var(--universal-border-radius) * 16);padding:var(--universal-padding) calc(var(--universal-padding) * 2);box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);transition:all 0.3s ease}header{box-sizing:border-box;overflow:hidden;height:3.5rem;position:fixed;width:110%;top:0;left:-5%;box-shadow:0 2px 4px rgba(0,0,0,0.5);z-index:5;background:var(--header-back-color);transition:top 0.3s ease}header h1.logo{position:relative;top:0;margin-top:0;font-size:1.625rem;text-align:center}header h1 a,header h1 a:link,header h1 a:visited{color:var(--header-fore-color)}header h1 a:hover,header h1 a:focus,header h1 a:link:hover,header h1 a:link:focus,header h1 a:visited:hover,header h1 a:visited:focus{text-decoration:none}header h1 small{display:block;font-size:0.875rem;color:var(--header-back-color);margin-top:0.75rem}header img{height:3.5rem;padding:0.375rem;box-sizing:border-box}header #title{position:relative;top:-1.125rem}@media screen and (max-width: 768px){header #title{display:none}}nav{position:fixed;top:3.5rem;left:-320px;width:320px;transition:left 0.3s ease;z-index:1100;height:calc(100vh - 3.5rem);box-sizing:border-box;display:block;background:var(--nav-back-color);border:0;overflow-y:auto}@media screen and (max-width: 320px){nav{width:100%}}@media screen and (min-width: 768px){nav{width:33vw;left:-33vw}}@media screen and (min-width: 1280px){nav{width:25vw;left:-25vw}}nav.col-nav{box-shadow:0 8px 17px 2px rgba(0,0,0,0.14),0 3px 14px 2px rgba(0,0,0,0.12),0 5px 5px -3px rgba(0,0,0,0.2);left:0}@media screen and (min-width: 768px){nav.col-nav+main.col-centered,nav.col-nav+main.col-centered+footer.col-full-width{grid-column:5/13}}@media screen and (min-width: 1280px){nav.col-nav+main.col-centered{grid-column:4/12;padding-left:8vw}nav.col-nav+main.col-centered+footer.col-full-width{grid-column:4/13}}nav h4{margin:var(--universal-margin);padding:var(--universal-padding) calc(1.5 * var(--universal-padding));padding-left:0;color:var(--nav-fore-color)}nav a{display:block;margin:calc(0.5 * var(--universal-margin));margin-left:var(--universal-margin);margin-bottom:0;padding:calc(2 * var(--universal-padding)) calc(1.5 * var(--universal-padding));border-left:.0625rem solid var(--nav-link-border-color)}nav a:hover{text-decoration:none;background:var(--nav-link-hover-color)}nav a+a{margin-top:0}nav a:link,nav a:visited{color:var(--nav-link-fore-color)}[type="search"]{color:var(--search-fore-color);background:var(--search-back-color);outline:none;box-sizing:border-box;border:none;border-bottom:.0625rem solid var(--search-border-color);width:100%;margin-bottom:var(--universal-margin);padding:calc(2 * var(--universal-padding)) calc(1.5 * var(--universal-padding)) var(--universal-padding) calc(1.5 * var(--universal-padding));transition:all 0.3s ease}[type="search"]:hover,[type="search"]:focus{border-bottom:.0625rem solid var(--search-hover-border-color)}[type="search"]:focus{box-shadow:0 .0625rem 0 0 var(--search-hover-border-color)}.menu-button{position:fixed;top:0;left:0;z-index:1000;box-sizing:border-box;height:3.5rem;width:3.5rem;border:0;background:transparent;background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23fafafa' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-more-horizontal'%3E%3Ccircle cx='12' cy='12' r='1'%3E%3C/circle%3E%3Ccircle cx='19' cy='12' r='1'%3E%3C/circle%3E%3Ccircle cx='5' cy='12' r='1'%3E%3C/circle%3E%3C/svg%3E");background-repeat:no-repeat;background-position:0.875rem 0.875rem;cursor:pointer;transition:all 0.3s ease}.menu-button:hover{background-color:rgba(255,255,255,0.08)}.menu-button.toggled{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23fafafa' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-more-vertical'%3E%3Ccircle cx='12' cy='12' r='1'%3E%3C/circle%3E%3Ccircle cx='12' cy='5' r='1'%3E%3C/circle%3E%3Ccircle cx='12' cy='19' r='1'%3E%3C/circle%3E%3C/svg%3E")}footer{color:var(--footer-fore-color);background:var(--footer-back-color);padding-top:calc(2 * var(--universal-padding));padding-bottom:calc(3 * var(--universal-padding));margin-top:calc(6 * var(--universal-margin))}footer *{font-size:0.875rem}footer a,footer a:link,footer a:visited{color:var(--fore-color)}.scroll-to-top{position:fixed;bottom:1rem;right:1.3125rem;box-sizing:border-box;z-index:1100;height:2.75rem;width:2.75rem;border:0;border-radius:100%;background:var(--scrolltop-button-color);background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23f0f0f0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-arrow-up'%3E%3Cline x1='12' y1='19' x2='12' y2='5'%3E%3C/line%3E%3Cpolyline points='5 12 12 5 19 12'%3E%3C/polyline%3E%3C/svg%3E");background-repeat:no-repeat;background-position:center center;cursor:pointer;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);transition:all 0.3s ease}.scroll-to-top:hover,.scroll-to-top:focus{background-color:var(--scrolltop-button-hover-color);box-shadow:0 3px 3px 0 rgba(0,0,0,0.14),0 1px 7px 0 rgba(0,0,0,0.12),0 3px 1px -1px rgba(0,0,0,0.2)}.card.contributor>.section.button{font-size:1rem;font-weight:500;text-align:center;display:block;transition:all 0.3s ease}.card.contributor>.section.button:link,.card.contributor>.section.button:visited{color:var(--fore-color)}.card.contributor>.section.button:link:hover,.card.contributor>.section.button:visited:hover{color:var(--a-link-color);text-decoration:none}
diff --git a/docs/type.html b/docs/type.html
index 3c3d2efdf..43d5ece6a 100644
--- a/docs/type.html
+++ b/docs/type.html
@@ -73,10 +73,10 @@
             },1700);
           }
         }, false);
-      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Type

intermediate

getType

Returns the native type of a value.

Returns lowercased constructor name of value, "undefined" or "null" if value is undefined or null.

const getType = v =>
+      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Type

beginner

getType

Returns the native type of a value.

Returns lowercased constructor name of value, "undefined" or "null" if value is undefined or null.

const getType = v =>
   v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name.toLowerCase();
 
getType(new Set([1, 2, 3])); // 'set'
-
intermediate

is

Checks if the provided value is of the specified type.

Ensure the value is not undefined or null using Array.includes(), and compare the constructor property on the value with type to check if the provided value is of the specified type.

const is = (type, val) => ![, null].includes(val) && val.constructor === type;
+
beginner

is

Checks if the provided value is of the specified type.

Ensure the value is not undefined or null using Array.includes(), and compare the constructor property on the value with type to check if the provided value is of the specified type.

const is = (type, val) => ![, null].includes(val) && val.constructor === type;
 
is(Array, [1]); // true
 is(ArrayBuffer, new ArrayBuffer()); // true
 is(Map, new Map()); // true
@@ -94,10 +94,10 @@
 
isArrayLike(document.querySelectorAll('.className')); // true
 isArrayLike('abc'); // true
 isArrayLike(null); // false
-
intermediate

isBoolean

Checks if the given argument is a native boolean element.

Use typeof to check if a value is classified as a boolean primitive.

const isBoolean = val => typeof val === 'boolean';
+
beginner

isBoolean

Checks if the given argument is a native boolean element.

Use typeof to check if a value is classified as a boolean primitive.

const isBoolean = val => typeof val === 'boolean';
 
isBoolean(null); // false
 isBoolean(false); // true
-
intermediate

isEmpty

Returns true if the a value is an empty object, collection, map or set, has no enumerable properties or is any type that is not considered a collection.

Check if the provided value is null or if its length is equal to 0.

const isEmpty = val => val == null || !(Object.keys(val) || val).length;
+
beginner

isEmpty

Returns true if the a value is an empty object, collection, map or set, has no enumerable properties or is any type that is not considered a collection.

Check if the provided value is null or if its length is equal to 0.

const isEmpty = val => val == null || !(Object.keys(val) || val).length;
 
isEmpty(new Map()); // true
 isEmpty(new Set()); // true
 isEmpty([]); // true
@@ -108,25 +108,25 @@
 isEmpty('text'); // false
 isEmpty(123); // true - type is not considered a collection
 isEmpty(true); // true - type is not considered a collection
-
intermediate

isFunction

Checks if the given argument is a function.

Use typeof to check if a value is classified as a function primitive.

const isFunction = val => typeof val === 'function';
+
beginner

isFunction

Checks if the given argument is a function.

Use typeof to check if a value is classified as a function primitive.

const isFunction = val => typeof val === 'function';
 
isFunction('x'); // false
 isFunction(x => x); // true
-
intermediate

isNil

Returns true if the specified value is null or undefined, false otherwise.

Use the strict equality operator to check if the value and of val are equal to null or undefined.

const isNil = val => val === undefined || val === null;
+
beginner

isNil

Returns true if the specified value is null or undefined, false otherwise.

Use the strict equality operator to check if the value and of val are equal to null or undefined.

const isNil = val => val === undefined || val === null;
 
isNil(null); // true
 isNil(undefined); // true
-
intermediate

isNull

Returns true if the specified value is null, false otherwise.

Use the strict equality operator to check if the value and of val are equal to null.

const isNull = val => val === null;
+
beginner

isNull

Returns true if the specified value is null, false otherwise.

Use the strict equality operator to check if the value and of val are equal to null.

const isNull = val => val === null;
 
isNull(null); // true
-
intermediate

isNumber

Checks if the given argument is a number.

Use typeof to check if a value is classified as a number primitive.

const isNumber = val => typeof val === 'number';
+
beginner

isNumber

Checks if the given argument is a number.

Use typeof to check if a value is classified as a number primitive.

const isNumber = val => typeof val === 'number';
 
isNumber('1'); // false
 isNumber(1); // true
-
intermediate

isObject

Returns a boolean determining if the passed value is an object or not.

Uses the Object constructor to create an object wrapper for the given value. If the value is null or undefined, create and return an empty object. Οtherwise, return an object of a type that corresponds to the given value.

const isObject = obj => obj === Object(obj);
+
beginner

isObject

Returns a boolean determining if the passed value is an object or not.

Uses the Object constructor to create an object wrapper for the given value. If the value is null or undefined, create and return an empty object. Οtherwise, return an object of a type that corresponds to the given value.

const isObject = obj => obj === Object(obj);
 
isObject([1, 2, 3, 4]); // true
 isObject([]); // true
 isObject(['Hello!']); // true
 isObject({ a: 1 }); // true
 isObject({}); // true
 isObject(true); // false
-
intermediate

isObjectLike

Checks if a value is object-like.

Check if the provided value is not null and its typeof is equal to 'object'.

const isObjectLike = val => val !== null && typeof val === 'object';
+
beginner

isObjectLike

Checks if a value is object-like.

Check if the provided value is not null and its typeof is equal to 'object'.

const isObjectLike = val => val !== null && typeof val === 'object';
 
isObjectLike({}); // true
 isObjectLike([1, 2, 3]); // true
 isObjectLike(x => x); // false
@@ -152,11 +152,11 @@
 }); // true
 isPromiseLike(null); // false
 isPromiseLike({}); // false
-
intermediate

isString

Checks if the given argument is a string.

Use typeof to check if a value is classified as a string primitive.

const isString = val => typeof val === 'string';
+
beginner

isString

Checks if the given argument is a string.

Use typeof to check if a value is classified as a string primitive.

const isString = val => typeof val === 'string';
 
isString('10'); // true
-
intermediate

isSymbol

Checks if the given argument is a symbol.

Use typeof to check if a value is classified as a symbol primitive.

const isSymbol = val => typeof val === 'symbol';
+
beginner

isSymbol

Checks if the given argument is a symbol.

Use typeof to check if a value is classified as a symbol primitive.

const isSymbol = val => typeof val === 'symbol';
 
isSymbol(Symbol('x')); // true
-
intermediate

isUndefined

Returns true if the specified value is undefined, false otherwise.

Use the strict equality operator to check if the value and of val are equal to undefined.

const isUndefined = val => val === undefined;
+
beginner

isUndefined

Returns true if the specified value is undefined, false otherwise.

Use the strict equality operator to check if the value and of val are equal to undefined.

const isUndefined = val => val === undefined;
 
isUndefined(undefined); // true
 
intermediate

isValidJSON

Checks if the provided argument is a valid JSON.

Use JSON.parse() and a try... catch block to check if the provided argument is a valid JSON.

const isValidJSON = obj => {
   try {
diff --git a/docs/utility.html b/docs/utility.html
index e94c33cff..5db21f79b 100644
--- a/docs/utility.html
+++ b/docs/utility.html
@@ -73,13 +73,13 @@
             },1700);
           }
         }, false);
-      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Utility

intermediate

castArray

Casts the provided value as an array if it's not one.

Use Array.isArray() to determine if val is an array and return it as-is or encapsulated in an array accordingly.

const castArray = val => (Array.isArray(val) ? val : [val]);
+      }

logo 30 seconds of code Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.



Utility

beginner

castArray

Casts the provided value as an array if it's not one.

Use Array.isArray() to determine if val is an array and return it as-is or encapsulated in an array accordingly.

const castArray = val => (Array.isArray(val) ? val : [val]);
 
castArray('foo'); // ['foo']
 castArray([1]); // [1]
 
intermediate

cloneRegExp

Clones a regular expression.

Use new RegExp(), RegExp.source and RegExp.flags to clone the given regular expression.

const cloneRegExp = regExp => new RegExp(regExp.source, regExp.flags);
 
const regExp = /lorem ipsum/gi;
 const regExp2 = cloneRegExp(regExp); // /lorem ipsum/gi
-
intermediate

coalesce

Returns the first non-null/undefined argument.

Use Array.find() to return the first non null/undefined argument.

const coalesce = (...args) => args.find(_ => ![undefined, null].includes(_));
+
beginner

coalesce

Returns the first non-null/undefined argument.

Use Array.find() to return the first non null/undefined argument.

const coalesce = (...args) => args.find(_ => ![undefined, null].includes(_));
 
coalesce(null, undefined, '', NaN, 'Waldo'); // ""
 
intermediate

coalesceFactory

Returns a customized coalesce function that returns the first argument that returns true from the provided argument validation function.

Use Array.find() to return the first argument that returns true from the provided argument validation function.

const coalesceFactory = valid => (...args) => args.find(valid);
 
const customCoalesce = coalesceFactory(_ => ![null, undefined, '', NaN].includes(_));
@@ -197,7 +197,7 @@ Logs: {
     [1, '2', 3, 4, 5, 6, 7, 8, 9, 10].every(el => typeof el === 'number');
   }
 ]); // 1
-
intermediate

nthArg

Creates a function that gets the argument at index n. If n is negative, the nth argument from the end is returned.

Use Array.slice() to get the desired argument at index n.

const nthArg = n => (...args) => args.slice(n)[0];
+
beginner

nthArg

Creates a function that gets the argument at index n. If n is negative, the nth argument from the end is returned.

Use Array.slice() to get the desired argument at index n.

const nthArg = n => (...args) => args.slice(n)[0];
 
const third = nthArg(2);
 third(1, 2, 3); // 3
 third(1, 2); // undefined
@@ -212,7 +212,7 @@ Logs: {
       return acc;
     }, {});
 
parseCookie('foo=bar; equation=E%3Dmc%5E2'); // { foo: 'bar', equation: 'E=mc^2' }
-
intermediate

prettyBytes

Converts a number in bytes to a human-readable string.

Use an array dictionary of units to be accessed based on the exponent. Use Number.toPrecision() to truncate the number to a certain number of digits. Return the prettified string by building it up, taking into account the supplied options and whether it is negative or not. Omit the second argument, precision, to use a default precision of 3 digits. Omit the third argument, addSpace, to add space between the number and unit by default.

const prettyBytes = (num, precision = 3, addSpace = true) => {
+
advanced

prettyBytes

Converts a number in bytes to a human-readable string.

Use an array dictionary of units to be accessed based on the exponent. Use Number.toPrecision() to truncate the number to a certain number of digits. Return the prettified string by building it up, taking into account the supplied options and whether it is negative or not. Omit the second argument, precision, to use a default precision of 3 digits. Omit the third argument, addSpace, to add space between the number and unit by default.

const prettyBytes = (num, precision = 3, addSpace = true) => {
   const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
   if (Math.abs(num) < 1) return num + (addSpace ? ' ' : '') + UNITS[0];
   const exponent = Math.min(Math.floor(Math.log10(num < 0 ? -num : num) / 3), UNITS.length - 1);
@@ -222,7 +222,7 @@ Logs: {
 
prettyBytes(1000); // "1 KB"
 prettyBytes(-27145424323.5821, 5); // "-27.145 GB"
 prettyBytes(123456789, 3, false); // "123MB"
-
intermediate

randomHexColorCode

Generates a random hexadecimal color code.

Use Math.random to generate a random 24-bit(6x4bits) hexadecimal number. Use bit shifting and then convert it to an hexadecimal String using toString(16).

const randomHexColorCode = () => {
+
beginner

randomHexColorCode

Generates a random hexadecimal color code.

Use Math.random to generate a random 24-bit(6x4bits) hexadecimal number. Use bit shifting and then convert it to an hexadecimal String using toString(16).

const randomHexColorCode = () => {
   let n = (Math.random() * 0xfffff * 1000000).toString(16);
   return '#' + n.slice(0, 6);
 };
@@ -231,7 +231,7 @@ Logs: {
 
RGBToHex(255, 165, 1); // 'ffa501'
 
intermediate

serializeCookie

Serialize a cookie name-value pair into a Set-Cookie header string.

Use template literals and encodeURIComponent() to create the appropriate string.

const serializeCookie = (name, val) => `${encodeURIComponent(name)}=${encodeURIComponent(val)}`;
 
serializeCookie('foo', 'bar'); // 'foo=bar'
-
intermediate

timeTaken

Measures the time taken by a function to execute.

Use console.time() and console.timeEnd() to measure the difference between the start and end times to determine how long the callback took to execute.

const timeTaken = callback => {
+
beginner

timeTaken

Measures the time taken by a function to execute.

Use console.time() and console.timeEnd() to measure the difference between the start and end times to determine how long the callback took to execute.

const timeTaken = callback => {
   console.time('timeTaken');
   const r = callback();
   console.timeEnd('timeTaken');
@@ -245,7 +245,7 @@ Logs: {
 toCurrency(123456.789, 'USD', 'fa'); // ۱۲۳٬۴۵۶٫۷۹ ؜$ | currency: US Dollar | currencyLangFormat: Farsi
 toCurrency(322342436423.2435, 'JPY'); // ¥322,342,436,423 | currency: Japanese Yen | currencyLangFormat: Local
 toCurrency(322342436423.2435, 'JPY', 'fi'); // 322 342 436 423 ¥ | currency: Japanese Yen | currencyLangFormat: Finnish
-
intermediate

toDecimalMark

Use toLocaleString() to convert a float-point arithmetic to the Decimal mark form. It makes a comma separated string from a number.

const toDecimalMark = num => num.toLocaleString('en-US');
+
beginner

toDecimalMark

Use toLocaleString() to convert a float-point arithmetic to the Decimal mark form. It makes a comma separated string from a number.

const toDecimalMark = num => num.toLocaleString('en-US');
 
toDecimalMark(12305030388.9087); // "12,305,030,388.909"
 
intermediate

toOrdinalSuffix

Adds an ordinal suffix to a number.

Use the modulo operator (%) to find values of single and tens digits. Find which ordinal pattern digits match. If digit is found in teens pattern, use teens ordinal.

const toOrdinalSuffix = num => {
   const int = parseInt(num),