UNPKG

243 kBMarkdownView Raw
1[![Logo](/logo.png)](https://30secondsofcode.org/)
2
3# 30 seconds of code
4
5[![License](https://img.shields.io/badge/license-CC0--1.0-blue.svg)](https://github.com/30-seconds/30-seconds-of-code/blob/master/LICENSE) [![npm Downloads](https://img.shields.io/npm/dt/30-seconds-of-code.svg)](https://www.npmjs.com/package/30-seconds-of-code) [![npm Version](https://img.shields.io/npm/v/30-seconds-of-code.svg)](https://www.npmjs.com/package/30-seconds-of-code) [![Known Vulnerabilities](https://snyk.io/test/github/30-seconds/30-seconds-of-code/badge.svg?targetFile=package.json)](https://snyk.io/test/github/30-seconds/30-seconds-of-code?targetFile=package.json) <br/>
6[![Travis Build](https://travis-ci.com/30-seconds/30-seconds-of-code.svg?branch=master)](https://travis-ci.com/30-seconds/30-seconds-of-code) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/6ab7791fb1ea40b4a576d658fb96807f)](https://www.codacy.com/app/Chalarangelo/30-seconds-of-code?utm_source=github.com&utm_medium=referral&utm_content=30-seconds/30-seconds-of-code&utm_campaign=Badge_Grade) [![Maintainability](https://api.codeclimate.com/v1/badges/4b8c1e099135f2d53413/maintainability)](https://codeclimate.com/github/30-seconds/30-seconds-of-code/maintainability) [![js-semistandard-style](https://img.shields.io/badge/code%20style-semistandard-brightgreen.svg)](https://github.com/Flet/semistandard) <br/>
7[![Awesome](https://awesome.re/badge.svg)](https://awesome.re) [![ProductHunt](https://img.shields.io/badge/producthunt-vote-orange.svg)](https://www.producthunt.com/posts/30-seconds-of-code) [![Gitter chat](https://img.shields.io/badge/chat-on%20gitter-4FB999.svg)](https://gitter.im/30-seconds-of-code/Lobby) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)
8
9> Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less.
10
11[![Sponsored by DigitalOcean](/sponsored_by_DigitalOcean.png)](https://www.digitalocean.com)
12
13* Use <kbd>Ctrl</kbd> + <kbd>F</kbd> or <kbd>command</kbd> + <kbd>F</kbd> to search for a snippet.
14* Contributions welcome, please read the [contribution guide](CONTRIBUTING.md).
15* Snippets are written in ES6, use the [Babel transpiler](https://babeljs.io/) to ensure backwards-compatibility.
16* You can import these snippets into VSCode, by following the instructions found [here](https://github.com/30-seconds/30-seconds-of-code/tree/master/vscode_snippets).
17* You can search, view and copy these snippets from a terminal, using the CLI application from [this repo](https://github.com/sQVe/30s).
18* If you want to follow 30-seconds-of-code on social media, you can find us on [Facebook](https://www.facebook.com/30secondsofcode), [Instagram](https://www.instagram.com/30secondsofcode) and [Twitter](https://twitter.com/30secondsofcode).
19
20#### Related projects
21
22* [30 Seconds of CSS](https://30-seconds.github.io/30-seconds-of-css/)
23* [30 Seconds of Interviews](https://30secondsofinterviews.org/)
24* [30 Seconds of React](https://github.com/30-seconds/30-seconds-of-react)
25* [30 Seconds of Python](https://github.com/kriadmin/30-seconds-of-python-code) _(unofficial)_
26* [30 Seconds of PHP](https://github.com/appzcoder/30-seconds-of-php-code) _(unofficial)_
27
28#### Package
29
30⚠️ **NOTICE:** A few of our snippets are not yet optimized for production (see disclaimers for individual snippet issues).
31
32You can find a package with all the snippets on [npm](https://www.npmjs.com/package/30-seconds-of-code).
33
34```bash
35# With npm
36npm install 30-seconds-of-code
37
38# With yarn
39yarn add 30-seconds-of-code
40```
41
42[CDN link](https://unpkg.com/30-seconds-of-code/)
43
44<details>
45<summary>Details</summary>
46
47**Browser**
48
49```html
50<script src="https://unpkg.com/30-seconds-of-code@1/dist/_30s.es5.min.js"></script>
51<script>
52 _30s.average(1, 2, 3);
53</script>
54```
55
56**Node**
57
58```js
59// CommonJS
60const _30s = require('30-seconds-of-code');
61_30s.average(1, 2, 3);
62
63// ES Modules
64import _30s from '30-seconds-of-code';
65_30s.average(1, 2, 3);
66```
67
68</details>
69
70## Contents
71
72### 🔌 Adapter
73
74<details>
75<summary>View contents</summary>
76
77* [`ary`](#ary)
78* [`call`](#call)
79* [`collectInto`](#collectinto)
80* [`flip`](#flip)
81* [`over`](#over)
82* [`overArgs`](#overargs)
83* [`pipeAsyncFunctions`](#pipeasyncfunctions)
84* [`pipeFunctions`](#pipefunctions)
85* [`promisify`](#promisify)
86* [`rearg`](#rearg)
87* [`spreadOver`](#spreadover)
88* [`unary`](#unary)
89
90</details>
91
92### 📚 Array
93
94<details>
95<summary>View contents</summary>
96
97* [`all`](#all)
98* [`allEqual`](#allequal)
99* [`any`](#any)
100* [`arrayToCSV`](#arraytocsv)
101* [`bifurcate`](#bifurcate)
102* [`bifurcateBy`](#bifurcateby)
103* [`chunk`](#chunk)
104* [`compact`](#compact)
105* [`countBy`](#countby)
106* [`countOccurrences`](#countoccurrences)
107* [`deepFlatten`](#deepflatten)
108* [`difference`](#difference)
109* [`differenceBy`](#differenceby)
110* [`differenceWith`](#differencewith)
111* [`drop`](#drop)
112* [`dropRight`](#dropright)
113* [`dropRightWhile`](#droprightwhile)
114* [`dropWhile`](#dropwhile)
115* [`everyNth`](#everynth)
116* [`filterNonUnique`](#filternonunique)
117* [`filterNonUniqueBy`](#filternonuniqueby)
118* [`findLast`](#findlast)
119* [`findLastIndex`](#findlastindex)
120* [`flatten`](#flatten)
121* [`forEachRight`](#foreachright)
122* [`groupBy`](#groupby)
123* [`head`](#head)
124* [`indexOfAll`](#indexofall)
125* [`initial`](#initial)
126* [`initialize2DArray`](#initialize2darray)
127* [`initializeArrayWithRange`](#initializearraywithrange)
128* [`initializeArrayWithRangeRight`](#initializearraywithrangeright)
129* [`initializeArrayWithValues`](#initializearraywithvalues)
130* [`initializeNDArray`](#initializendarray)
131* [`intersection`](#intersection)
132* [`intersectionBy`](#intersectionby)
133* [`intersectionWith`](#intersectionwith)
134* [`isSorted`](#issorted)
135* [`join`](#join)
136* [`JSONtoCSV`](#jsontocsv-)
137* [`last`](#last)
138* [`longestItem`](#longestitem)
139* [`mapObject`](#mapobject-)
140* [`maxN`](#maxn)
141* [`minN`](#minn)
142* [`none`](#none)
143* [`nthElement`](#nthelement)
144* [`offset`](#offset)
145* [`partition`](#partition)
146* [`permutations`](#permutations-)
147* [`pull`](#pull)
148* [`pullAtIndex`](#pullatindex-)
149* [`pullAtValue`](#pullatvalue-)
150* [`pullBy`](#pullby-)
151* [`reducedFilter`](#reducedfilter)
152* [`reduceSuccessive`](#reducesuccessive)
153* [`reduceWhich`](#reducewhich)
154* [`reject`](#reject)
155* [`remove`](#remove)
156* [`sample`](#sample)
157* [`sampleSize`](#samplesize)
158* [`shank`](#shank)
159* [`shuffle`](#shuffle)
160* [`similarity`](#similarity)
161* [`sortedIndex`](#sortedindex)
162* [`sortedIndexBy`](#sortedindexby)
163* [`sortedLastIndex`](#sortedlastindex)
164* [`sortedLastIndexBy`](#sortedlastindexby)
165* [`stableSort`](#stablesort-)
166* [`symmetricDifference`](#symmetricdifference)
167* [`symmetricDifferenceBy`](#symmetricdifferenceby)
168* [`symmetricDifferenceWith`](#symmetricdifferencewith)
169* [`tail`](#tail)
170* [`take`](#take)
171* [`takeRight`](#takeright)
172* [`takeRightWhile`](#takerightwhile)
173* [`takeWhile`](#takewhile)
174* [`toHash`](#tohash)
175* [`union`](#union)
176* [`unionBy`](#unionby)
177* [`unionWith`](#unionwith)
178* [`uniqueElements`](#uniqueelements)
179* [`uniqueElementsBy`](#uniqueelementsby)
180* [`uniqueElementsByRight`](#uniqueelementsbyright)
181* [`uniqueSymmetricDifference`](#uniquesymmetricdifference)
182* [`unzip`](#unzip)
183* [`unzipWith`](#unzipwith-)
184* [`without`](#without)
185* [`xProd`](#xprod)
186* [`zip`](#zip)
187* [`zipObject`](#zipobject)
188* [`zipWith`](#zipwith-)
189
190</details>
191
192### 🌐 Browser
193
194<details>
195<summary>View contents</summary>
196
197* [`arrayToHtmlList`](#arraytohtmllist)
198* [`bottomVisible`](#bottomvisible)
199* [`copyToClipboard`](#copytoclipboard-)
200* [`counter`](#counter-)
201* [`createElement`](#createelement)
202* [`createEventHub`](#createeventhub-)
203* [`currentURL`](#currenturl)
204* [`detectDeviceType`](#detectdevicetype)
205* [`elementContains`](#elementcontains)
206* [`elementIsVisibleInViewport`](#elementisvisibleinviewport-)
207* [`getImages`](#getimages)
208* [`getScrollPosition`](#getscrollposition)
209* [`getStyle`](#getstyle)
210* [`hasClass`](#hasclass)
211* [`hashBrowser`](#hashbrowser-)
212* [`hide`](#hide)
213* [`httpsRedirect`](#httpsredirect)
214* [`insertAfter`](#insertafter)
215* [`insertBefore`](#insertbefore)
216* [`isBrowserTabFocused`](#isbrowsertabfocused)
217* [`nodeListToArray`](#nodelisttoarray)
218* [`observeMutations`](#observemutations-)
219* [`off`](#off)
220* [`on`](#on)
221* [`onUserInputChange`](#onuserinputchange-)
222* [`prefix`](#prefix)
223* [`recordAnimationFrames`](#recordanimationframes)
224* [`redirect`](#redirect)
225* [`runAsync`](#runasync-)
226* [`scrollToTop`](#scrolltotop)
227* [`setStyle`](#setstyle)
228* [`show`](#show)
229* [`smoothScroll`](#smoothscroll)
230* [`toggleClass`](#toggleclass)
231* [`triggerEvent`](#triggerevent)
232* [`UUIDGeneratorBrowser`](#uuidgeneratorbrowser)
233
234</details>
235
236### ⏱️ Date
237
238<details>
239<summary>View contents</summary>
240
241* [`dayOfYear`](#dayofyear)
242* [`formatDuration`](#formatduration)
243* [`getColonTimeFromDate`](#getcolontimefromdate)
244* [`getDaysDiffBetweenDates`](#getdaysdiffbetweendates)
245* [`getMeridiemSuffixOfInteger`](#getmeridiemsuffixofinteger)
246* [`isAfterDate`](#isafterdate)
247* [`isBeforeDate`](#isbeforedate)
248* [`isSameDate`](#issamedate)
249* [`maxDate`](#maxdate)
250* [`minDate`](#mindate)
251* [`tomorrow`](#tomorrow)
252
253</details>
254
255### 🎛️ Function
256
257<details>
258<summary>View contents</summary>
259
260* [`attempt`](#attempt)
261* [`bind`](#bind)
262* [`bindKey`](#bindkey)
263* [`chainAsync`](#chainasync)
264* [`compose`](#compose)
265* [`composeRight`](#composeright)
266* [`converge`](#converge)
267* [`curry`](#curry)
268* [`debounce`](#debounce)
269* [`defer`](#defer)
270* [`delay`](#delay)
271* [`functionName`](#functionname)
272* [`hz`](#hz)
273* [`memoize`](#memoize-)
274* [`negate`](#negate)
275* [`once`](#once)
276* [`partial`](#partial)
277* [`partialRight`](#partialright)
278* [`runPromisesInSeries`](#runpromisesinseries)
279* [`sleep`](#sleep)
280* [`throttle`](#throttle-)
281* [`times`](#times)
282* [`uncurry`](#uncurry)
283* [`unfold`](#unfold)
284* [`when`](#when)
285
286</details>
287
288### ➗ Math
289
290<details>
291<summary>View contents</summary>
292
293* [`approximatelyEqual`](#approximatelyequal)
294* [`average`](#average)
295* [`averageBy`](#averageby)
296* [`binomialCoefficient`](#binomialcoefficient)
297* [`clampNumber`](#clampnumber)
298* [`degreesToRads`](#degreestorads)
299* [`digitize`](#digitize)
300* [`distance`](#distance)
301* [`elo`](#elo-)
302* [`factorial`](#factorial)
303* [`fibonacci`](#fibonacci)
304* [`gcd`](#gcd)
305* [`geometricProgression`](#geometricprogression)
306* [`hammingDistance`](#hammingdistance)
307* [`inRange`](#inrange)
308* [`isDivisible`](#isdivisible)
309* [`isEven`](#iseven)
310* [`isNegativeZero`](#isnegativezero)
311* [`isPrime`](#isprime)
312* [`lcm`](#lcm)
313* [`luhnCheck`](#luhncheck-)
314* [`maxBy`](#maxby)
315* [`median`](#median)
316* [`minBy`](#minby)
317* [`percentile`](#percentile)
318* [`powerset`](#powerset)
319* [`primes`](#primes)
320* [`radsToDegrees`](#radstodegrees)
321* [`randomIntArrayInRange`](#randomintarrayinrange)
322* [`randomIntegerInRange`](#randomintegerinrange)
323* [`randomNumberInRange`](#randomnumberinrange)
324* [`round`](#round)
325* [`sdbm`](#sdbm)
326* [`standardDeviation`](#standarddeviation)
327* [`sum`](#sum)
328* [`sumBy`](#sumby)
329* [`sumPower`](#sumpower)
330* [`toSafeInteger`](#tosafeinteger)
331
332</details>
333
334### 📦 Node
335
336<details>
337<summary>View contents</summary>
338
339* [`atob`](#atob)
340* [`btoa`](#btoa)
341* [`colorize`](#colorize)
342* [`hasFlags`](#hasflags)
343* [`hashNode`](#hashnode)
344* [`isDuplexStream`](#isduplexstream)
345* [`isReadableStream`](#isreadablestream)
346* [`isStream`](#isstream)
347* [`isTravisCI`](#istravisci)
348* [`isWritableStream`](#iswritablestream)
349* [`JSONToFile`](#jsontofile)
350* [`readFileLines`](#readfilelines)
351* [`untildify`](#untildify)
352* [`UUIDGeneratorNode`](#uuidgeneratornode)
353
354</details>
355
356### 🗃️ Object
357
358<details>
359<summary>View contents</summary>
360
361* [`bindAll`](#bindall)
362* [`deepClone`](#deepclone)
363* [`deepFreeze`](#deepfreeze)
364* [`defaults`](#defaults)
365* [`dig`](#dig)
366* [`equals`](#equals-)
367* [`findKey`](#findkey)
368* [`findLastKey`](#findlastkey)
369* [`flattenObject`](#flattenobject)
370* [`forOwn`](#forown)
371* [`forOwnRight`](#forownright)
372* [`functions`](#functions)
373* [`get`](#get)
374* [`invertKeyValues`](#invertkeyvalues)
375* [`lowercaseKeys`](#lowercasekeys)
376* [`mapKeys`](#mapkeys)
377* [`mapValues`](#mapvalues)
378* [`matches`](#matches)
379* [`matchesWith`](#matcheswith)
380* [`merge`](#merge)
381* [`nest`](#nest)
382* [`objectFromPairs`](#objectfrompairs)
383* [`objectToPairs`](#objecttopairs)
384* [`omit`](#omit)
385* [`omitBy`](#omitby)
386* [`orderBy`](#orderby)
387* [`pick`](#pick)
388* [`pickBy`](#pickby)
389* [`renameKeys`](#renamekeys)
390* [`shallowClone`](#shallowclone)
391* [`size`](#size)
392* [`transform`](#transform)
393* [`truthCheckCollection`](#truthcheckcollection)
394* [`unflattenObject`](#unflattenobject-)
395
396</details>
397
398### 📜 String
399
400<details>
401<summary>View contents</summary>
402
403* [`byteSize`](#bytesize)
404* [`capitalize`](#capitalize)
405* [`capitalizeEveryWord`](#capitalizeeveryword)
406* [`CSVToArray`](#csvtoarray)
407* [`CSVToJSON`](#csvtojson-)
408* [`decapitalize`](#decapitalize)
409* [`escapeHTML`](#escapehtml)
410* [`escapeRegExp`](#escaperegexp)
411* [`fromCamelCase`](#fromcamelcase)
412* [`indentString`](#indentstring)
413* [`isAbsoluteURL`](#isabsoluteurl)
414* [`isAnagram`](#isanagram)
415* [`isLowerCase`](#islowercase)
416* [`isUpperCase`](#isuppercase)
417* [`mapString`](#mapstring)
418* [`mask`](#mask)
419* [`pad`](#pad)
420* [`palindrome`](#palindrome)
421* [`pluralize`](#pluralize)
422* [`removeNonASCII`](#removenonascii)
423* [`reverseString`](#reversestring)
424* [`sortCharactersInString`](#sortcharactersinstring)
425* [`splitLines`](#splitlines)
426* [`stringPermutations`](#stringpermutations-)
427* [`stripHTMLTags`](#striphtmltags)
428* [`toCamelCase`](#tocamelcase)
429* [`toKebabCase`](#tokebabcase)
430* [`toSnakeCase`](#tosnakecase)
431* [`toTitleCase`](#totitlecase)
432* [`truncateString`](#truncatestring)
433* [`unescapeHTML`](#unescapehtml)
434* [`URLJoin`](#urljoin-)
435* [`words`](#words)
436
437</details>
438
439### 📃 Type
440
441<details>
442<summary>View contents</summary>
443
444* [`getType`](#gettype)
445* [`is`](#is)
446* [`isArrayLike`](#isarraylike)
447* [`isBoolean`](#isboolean)
448* [`isEmpty`](#isempty)
449* [`isFunction`](#isfunction)
450* [`isNil`](#isnil)
451* [`isNull`](#isnull)
452* [`isNumber`](#isnumber)
453* [`isObject`](#isobject)
454* [`isObjectLike`](#isobjectlike)
455* [`isPlainObject`](#isplainobject)
456* [`isPrimitive`](#isprimitive)
457* [`isPromiseLike`](#ispromiselike)
458* [`isString`](#isstring)
459* [`isSymbol`](#issymbol)
460* [`isUndefined`](#isundefined)
461* [`isValidJSON`](#isvalidjson)
462
463</details>
464
465### 🔧 Utility
466
467<details>
468<summary>View contents</summary>
469
470* [`castArray`](#castarray)
471* [`cloneRegExp`](#cloneregexp)
472* [`coalesce`](#coalesce)
473* [`coalesceFactory`](#coalescefactory)
474* [`extendHex`](#extendhex)
475* [`getURLParameters`](#geturlparameters)
476* [`hexToRGB`](#hextorgb-)
477* [`httpGet`](#httpget)
478* [`httpPost`](#httppost)
479* [`isBrowser`](#isbrowser)
480* [`mostPerformant`](#mostperformant)
481* [`nthArg`](#ntharg)
482* [`parseCookie`](#parsecookie)
483* [`prettyBytes`](#prettybytes-)
484* [`randomHexColorCode`](#randomhexcolorcode)
485* [`RGBToHex`](#rgbtohex)
486* [`serializeCookie`](#serializecookie)
487* [`timeTaken`](#timetaken)
488* [`toCurrency`](#tocurrency)
489* [`toDecimalMark`](#todecimalmark)
490* [`toOrdinalSuffix`](#toordinalsuffix)
491* [`validateNumber`](#validatenumber)
492* [`yesNo`](#yesno)
493
494</details>
495
496
497---
498
499## 🔌 Adapter
500
501### ary
502
503Creates a function that accepts up to `n` arguments, ignoring any additional arguments.
504
505Call the provided function, `fn`, with up to `n` arguments, using `Array.prototype.slice(0,n)` and the spread operator (`...`).
506
507```js
508const ary = (fn, n) => (...args) => fn(...args.slice(0, n));
509```
510
511<details>
512<summary>Examples</summary>
513
514```js
515const firstTwoMax = ary(Math.max, 2);
516[[2, 6, 'a'], [8, 4, 6], [10]].map(x => firstTwoMax(...x)); // [6, 8, 10]
517```
518
519</details>
520
521<br>[⬆ Back to top](#contents)
522
523### call
524
525Given a key and a set of arguments, call them when given a context. Primarily useful in composition.
526
527Use a closure to call a stored key with stored arguments.
528
529```js
530const call = (key, ...args) => context => context[key](...args);
531```
532
533<details>
534<summary>Examples</summary>
535
536```js
537Promise.resolve([1, 2, 3])
538 .then(call('map', x => 2 * x))
539 .then(console.log); // [ 2, 4, 6 ]
540const map = call.bind(null, 'map');
541Promise.resolve([1, 2, 3])
542 .then(map(x => 2 * x))
543 .then(console.log); // [ 2, 4, 6 ]
544```
545
546</details>
547
548<br>[⬆ Back to top](#contents)
549
550### collectInto
551
552Changes a function that accepts an array into a variadic function.
553
554Given a function, return a closure that collects all inputs into an array-accepting function.
555
556```js
557const collectInto = fn => (...args) => fn(args);
558```
559
560<details>
561<summary>Examples</summary>
562
563```js
564const Pall = collectInto(Promise.all.bind(Promise));
565let p1 = Promise.resolve(1);
566let p2 = Promise.resolve(2);
567let p3 = new Promise(resolve => setTimeout(resolve, 2000, 3));
568Pall(p1, p2, p3).then(console.log); // [1, 2, 3] (after about 2 seconds)
569```
570
571</details>
572
573<br>[⬆ Back to top](#contents)
574
575### flip
576
577Flip takes a function as an argument, then makes the first argument the last.
578
579Return a closure that takes variadic inputs, and splices the last argument to make it the first argument before applying the rest.
580
581```js
582const flip = fn => (first, ...rest) => fn(...rest, first);
583```
584
585<details>
586<summary>Examples</summary>
587
588```js
589let a = { name: 'John Smith' };
590let b = {};
591const mergeFrom = flip(Object.assign);
592let mergePerson = mergeFrom.bind(null, a);
593mergePerson(b); // == b
594b = {};
595Object.assign(b, a); // == b
596```
597
598</details>
599
600<br>[⬆ Back to top](#contents)
601
602### over
603
604Creates a function that invokes each provided function with the arguments it receives and returns the results.
605
606Use `Array.prototype.map()` and `Function.prototype.apply()` to apply each function to the given arguments.
607
608```js
609const over = (...fns) => (...args) => fns.map(fn => fn.apply(null, args));
610```
611
612<details>
613<summary>Examples</summary>
614
615```js
616const minMax = over(Math.min, Math.max);
617minMax(1, 2, 3, 4, 5); // [1,5]
618```
619
620</details>
621
622<br>[⬆ Back to top](#contents)
623
624### overArgs
625
626Creates a function that invokes the provided function with its arguments transformed.
627
628Use `Array.prototype.map()` to apply `transforms` to `args` in combination with the spread operator (`...`) to pass the transformed arguments to `fn`.
629
630```js
631const overArgs = (fn, transforms) => (...args) => fn(...args.map((val, i) => transforms[i](val)));
632```
633
634<details>
635<summary>Examples</summary>
636
637```js
638const square = n => n * n;
639const double = n => n * 2;
640const fn = overArgs((x, y) => [x, y], [square, double]);
641fn(9, 3); // [81, 6]
642```
643
644</details>
645
646<br>[⬆ Back to top](#contents)
647
648### pipeAsyncFunctions
649
650Performs left-to-right function composition for asynchronous functions.
651
652Use `Array.prototype.reduce()` with the spread operator (`...`) to perform left-to-right function composition using `Promise.then()`.
653The functions can return a combination of: simple values, `Promise`'s, or they can be defined as `async` ones returning through `await`.
654All functions must be unary.
655
656```js
657const pipeAsyncFunctions = (...fns) => arg => fns.reduce((p, f) => p.then(f), Promise.resolve(arg));
658```
659
660<details>
661<summary>Examples</summary>
662
663```js
664const sum = pipeAsyncFunctions(
665 x => x + 1,
666 x => new Promise(resolve => setTimeout(() => resolve(x + 2), 1000)),
667 x => x + 3,
668 async x => (await x) + 4
669);
670(async() => {
671 console.log(await sum(5)); // 15 (after one second)
672})();
673```
674
675</details>
676
677<br>[⬆ Back to top](#contents)
678
679### pipeFunctions
680
681Performs left-to-right function composition.
682
683Use `Array.prototype.reduce()` with the spread operator (`...`) to perform left-to-right function composition.
684The first (leftmost) function can accept one or more arguments; the remaining functions must be unary.
685
686```js
687const pipeFunctions = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)));
688```
689
690<details>
691<summary>Examples</summary>
692
693```js
694const add5 = x => x + 5;
695const multiply = (x, y) => x * y;
696const multiplyAndAdd5 = pipeFunctions(multiply, add5);
697multiplyAndAdd5(5, 2); // 15
698```
699
700</details>
701
702<br>[⬆ Back to top](#contents)
703
704### promisify
705
706Converts an asynchronous function to return a promise.
707
708Use currying to return a function returning a `Promise` that calls the original function.
709Use the `...rest` operator to pass in all the parameters.
710
711*In Node 8+, you can use [`util.promisify`](https://nodejs.org/api/util.html#util_util_promisify_original)*
712
713```js
714const promisify = func => (...args) =>
715 new Promise((resolve, reject) =>
716 func(...args, (err, result) => (err ? reject(err) : resolve(result)))
717 );
718```
719
720<details>
721<summary>Examples</summary>
722
723```js
724const delay = promisify((d, cb) => setTimeout(cb, d));
725delay(2000).then(() => console.log('Hi!')); // // Promise resolves after 2s
726```
727
728</details>
729
730<br>[⬆ Back to top](#contents)
731
732### rearg
733
734Creates a function that invokes the provided function with its arguments arranged according to the specified indexes.
735
736Use `Array.prototype.map()` to reorder arguments based on `indexes` in combination with the spread operator (`...`) to pass the transformed arguments to `fn`.
737
738```js
739const rearg = (fn, indexes) => (...args) => fn(...indexes.map(i => args[i]));
740```
741
742<details>
743<summary>Examples</summary>
744
745```js
746var rearged = rearg(
747 function(a, b, c) {
748 return [a, b, c];
749 },
750 [2, 0, 1]
751);
752rearged('b', 'c', 'a'); // ['a', 'b', 'c']
753```
754
755</details>
756
757<br>[⬆ Back to top](#contents)
758
759### spreadOver
760
761Takes a variadic function and returns a closure that accepts an array of arguments to map to the inputs of the function.
762
763Use closures and the spread operator (`...`) to map the array of arguments to the inputs of the function.
764
765```js
766const spreadOver = fn => argsArr => fn(...argsArr);
767```
768
769<details>
770<summary>Examples</summary>
771
772```js
773const arrayMax = spreadOver(Math.max);
774arrayMax([1, 2, 3]); // 3
775```
776
777</details>
778
779<br>[⬆ Back to top](#contents)
780
781### unary
782
783Creates a function that accepts up to one argument, ignoring any additional arguments.
784
785Call the provided function, `fn`, with just the first argument given.
786
787```js
788const unary = fn => val => fn(val);
789```
790
791<details>
792<summary>Examples</summary>
793
794```js
795['6', '8', '10'].map(unary(parseInt)); // [6, 8, 10]
796```
797
798</details>
799
800<br>[⬆ Back to top](#contents)
801
802
803---
804
805## 📚 Array
806
807### all
808
809Returns `true` if the provided predicate function returns `true` for all elements in a collection, `false` otherwise.
810
811Use `Array.prototype.every()` to test if all elements in the collection return `true` based on `fn`.
812Omit the second argument, `fn`, to use `Boolean` as a default.
813
814```js
815const all = (arr, fn = Boolean) => arr.every(fn);
816```
817
818<details>
819<summary>Examples</summary>
820
821```js
822all([4, 2, 3], x => x > 1); // true
823all([1, 2, 3]); // true
824```
825
826</details>
827
828<br>[⬆ Back to top](#contents)
829
830### allEqual
831
832Check if all elements in an array are equal.
833
834Use `Array.prototype.every()` to check if all the elements of the array are the same as the first one.
835
836```js
837const allEqual = arr => arr.every(val => val === arr[0]);
838```
839
840<details>
841<summary>Examples</summary>
842
843```js
844allEqual([1, 2, 3, 4, 5, 6]); // false
845allEqual([1, 1, 1, 1]); // true
846```
847
848</details>
849
850<br>[⬆ Back to top](#contents)
851
852### any
853
854Returns `true` if the provided predicate function returns `true` for at least one element in a collection, `false` otherwise.
855
856Use `Array.prototype.some()` to test if any elements in the collection return `true` based on `fn`.
857Omit the second argument, `fn`, to use `Boolean` as a default.
858
859```js
860const any = (arr, fn = Boolean) => arr.some(fn);
861```
862
863<details>
864<summary>Examples</summary>
865
866```js
867any([0, 1, 2, 0], x => x >= 2); // true
868any([0, 0, 1, 0]); // true
869```
870
871</details>
872
873<br>[⬆ Back to top](#contents)
874
875### arrayToCSV
876
877Converts a 2D array to a comma-separated values (CSV) string.
878
879Use `Array.prototype.map()` and `Array.prototype.join(delimiter)` to combine individual 1D arrays (rows) into strings.
880Use `Array.prototype.join('\n')` to combine all rows into a CSV string, separating each row with a newline.
881Omit the second argument, `delimiter`, to use a default delimiter of `,`.
882
883```js
884const arrayToCSV = (arr, delimiter = ',') =>
885 arr.map(v => v.map(x => `"${x}"`).join(delimiter)).join('\n');
886```
887
888<details>
889<summary>Examples</summary>
890
891```js
892arrayToCSV([['a', 'b'], ['c', 'd']]); // '"a","b"\n"c","d"'
893arrayToCSV([['a', 'b'], ['c', 'd']], ';'); // '"a";"b"\n"c";"d"'
894```
895
896</details>
897
898<br>[⬆ Back to top](#contents)
899
900### bifurcate
901
902Splits values into two groups. If an element in `filter` is truthy, the corresponding element in the collection belongs to the first group; otherwise, it belongs to the second group.
903
904Use `Array.prototype.reduce()` and `Array.prototype.push()` to add elements to groups, based on `filter`.
905
906```js
907const bifurcate = (arr, filter) =>
908 arr.reduce((acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc), [[], []]);
909```
910
911<details>
912<summary>Examples</summary>
913
914```js
915bifurcate(['beep', 'boop', 'foo', 'bar'], [true, true, false, true]); // [ ['beep', 'boop', 'bar'], ['foo'] ]
916```
917
918</details>
919
920<br>[⬆ Back to top](#contents)
921
922### bifurcateBy
923
924Splits values into two groups according to a predicate function, which specifies which group an element in the input collection belongs to. If the predicate function returns a truthy value, the collection element belongs to the first group; otherwise, it belongs to the second group.
925
926Use `Array.prototype.reduce()` and `Array.prototype.push()` to add elements to groups, based on the value returned by `fn` for each element.
927
928```js
929const bifurcateBy = (arr, fn) =>
930 arr.reduce((acc, val, i) => (acc[fn(val, i) ? 0 : 1].push(val), acc), [[], []]);
931```
932
933<details>
934<summary>Examples</summary>
935
936```js
937bifurcateBy(['beep', 'boop', 'foo', 'bar'], x => x[0] === 'b'); // [ ['beep', 'boop', 'bar'], ['foo'] ]
938```
939
940</details>
941
942<br>[⬆ Back to top](#contents)
943
944### chunk
945
946Chunks an array into smaller arrays of a specified size.
947
948Use `Array.from()` to create a new array, that fits the number of chunks that will be produced.
949Use `Array.prototype.slice()` to map each element of the new array to a chunk the length of `size`.
950If the original array can't be split evenly, the final chunk will contain the remaining elements.
951
952```js
953const chunk = (arr, size) =>
954 Array.from({ length: Math.ceil(arr.length / size) }, (v, i) =>
955 arr.slice(i * size, i * size + size)
956 );
957```
958
959<details>
960<summary>Examples</summary>
961
962```js
963chunk([1, 2, 3, 4, 5], 2); // [[1,2],[3,4],[5]]
964```
965
966</details>
967
968<br>[⬆ Back to top](#contents)
969
970### compact
971
972Removes falsey values from an array.
973
974Use `Array.prototype.filter()` to filter out falsey values (`false`, `null`, `0`, `""`, `undefined`, and `NaN`).
975
976```js
977const compact = arr => arr.filter(Boolean);
978```
979
980<details>
981<summary>Examples</summary>
982
983```js
984compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34]); // [ 1, 2, 3, 'a', 's', 34 ]
985```
986
987</details>
988
989<br>[⬆ Back to top](#contents)
990
991### countBy
992
993Groups the elements of an array based on the given function and returns the count of elements in each group.
994
995Use `Array.prototype.map()` to map the values of an array to a function or property name.
996Use `Array.prototype.reduce()` to create an object, where the keys are produced from the mapped results.
997
998```js
999const countBy = (arr, fn) =>
1000 arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => {
1001 acc[val] = (acc[val] || 0) + 1;
1002 return acc;
1003 }, {});
1004```
1005
1006<details>
1007<summary>Examples</summary>
1008
1009```js
1010countBy([6.1, 4.2, 6.3], Math.floor); // {4: 1, 6: 2}
1011countBy(['one', 'two', 'three'], 'length'); // {3: 2, 5: 1}
1012```
1013
1014</details>
1015
1016<br>[⬆ Back to top](#contents)
1017
1018### countOccurrences
1019
1020Counts the occurrences of a value in an array.
1021
1022Use `Array.prototype.reduce()` to increment a counter each time you encounter the specific value inside the array.
1023
1024```js
1025const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0);
1026```
1027
1028<details>
1029<summary>Examples</summary>
1030
1031```js
1032countOccurrences([1, 1, 2, 1, 2, 3], 1); // 3
1033```
1034
1035</details>
1036
1037<br>[⬆ Back to top](#contents)
1038
1039### deepFlatten
1040
1041Deep flattens an array.
1042
1043Use recursion.
1044Use `Array.prototype.concat()` with an empty array (`[]`) and the spread operator (`...`) to flatten an array.
1045Recursively flatten each element that is an array.
1046
1047```js
1048const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)));
1049```
1050
1051<details>
1052<summary>Examples</summary>
1053
1054```js
1055deepFlatten([1, [2], [[3], 4], 5]); // [1,2,3,4,5]
1056```
1057
1058</details>
1059
1060<br>[⬆ Back to top](#contents)
1061
1062### difference
1063
1064Returns the difference between two arrays.
1065
1066Create a `Set` from `b`, then use `Array.prototype.filter()` on `a` to only keep values not contained in `b`.
1067
1068```js
1069const difference = (a, b) => {
1070 const s = new Set(b);
1071 return a.filter(x => !s.has(x));
1072};
1073```
1074
1075<details>
1076<summary>Examples</summary>
1077
1078```js
1079difference([1, 2, 3], [1, 2, 4]); // [3]
1080```
1081
1082</details>
1083
1084<br>[⬆ Back to top](#contents)
1085
1086### differenceBy
1087
1088Returns the difference between two arrays, after applying the provided function to each array element of both.
1089
1090Create a `Set` by applying `fn` to each element in `b`, then use `Array.prototype.filter()` in combination with `fn` on `a` to only keep values not contained in the previously created set.
1091
1092```js
1093const differenceBy = (a, b, fn) => {
1094 const s = new Set(b.map(fn));
1095 return a.filter(x => !s.has(fn(x)));
1096};
1097```
1098
1099<details>
1100<summary>Examples</summary>
1101
1102```js
1103differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [1.2]
1104differenceBy([{ x: 2 }, { x: 1 }], [{ x: 1 }], v => v.x); // [ { x: 2 } ]
1105```
1106
1107</details>
1108
1109<br>[⬆ Back to top](#contents)
1110
1111### differenceWith
1112
1113Filters out all values from an array for which the comparator function does not return `true`.
1114
1115Use `Array.prototype.filter()` and `Array.prototype.findIndex()` to find the appropriate values.
1116
1117```js
1118const differenceWith = (arr, val, comp) => arr.filter(a => val.findIndex(b => comp(a, b)) === -1);
1119```
1120
1121<details>
1122<summary>Examples</summary>
1123
1124```js
1125differenceWith([1, 1.2, 1.5, 3, 0], [1.9, 3, 0], (a, b) => Math.round(a) === Math.round(b)); // [1, 1.2]
1126```
1127
1128</details>
1129
1130<br>[⬆ Back to top](#contents)
1131
1132### drop
1133
1134Returns a new array with `n` elements removed from the left.
1135
1136Use `Array.prototype.slice()` to slice the remove the specified number of elements from the left.
1137
1138```js
1139const drop = (arr, n = 1) => arr.slice(n);
1140```
1141
1142<details>
1143<summary>Examples</summary>
1144
1145```js
1146drop([1, 2, 3]); // [2,3]
1147drop([1, 2, 3], 2); // [3]
1148drop([1, 2, 3], 42); // []
1149```
1150
1151</details>
1152
1153<br>[⬆ Back to top](#contents)
1154
1155### dropRight
1156
1157Returns a new array with `n` elements removed from the right.
1158
1159Use `Array.prototype.slice()` to slice the remove the specified number of elements from the right.
1160
1161```js
1162const dropRight = (arr, n = 1) => arr.slice(0, -n);
1163```
1164
1165<details>
1166<summary>Examples</summary>
1167
1168```js
1169dropRight([1, 2, 3]); // [1,2]
1170dropRight([1, 2, 3], 2); // [1]
1171dropRight([1, 2, 3], 42); // []
1172```
1173
1174</details>
1175
1176<br>[⬆ Back to top](#contents)
1177
1178### dropRightWhile
1179
1180Removes elements from the end of an array until the passed function returns `true`. Returns the remaining elements in the array.
1181
1182Loop through the array, using `Array.prototype.slice()` to drop the last element of the array until the returned value from the function is `true`.
1183Returns the remaining elements.
1184
1185```js
1186const dropRightWhile = (arr, func) => {
1187 while (arr.length > 0 && !func(arr[arr.length - 1])) arr = arr.slice(0, -1);
1188 return arr;
1189};
1190```
1191
1192<details>
1193<summary>Examples</summary>
1194
1195```js
1196dropRightWhile([1, 2, 3, 4], n => n < 3); // [1, 2]
1197```
1198
1199</details>
1200
1201<br>[⬆ Back to top](#contents)
1202
1203### dropWhile
1204
1205Removes elements in an array until the passed function returns `true`. Returns the remaining elements in the array.
1206
1207Loop through the array, using `Array.prototype.slice()` to drop the first element of the array until the returned value from the function is `true`.
1208Returns the remaining elements.
1209
1210```js
1211const dropWhile = (arr, func) => {
1212 while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1);
1213 return arr;
1214};
1215```
1216
1217<details>
1218<summary>Examples</summary>
1219
1220```js
1221dropWhile([1, 2, 3, 4], n => n >= 3); // [3,4]
1222```
1223
1224</details>
1225
1226<br>[⬆ Back to top](#contents)
1227
1228### everyNth
1229
1230Returns every nth element in an array.
1231
1232Use `Array.prototype.filter()` to create a new array that contains every nth element of a given array.
1233
1234```js
1235const everyNth = (arr, nth) => arr.filter((e, i) => i % nth === nth - 1);
1236```
1237
1238<details>
1239<summary>Examples</summary>
1240
1241```js
1242everyNth([1, 2, 3, 4, 5, 6], 2); // [ 2, 4, 6 ]
1243```
1244
1245</details>
1246
1247<br>[⬆ Back to top](#contents)
1248
1249### filterNonUnique
1250
1251Filters out the non-unique values in an array.
1252
1253Use `Array.prototype.filter()` for an array containing only the unique values.
1254
1255```js
1256const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i));
1257```
1258
1259<details>
1260<summary>Examples</summary>
1261
1262```js
1263filterNonUnique([1, 2, 2, 3, 4, 4, 5]); // [1, 3, 5]
1264```
1265
1266</details>
1267
1268<br>[⬆ Back to top](#contents)
1269
1270### filterNonUniqueBy
1271
1272Filters out the non-unique values in an array, based on a provided comparator function.
1273
1274Use `Array.prototype.filter()` and `Array.prototype.every()` for an array containing only the unique values, based on the comparator function, `fn`.
1275The comparator function takes four arguments: the values of the two elements being compared and their indexes.
1276
1277```js
1278const filterNonUniqueBy = (arr, fn) =>
1279 arr.filter((v, i) => arr.every((x, j) => (i === j) === fn(v, x, i, j)));
1280```
1281
1282<details>
1283<summary>Examples</summary>
1284
1285```js
1286filterNonUniqueBy(
1287 [
1288 { id: 0, value: 'a' },
1289 { id: 1, value: 'b' },
1290 { id: 2, value: 'c' },
1291 { id: 1, value: 'd' },
1292 { id: 0, value: 'e' }
1293 ],
1294 (a, b) => a.id == b.id
1295); // [ { id: 2, value: 'c' } ]
1296```
1297
1298</details>
1299
1300<br>[⬆ Back to top](#contents)
1301
1302### findLast
1303
1304Returns the last element for which the provided function returns a truthy value.
1305
1306Use `Array.prototype.filter()` to remove elements for which `fn` returns falsey values, `Array.prototype.pop()` to get the last one.
1307
1308```js
1309const findLast = (arr, fn) => arr.filter(fn).pop();
1310```
1311
1312<details>
1313<summary>Examples</summary>
1314
1315```js
1316findLast([1, 2, 3, 4], n => n % 2 === 1); // 3
1317```
1318
1319</details>
1320
1321<br>[⬆ Back to top](#contents)
1322
1323### findLastIndex
1324
1325Returns the index of the last element for which the provided function returns a truthy value.
1326
1327Use `Array.prototype.map()` to map each element to an array with its index and value.
1328Use `Array.prototype.filter()` to remove elements for which `fn` returns falsey values, `Array.prototype.pop()` to get the last one.
1329
1330```js
1331const findLastIndex = (arr, fn) =>
1332 arr
1333 .map((val, i) => [i, val])
1334 .filter(([i, val]) => fn(val, i, arr))
1335 .pop()[0];
1336```
1337
1338<details>
1339<summary>Examples</summary>
1340
1341```js
1342findLastIndex([1, 2, 3, 4], n => n % 2 === 1); // 2 (index of the value 3)
1343```
1344
1345</details>
1346
1347<br>[⬆ Back to top](#contents)
1348
1349### flatten
1350
1351Flattens an array up to the specified depth.
1352
1353Use recursion, decrementing `depth` by 1 for each level of depth.
1354Use `Array.prototype.reduce()` and `Array.prototype.concat()` to merge elements or arrays.
1355Base case, for `depth` equal to `1` stops recursion.
1356Omit the second argument, `depth` to flatten only to a depth of `1` (single flatten).
1357
1358```js
1359const flatten = (arr, depth = 1) =>
1360 arr.reduce((a, v) => a.concat(depth > 1 && Array.isArray(v) ? flatten(v, depth - 1) : v), []);
1361```
1362
1363<details>
1364<summary>Examples</summary>
1365
1366```js
1367flatten([1, [2], 3, 4]); // [1, 2, 3, 4]
1368flatten([1, [2, [3, [4, 5], 6], 7], 8], 2); // [1, 2, 3, [4, 5], 6, 7, 8]
1369```
1370
1371</details>
1372
1373<br>[⬆ Back to top](#contents)
1374
1375### forEachRight
1376
1377Executes a provided function once for each array element, starting from the array's last element.
1378
1379Use `Array.prototype.slice(0)` to clone the given array, `Array.prototype.reverse()` to reverse it and `Array.prototype.forEach()` to iterate over the reversed array.
1380
1381```js
1382const forEachRight = (arr, callback) =>
1383 arr
1384 .slice(0)
1385 .reverse()
1386 .forEach(callback);
1387```
1388
1389<details>
1390<summary>Examples</summary>
1391
1392```js
1393forEachRight([1, 2, 3, 4], val => console.log(val)); // '4', '3', '2', '1'
1394```
1395
1396</details>
1397
1398<br>[⬆ Back to top](#contents)
1399
1400### groupBy
1401
1402Groups the elements of an array based on the given function.
1403
1404Use `Array.prototype.map()` to map the values of an array to a function or property name.
1405Use `Array.prototype.reduce()` to create an object, where the keys are produced from the mapped results.
1406
1407```js
1408const groupBy = (arr, fn) =>
1409 arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => {
1410 acc[val] = (acc[val] || []).concat(arr[i]);
1411 return acc;
1412 }, {});
1413```
1414
1415<details>
1416<summary>Examples</summary>
1417
1418```js
1419groupBy([6.1, 4.2, 6.3], Math.floor); // {4: [4.2], 6: [6.1, 6.3]}
1420groupBy(['one', 'two', 'three'], 'length'); // {3: ['one', 'two'], 5: ['three']}
1421```
1422
1423</details>
1424
1425<br>[⬆ Back to top](#contents)
1426
1427### head
1428
1429Returns the head of a list.
1430
1431Use `arr[0]` to return the first element of the passed array.
1432
1433```js
1434const head = arr => arr[0];
1435```
1436
1437<details>
1438<summary>Examples</summary>
1439
1440```js
1441head([1, 2, 3]); // 1
1442```
1443
1444</details>
1445
1446<br>[⬆ Back to top](#contents)
1447
1448### indexOfAll
1449
1450Returns all indices of `val` in an array.
1451If `val` never occurs, returns `[]`.
1452
1453Use `Array.prototype.reduce()` to loop over elements and store indices for matching elements.
1454Return the array of indices.
1455
1456```js
1457const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []);
1458```
1459
1460<details>
1461<summary>Examples</summary>
1462
1463```js
1464indexOfAll([1, 2, 3, 1, 2, 3], 1); // [0,3]
1465indexOfAll([1, 2, 3], 4); // []
1466```
1467
1468</details>
1469
1470<br>[⬆ Back to top](#contents)
1471
1472### initial
1473
1474Returns all the elements of an array except the last one.
1475
1476Use `arr.slice(0,-1)` to return all but the last element of the array.
1477
1478```js
1479const initial = arr => arr.slice(0, -1);
1480```
1481
1482<details>
1483<summary>Examples</summary>
1484
1485```js
1486initial([1, 2, 3]); // [1,2]
1487```
1488
1489</details>
1490
1491<br>[⬆ Back to top](#contents)
1492
1493### initialize2DArray
1494
1495Initializes a 2D array of given width and height and value.
1496
1497Use `Array.prototype.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`.
1498
1499```js
1500const initialize2DArray = (w, h, val = null) =>
1501 Array.from({ length: h }).map(() => Array.from({ length: w }).fill(val));
1502```
1503
1504<details>
1505<summary>Examples</summary>
1506
1507```js
1508initialize2DArray(2, 2, 0); // [[0,0], [0,0]]
1509```
1510
1511</details>
1512
1513<br>[⬆ Back to top](#contents)
1514
1515### initializeArrayWithRange
1516
1517Initializes an array containing the numbers in the specified range where `start` and `end` are inclusive with their common difference `step`.
1518
1519Use `Array.from()` to create an array of the desired length, `(end - start + 1)/step`, and a map function to fill it with the desired values in the given range.
1520You can omit `start` to use a default value of `0`.
1521You can omit `step` to use a default value of `1`.
1522
1523```js
1524const initializeArrayWithRange = (end, start = 0, step = 1) =>
1525 Array.from({ length: Math.ceil((end - start + 1) / step) }, (v, i) => i * step + start);
1526```
1527
1528<details>
1529<summary>Examples</summary>
1530
1531```js
1532initializeArrayWithRange(5); // [0,1,2,3,4,5]
1533initializeArrayWithRange(7, 3); // [3,4,5,6,7]
1534initializeArrayWithRange(9, 0, 2); // [0,2,4,6,8]
1535```
1536
1537</details>
1538
1539<br>[⬆ Back to top](#contents)
1540
1541### initializeArrayWithRangeRight
1542
1543Initializes an array containing the numbers in the specified range (in reverse) where `start` and `end` are inclusive with their common difference `step`.
1544
1545Use `Array.from(Math.ceil((end+1-start)/step))` to create an array of the desired length(the amounts of elements is equal to `(end-start)/step` or `(end+1-start)/step` for inclusive end), `Array.prototype.map()` to fill with the desired values in a range.
1546You can omit `start` to use a default value of `0`.
1547You can omit `step` to use a default value of `1`.
1548
1549```js
1550const initializeArrayWithRangeRight = (end, start = 0, step = 1) =>
1551 Array.from({ length: Math.ceil((end + 1 - start) / step) }).map(
1552 (v, i, arr) => (arr.length - i - 1) * step + start
1553 );
1554```
1555
1556<details>
1557<summary>Examples</summary>
1558
1559```js
1560initializeArrayWithRangeRight(5); // [5,4,3,2,1,0]
1561initializeArrayWithRangeRight(7, 3); // [7,6,5,4,3]
1562initializeArrayWithRangeRight(9, 0, 2); // [8,6,4,2,0]
1563```
1564
1565</details>
1566
1567<br>[⬆ Back to top](#contents)
1568
1569### initializeArrayWithValues
1570
1571Initializes and fills an array with the specified values.
1572
1573Use `Array(n)` to create an array of the desired length, `fill(v)` to fill it with the desired values.
1574You can omit `val` to use a default value of `0`.
1575
1576```js
1577const initializeArrayWithValues = (n, val = 0) => Array(n).fill(val);
1578```
1579
1580<details>
1581<summary>Examples</summary>
1582
1583```js
1584initializeArrayWithValues(5, 2); // [2, 2, 2, 2, 2]
1585```
1586
1587</details>
1588
1589<br>[⬆ Back to top](#contents)
1590
1591### initializeNDArray
1592
1593Create a n-dimensional array with given value.
1594
1595Use recursion.
1596Use `Array.prototype.map()` to generate rows where each is a new array initialized using `initializeNDArray`.
1597
1598```js
1599const initializeNDArray = (val, ...args) =>
1600 args.length === 0
1601 ? val
1602 : Array.from({ length: args[0] }).map(() => initializeNDArray(val, ...args.slice(1)));
1603```
1604
1605<details>
1606<summary>Examples</summary>
1607
1608```js
1609initializeNDArray(1, 3); // [1,1,1]
1610initializeNDArray(5, 2, 2, 2); // [[[5,5],[5,5]],[[5,5],[5,5]]]
1611```
1612
1613</details>
1614
1615<br>[⬆ Back to top](#contents)
1616
1617### intersection
1618
1619Returns a list of elements that exist in both arrays.
1620
1621Create a `Set` from `b`, then use `Array.prototype.filter()` on `a` to only keep values contained in `b`.
1622
1623```js
1624const intersection = (a, b) => {
1625 const s = new Set(b);
1626 return a.filter(x => s.has(x));
1627};
1628```
1629
1630<details>
1631<summary>Examples</summary>
1632
1633```js
1634intersection([1, 2, 3], [4, 3, 2]); // [2, 3]
1635```
1636
1637</details>
1638
1639<br>[⬆ Back to top](#contents)
1640
1641### intersectionBy
1642
1643Returns a list of elements that exist in both arrays, after applying the provided function to each array element of both.
1644
1645Create a `Set` by applying `fn` to all elements in `b`, then use `Array.prototype.filter()` on `a` to only keep elements, which produce values contained in `b` when `fn` is applied to them.
1646
1647```js
1648const intersectionBy = (a, b, fn) => {
1649 const s = new Set(b.map(fn));
1650 return a.filter(x => s.has(fn(x)));
1651};
1652```
1653
1654<details>
1655<summary>Examples</summary>
1656
1657```js
1658intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [2.1]
1659```
1660
1661</details>
1662
1663<br>[⬆ Back to top](#contents)
1664
1665### intersectionWith
1666
1667Returns a list of elements that exist in both arrays, using a provided comparator function.
1668
1669Use `Array.prototype.filter()` and `Array.prototype.findIndex()` in combination with the provided comparator to determine intersecting values.
1670
1671```js
1672const intersectionWith = (a, b, comp) => a.filter(x => b.findIndex(y => comp(x, y)) !== -1);
1673```
1674
1675<details>
1676<summary>Examples</summary>
1677
1678```js
1679intersectionWith([1, 1.2, 1.5, 3, 0], [1.9, 3, 0, 3.9], (a, b) => Math.round(a) === Math.round(b)); // [1.5, 3, 0]
1680```
1681
1682</details>
1683
1684<br>[⬆ Back to top](#contents)
1685
1686### isSorted
1687
1688Returns `1` if the array is sorted in ascending order, `-1` if it is sorted in descending order or `0` if it is not sorted.
1689
1690Calculate the ordering `direction` for the first two elements.
1691Use `Object.entries()` to loop over array objects and compare them in pairs.
1692Return `0` if the `direction` changes or the `direction` if the last element is reached.
1693
1694```js
1695const isSorted = arr => {
1696 let direction = -(arr[0] - arr[1]);
1697 for (let [i, val] of arr.entries()) {
1698 direction = !direction ? -(arr[i - 1] - arr[i]) : direction;
1699 if (i === arr.length - 1) return !direction ? 0 : direction;
1700 else if ((val - arr[i + 1]) * direction > 0) return 0;
1701 }
1702};
1703```
1704
1705<details>
1706<summary>Examples</summary>
1707
1708```js
1709isSorted([0, 1, 2, 2]); // 1
1710isSorted([4, 3, 2]); // -1
1711isSorted([4, 3, 5]); // 0
1712```
1713
1714</details>
1715
1716<br>[⬆ Back to top](#contents)
1717
1718### join
1719
1720Joins all elements of an array into a string and returns this string.
1721Uses a separator and an end separator.
1722
1723Use `Array.prototype.reduce()` to combine elements into a string.
1724Omit the second argument, `separator`, to use a default separator of `','`.
1725Omit the third argument, `end`, to use the same value as `separator` by default.
1726
1727```js
1728const join = (arr, separator = ',', end = separator) =>
1729 arr.reduce(
1730 (acc, val, i) =>
1731 i === arr.length - 2
1732 ? acc + val + end
1733 : i === arr.length - 1
1734 ? acc + val
1735 : acc + val + separator,
1736 ''
1737 );
1738```
1739
1740<details>
1741<summary>Examples</summary>
1742
1743```js
1744join(['pen', 'pineapple', 'apple', 'pen'], ',', '&'); // "pen,pineapple,apple&pen"
1745join(['pen', 'pineapple', 'apple', 'pen'], ','); // "pen,pineapple,apple,pen"
1746join(['pen', 'pineapple', 'apple', 'pen']); // "pen,pineapple,apple,pen"
1747```
1748
1749</details>
1750
1751<br>[⬆ Back to top](#contents)
1752
1753### JSONtoCSV ![advanced](/advanced.svg)
1754
1755Converts an array of objects to a comma-separated values (CSV) string that contains only the `columns` specified.
1756
1757Use `Array.prototype.join(delimiter)` to combine all the names in `columns` to create the first row.
1758Use `Array.prototype.map()` and `Array.prototype.reduce()` to create a row for each object, substituting non-existent values with empty strings and only mapping values in `columns`.
1759Use `Array.prototype.join('\n')` to combine all rows into a string.
1760Omit the third argument, `delimiter`, to use a default delimiter of `,`.
1761
1762```js
1763const JSONtoCSV = (arr, columns, delimiter = ',') =>
1764 [
1765 columns.join(delimiter),
1766 ...arr.map(obj =>
1767 columns.reduce(
1768 (acc, key) => `${acc}${!acc.length ? '' : delimiter}"${!obj[key] ? '' : obj[key]}"`,
1769 ''
1770 )
1771 )
1772 ].join('\n');
1773```
1774
1775<details>
1776<summary>Examples</summary>
1777
1778```js
1779JSONtoCSV([{ a: 1, b: 2 }, { a: 3, b: 4, c: 5 }, { a: 6 }, { b: 7 }], ['a', 'b']); // 'a,b\n"1","2"\n"3","4"\n"6",""\n"","7"'
1780JSONtoCSV([{ a: 1, b: 2 }, { a: 3, b: 4, c: 5 }, { a: 6 }, { b: 7 }], ['a', 'b'], ';'); // 'a;b\n"1";"2"\n"3";"4"\n"6";""\n"";"7"'
1781```
1782
1783</details>
1784
1785<br>[⬆ Back to top](#contents)
1786
1787### last
1788
1789Returns the last element in an array.
1790
1791Use `arr.length - 1` to compute the index of the last element of the given array and returning it.
1792
1793```js
1794const last = arr => arr[arr.length - 1];
1795```
1796
1797<details>
1798<summary>Examples</summary>
1799
1800```js
1801last([1, 2, 3]); // 3
1802```
1803
1804</details>
1805
1806<br>[⬆ Back to top](#contents)
1807
1808### longestItem
1809
1810Takes any number of iterable objects or objects with a `length` property and returns the longest one.
1811If multiple objects have the same length, the first one will be returned.
1812Returns `undefined` if no arguments are provided.
1813
1814Use `Array.prototype.reduce()`, comparing the `length` of objects to find the longest one.
1815
1816```js
1817const longestItem = (...vals) => vals.reduce((a, x) => (x.length > a.length ? x : a));
1818```
1819
1820<details>
1821<summary>Examples</summary>
1822
1823```js
1824longestItem('this', 'is', 'a', 'testcase'); // 'testcase'
1825longestItem(...['a', 'ab', 'abc']); // 'abc'
1826longestItem(...['a', 'ab', 'abc'], 'abcd'); // 'abcd'
1827longestItem([1, 2, 3], [1, 2], [1, 2, 3, 4, 5]); // [1, 2, 3, 4, 5]
1828longestItem([1, 2, 3], 'foobar'); // 'foobar'
1829```
1830
1831</details>
1832
1833<br>[⬆ Back to top](#contents)
1834
1835### mapObject ![advanced](/advanced.svg)
1836
1837Maps the values of an array to an object using a function, where the key-value pairs consist of the original value as the key and the mapped value.
1838
1839Use an anonymous inner function scope to declare an undefined memory space, using closures to store a return value. Use a new `Array` to store the array with a map of the function over its data set and a comma operator to return a second step, without needing to move from one context to another (due to closures and order of operations).
1840
1841```js
1842const mapObject = (arr, fn) =>
1843 (a => (
1844 (a = [arr, arr.map(fn)]), a[0].reduce((acc, val, ind) => ((acc[val] = a[1][ind]), acc), {})
1845 ))();
1846```
1847
1848<details>
1849<summary>Examples</summary>
1850
1851```js
1852const squareIt = arr => mapObject(arr, a => a * a);
1853squareIt([1, 2, 3]); // { 1: 1, 2: 4, 3: 9 }
1854```
1855
1856</details>
1857
1858<br>[⬆ Back to top](#contents)
1859
1860### maxN
1861
1862Returns the `n` maximum elements from the provided array.
1863If `n` is greater than or equal to the provided array's length, then return the original array (sorted in descending order).
1864
1865Use `Array.prototype.sort()` combined with the spread operator (`...`) to create a shallow clone of the array and sort it in descending order.
1866Use `Array.prototype.slice()` to get the specified number of elements.
1867Omit the second argument, `n`, to get a one-element array.
1868
1869```js
1870const maxN = (arr, n = 1) => [...arr].sort((a, b) => b - a).slice(0, n);
1871```
1872
1873<details>
1874<summary>Examples</summary>
1875
1876```js
1877maxN([1, 2, 3]); // [3]
1878maxN([1, 2, 3], 2); // [3,2]
1879```
1880
1881</details>
1882
1883<br>[⬆ Back to top](#contents)
1884
1885### minN
1886
1887Returns the `n` minimum elements from the provided array.
1888If `n` is greater than or equal to the provided array's length, then return the original array (sorted in ascending order).
1889
1890Use `Array.prototype.sort()` combined with the spread operator (`...`) to create a shallow clone of the array and sort it in ascending order.
1891Use `Array.prototype.slice()` to get the specified number of elements.
1892Omit the second argument, `n`, to get a one-element array.
1893
1894```js
1895const minN = (arr, n = 1) => [...arr].sort((a, b) => a - b).slice(0, n);
1896```
1897
1898<details>
1899<summary>Examples</summary>
1900
1901```js
1902minN([1, 2, 3]); // [1]
1903minN([1, 2, 3], 2); // [1,2]
1904```
1905
1906</details>
1907
1908<br>[⬆ Back to top](#contents)
1909
1910### none
1911
1912Returns `true` if the provided predicate function returns `false` for all elements in a collection, `false` otherwise.
1913
1914Use `Array.prototype.some()` to test if any elements in the collection return `true` based on `fn`.
1915Omit the second argument, `fn`, to use `Boolean` as a default.
1916
1917```js
1918const none = (arr, fn = Boolean) => !arr.some(fn);
1919```
1920
1921<details>
1922<summary>Examples</summary>
1923
1924```js
1925none([0, 1, 3, 0], x => x == 2); // true
1926none([0, 0, 0]); // true
1927```
1928
1929</details>
1930
1931<br>[⬆ Back to top](#contents)
1932
1933### nthElement
1934
1935Returns the nth element of an array.
1936
1937Use `Array.prototype.slice()` to get an array containing the nth element at the first place.
1938If the index is out of bounds, return `undefined`.
1939Omit the second argument, `n`, to get the first element of the array.
1940
1941```js
1942const nthElement = (arr, n = 0) => (n === -1 ? arr.slice(n) : arr.slice(n, n + 1))[0];
1943```
1944
1945<details>
1946<summary>Examples</summary>
1947
1948```js
1949nthElement(['a', 'b', 'c'], 1); // 'b'
1950nthElement(['a', 'b', 'b'], -3); // 'a'
1951```
1952
1953</details>
1954
1955<br>[⬆ Back to top](#contents)
1956
1957### offset
1958
1959Moves the specified amount of elements to the end of the array.
1960
1961Use `Array.prototype.slice()` twice to get the elements after the specified index and the elements before that.
1962Use the spread operator(`...`) to combine the two into one array.
1963If `offset` is negative, the elements will be moved from end to start.
1964
1965```js
1966const offset = (arr, offset) => [...arr.slice(offset), ...arr.slice(0, offset)];
1967```
1968
1969<details>
1970<summary>Examples</summary>
1971
1972```js
1973offset([1, 2, 3, 4, 5], 2); // [3, 4, 5, 1, 2]
1974offset([1, 2, 3, 4, 5], -2); // [4, 5, 1, 2, 3]
1975```
1976
1977</details>
1978
1979<br>[⬆ Back to top](#contents)
1980
1981### partition
1982
1983Groups the elements into two arrays, depending on the provided function's truthiness for each element.
1984
1985Use `Array.prototype.reduce()` to create an array of two arrays.
1986Use `Array.prototype.push()` to add elements for which `fn` returns `true` to the first array and elements for which `fn` returns `false` to the second one.
1987
1988```js
1989const partition = (arr, fn) =>
1990 arr.reduce(
1991 (acc, val, i, arr) => {
1992 acc[fn(val, i, arr) ? 0 : 1].push(val);
1993 return acc;
1994 },
1995 [[], []]
1996 );
1997```
1998
1999<details>
2000<summary>Examples</summary>
2001
2002```js
2003const users = [{ user: 'barney', age: 36, active: false }, { user: 'fred', age: 40, active: true }];
2004partition(users, o => o.active); // [[{ 'user': 'fred', 'age': 40, 'active': true }],[{ 'user': 'barney', 'age': 36, 'active': false }]]
2005```
2006
2007</details>
2008
2009<br>[⬆ Back to top](#contents)
2010
2011### permutations ![advanced](/advanced.svg)
2012
2013⚠️ **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.
2014
2015Generates all permutations of an array's elements (contains duplicates).
2016
2017Use recursion.
2018For each element in the given array, create all the partial permutations for the rest of its elements.
2019Use `Array.prototype.map()` to combine the element with each partial permutation, then `Array.prototype.reduce()` to combine all permutations in one array.
2020Base cases are for array `length` equal to `2` or `1`.
2021
2022```js
2023const permutations = arr => {
2024 if (arr.length <= 2) return arr.length === 2 ? [arr, [arr[1], arr[0]]] : arr;
2025 return arr.reduce(
2026 (acc, item, i) =>
2027 acc.concat(
2028 permutations([...arr.slice(0, i), ...arr.slice(i + 1)]).map(val => [item, ...val])
2029 ),
2030 []
2031 );
2032};
2033```
2034
2035<details>
2036<summary>Examples</summary>
2037
2038```js
2039permutations([1, 33, 5]); // [ [ 1, 33, 5 ], [ 1, 5, 33 ], [ 33, 1, 5 ], [ 33, 5, 1 ], [ 5, 1, 33 ], [ 5, 33, 1 ] ]
2040```
2041
2042</details>
2043
2044<br>[⬆ Back to top](#contents)
2045
2046### pull
2047
2048Mutates the original array to filter out the values specified.
2049
2050Use `Array.prototype.filter()` and `Array.prototype.includes()` to pull out the values that are not needed.
2051Use `Array.prototype.length = 0` to mutate the passed in an array by resetting it's length to zero and `Array.prototype.push()` to re-populate it with only the pulled values.
2052
2053_(For a snippet that does not mutate the original array see [`without`](#without))_
2054
2055```js
2056const pull = (arr, ...args) => {
2057 let argState = Array.isArray(args[0]) ? args[0] : args;
2058 let pulled = arr.filter((v, i) => !argState.includes(v));
2059 arr.length = 0;
2060 pulled.forEach(v => arr.push(v));
2061};
2062```
2063
2064<details>
2065<summary>Examples</summary>
2066
2067```js
2068let myArray = ['a', 'b', 'c', 'a', 'b', 'c'];
2069pull(myArray, 'a', 'c'); // myArray = [ 'b', 'b' ]
2070```
2071
2072</details>
2073
2074<br>[⬆ Back to top](#contents)
2075
2076### pullAtIndex ![advanced](/advanced.svg)
2077
2078Mutates the original array to filter out the values at the specified indexes.
2079
2080Use `Array.prototype.filter()` and `Array.prototype.includes()` to pull out the values that are not needed.
2081Use `Array.prototype.length = 0` to mutate the passed in an array by resetting it's length to zero and `Array.prototype.push()` to re-populate it with only the pulled values.
2082Use `Array.prototype.push()` to keep track of pulled values
2083
2084```js
2085const pullAtIndex = (arr, pullArr) => {
2086 let removed = [];
2087 let pulled = arr
2088 .map((v, i) => (pullArr.includes(i) ? removed.push(v) : v))
2089 .filter((v, i) => !pullArr.includes(i));
2090 arr.length = 0;
2091 pulled.forEach(v => arr.push(v));
2092 return removed;
2093};
2094```
2095
2096<details>
2097<summary>Examples</summary>
2098
2099```js
2100let myArray = ['a', 'b', 'c', 'd'];
2101let pulled = pullAtIndex(myArray, [1, 3]); // myArray = [ 'a', 'c' ] , pulled = [ 'b', 'd' ]
2102```
2103
2104</details>
2105
2106<br>[⬆ Back to top](#contents)
2107
2108### pullAtValue ![advanced](/advanced.svg)
2109
2110Mutates the original array to filter out the values specified. Returns the removed elements.
2111
2112Use `Array.prototype.filter()` and `Array.prototype.includes()` to pull out the values that are not needed.
2113Use `Array.prototype.length = 0` to mutate the passed in an array by resetting it's length to zero and `Array.prototype.push()` to re-populate it with only the pulled values.
2114Use `Array.prototype.push()` to keep track of pulled values
2115
2116```js
2117const pullAtValue = (arr, pullArr) => {
2118 let removed = [],
2119 pushToRemove = arr.forEach((v, i) => (pullArr.includes(v) ? removed.push(v) : v)),
2120 mutateTo = arr.filter((v, i) => !pullArr.includes(v));
2121 arr.length = 0;
2122 mutateTo.forEach(v => arr.push(v));
2123 return removed;
2124};
2125```
2126
2127<details>
2128<summary>Examples</summary>
2129
2130```js
2131let myArray = ['a', 'b', 'c', 'd'];
2132let pulled = pullAtValue(myArray, ['b', 'd']); // myArray = [ 'a', 'c' ] , pulled = [ 'b', 'd' ]
2133```
2134
2135</details>
2136
2137<br>[⬆ Back to top](#contents)
2138
2139### pullBy ![advanced](/advanced.svg)
2140
2141Mutates the original array to filter out the values specified, based on a given iterator function.
2142
2143Check if the last argument provided in a function.
2144Use `Array.prototype.map()` to apply the iterator function `fn` to all array elements.
2145Use `Array.prototype.filter()` and `Array.prototype.includes()` to pull out the values that are not needed.
2146Use `Array.prototype.length = 0` to mutate the passed in an array by resetting it's length to zero and `Array.prototype.push()` to re-populate it with only the pulled values.
2147
2148```js
2149const pullBy = (arr, ...args) => {
2150 const length = args.length;
2151 let fn = length > 1 ? args[length - 1] : undefined;
2152 fn = typeof fn == 'function' ? (args.pop(), fn) : undefined;
2153 let argState = (Array.isArray(args[0]) ? args[0] : args).map(val => fn(val));
2154 let pulled = arr.filter((v, i) => !argState.includes(fn(v)));
2155 arr.length = 0;
2156 pulled.forEach(v => arr.push(v));
2157};
2158```
2159
2160<details>
2161<summary>Examples</summary>
2162
2163```js
2164var myArray = [{ x: 1 }, { x: 2 }, { x: 3 }, { x: 1 }];
2165pullBy(myArray, [{ x: 1 }, { x: 3 }], o => o.x); // myArray = [{ x: 2 }]
2166```
2167
2168</details>
2169
2170<br>[⬆ Back to top](#contents)
2171
2172### reducedFilter
2173
2174Filter an array of objects based on a condition while also filtering out unspecified keys.
2175
2176Use `Array.prototype.filter()` to filter the array based on the predicate `fn` so that it returns the objects for which the condition returned a truthy value.
2177On the filtered array, use `Array.prototype.map()` to return the new object using `Array.prototype.reduce()` to filter out the keys which were not supplied as the `keys` argument.
2178
2179```js
2180const reducedFilter = (data, keys, fn) =>
2181 data.filter(fn).map(el =>
2182 keys.reduce((acc, key) => {
2183 acc[key] = el[key];
2184 return acc;
2185 }, {})
2186 );
2187```
2188
2189<details>
2190<summary>Examples</summary>
2191
2192```js
2193const data = [
2194 {
2195 id: 1,
2196 name: 'john',
2197 age: 24
2198 },
2199 {
2200 id: 2,
2201 name: 'mike',
2202 age: 50
2203 }
2204];
2205
2206reducedFilter(data, ['id', 'name'], item => item.age > 24); // [{ id: 2, name: 'mike'}]
2207```
2208
2209</details>
2210
2211<br>[⬆ Back to top](#contents)
2212
2213### reduceSuccessive
2214
2215Applies a function against an accumulator and each element in the array (from left to right), returning an array of successively reduced values.
2216
2217Use `Array.prototype.reduce()` to apply the given function to the given array, storing each new result.
2218
2219```js
2220const reduceSuccessive = (arr, fn, acc) =>
2221 arr.reduce((res, val, i, arr) => (res.push(fn(res.slice(-1)[0], val, i, arr)), res), [acc]);
2222```
2223
2224<details>
2225<summary>Examples</summary>
2226
2227```js
2228reduceSuccessive([1, 2, 3, 4, 5, 6], (acc, val) => acc + val, 0); // [0, 1, 3, 6, 10, 15, 21]
2229```
2230
2231</details>
2232
2233<br>[⬆ Back to top](#contents)
2234
2235### reduceWhich
2236
2237Returns the minimum/maximum value of an array, after applying the provided function to set comparing rule.
2238
2239Use `Array.prototype.reduce()` in combination with the `comparator` function to get the appropriate element in the array.
2240You can omit the second parameter, `comparator`, to use the default one that returns the minimum element in the array.
2241
2242```js
2243const reduceWhich = (arr, comparator = (a, b) => a - b) =>
2244 arr.reduce((a, b) => (comparator(a, b) >= 0 ? b : a));
2245```
2246
2247<details>
2248<summary>Examples</summary>
2249
2250```js
2251reduceWhich([1, 3, 2]); // 1
2252reduceWhich([1, 3, 2], (a, b) => b - a); // 3
2253reduceWhich(
2254 [{ name: 'Tom', age: 12 }, { name: 'Jack', age: 18 }, { name: 'Lucy', age: 9 }],
2255 (a, b) => a.age - b.age
2256); // {name: "Lucy", age: 9}
2257```
2258
2259</details>
2260
2261<br>[⬆ Back to top](#contents)
2262
2263### reject
2264
2265Takes a predicate and array, like `Array.prototype.filter()`, but only keeps `x` if `pred(x) === false`.
2266
2267```js
2268const reject = (pred, array) => array.filter((...args) => !pred(...args));
2269```
2270
2271<details>
2272<summary>Examples</summary>
2273
2274```js
2275reject(x => x % 2 === 0, [1, 2, 3, 4, 5]); // [1, 3, 5]
2276reject(word => word.length > 4, ['Apple', 'Pear', 'Kiwi', 'Banana']); // ['Pear', 'Kiwi']
2277```
2278
2279</details>
2280
2281<br>[⬆ Back to top](#contents)
2282
2283### remove
2284
2285Removes elements from an array for which the given function returns `false`.
2286
2287Use `Array.prototype.filter()` to find array elements that return truthy values and `Array.prototype.reduce()` to remove elements using `Array.prototype.splice()`.
2288The `func` is invoked with three arguments (`value, index, array`).
2289
2290```js
2291const remove = (arr, func) =>
2292 Array.isArray(arr)
2293 ? arr.filter(func).reduce((acc, val) => {
2294 arr.splice(arr.indexOf(val), 1);
2295 return acc.concat(val);
2296 }, [])
2297 : [];
2298```
2299
2300<details>
2301<summary>Examples</summary>
2302
2303```js
2304remove([1, 2, 3, 4], n => n % 2 === 0); // [2, 4]
2305```
2306
2307</details>
2308
2309<br>[⬆ Back to top](#contents)
2310
2311### sample
2312
2313Returns a random element from an array.
2314
2315Use `Math.random()` to generate a random number, multiply it by `length` and round it off to the nearest whole number using `Math.floor()`.
2316This method also works with strings.
2317
2318```js
2319const sample = arr => arr[Math.floor(Math.random() * arr.length)];
2320```
2321
2322<details>
2323<summary>Examples</summary>
2324
2325```js
2326sample([3, 7, 9, 11]); // 9
2327```
2328
2329</details>
2330
2331<br>[⬆ Back to top](#contents)
2332
2333### sampleSize
2334
2335Gets `n` random elements at unique keys from `array` up to the size of `array`.
2336
2337Shuffle the array using the [Fisher-Yates algorithm](https://github.com/30-seconds/30-seconds-of-code#shuffle).
2338Use `Array.prototype.slice()` to get the first `n` elements.
2339Omit the second argument, `n` to get only one element at random from the array.
2340
2341```js
2342const sampleSize = ([...arr], n = 1) => {
2343 let m = arr.length;
2344 while (m) {
2345 const i = Math.floor(Math.random() * m--);
2346 [arr[m], arr[i]] = [arr[i], arr[m]];
2347 }
2348 return arr.slice(0, n);
2349};
2350```
2351
2352<details>
2353<summary>Examples</summary>
2354
2355```js
2356sampleSize([1, 2, 3], 2); // [3,1]
2357sampleSize([1, 2, 3], 4); // [2,3,1]
2358```
2359
2360</details>
2361
2362<br>[⬆ Back to top](#contents)
2363
2364### shank
2365
2366Has the same functionality as [`Array.prototype.splice()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice), but returning a new array instead of mutating the original array.
2367
2368Use `Array.prototype.slice()` and `Array.prototype.concat()` to get a new array with the new contents after removing existing elements and/or adding new elements.
2369Omit the second argument, `index`, to start at `0`.
2370Omit the third argument, `delCount`, to remove `0` elements.
2371Omit the fourth argument, `elements`, in order to not add any new elements.
2372
2373```js
2374const shank = (arr, index = 0, delCount = 0, ...elements) =>
2375 arr
2376 .slice(0, index)
2377 .concat(elements)
2378 .concat(arr.slice(index + delCount));
2379```
2380
2381<details>
2382<summary>Examples</summary>
2383
2384```js
2385const names = ['alpha', 'bravo', 'charlie'];
2386const namesAndDelta = shank(names, 1, 0, 'delta'); // [ 'alpha', 'delta', 'bravo', 'charlie' ]
2387const namesNoBravo = shank(names, 1, 1); // [ 'alpha', 'charlie' ]
2388console.log(names); // ['alpha', 'bravo', 'charlie']
2389```
2390
2391</details>
2392
2393<br>[⬆ Back to top](#contents)
2394
2395### shuffle
2396
2397Randomizes the order of the values of an array, returning a new array.
2398
2399Uses the [Fisher-Yates algorithm](https://github.com/30-seconds/30-seconds-of-code#shuffle) to reorder the elements of the array.
2400
2401```js
2402const shuffle = ([...arr]) => {
2403 let m = arr.length;
2404 while (m) {
2405 const i = Math.floor(Math.random() * m--);
2406 [arr[m], arr[i]] = [arr[i], arr[m]];
2407 }
2408 return arr;
2409};
2410```
2411
2412<details>
2413<summary>Examples</summary>
2414
2415```js
2416const foo = [1, 2, 3];
2417shuffle(foo); // [2, 3, 1], foo = [1, 2, 3]
2418```
2419
2420</details>
2421
2422<br>[⬆ Back to top](#contents)
2423
2424### similarity
2425
2426Returns an array of elements that appear in both arrays.
2427
2428Use `Array.prototype.filter()` to remove values that are not part of `values`, determined using `Array.prototype.includes()`.
2429
2430```js
2431const similarity = (arr, values) => arr.filter(v => values.includes(v));
2432```
2433
2434<details>
2435<summary>Examples</summary>
2436
2437```js
2438similarity([1, 2, 3], [1, 2, 4]); // [1, 2]
2439```
2440
2441</details>
2442
2443<br>[⬆ Back to top](#contents)
2444
2445### sortedIndex
2446
2447Returns the lowest index at which value should be inserted into array in order to maintain its sort order.
2448
2449Check if the array is sorted in descending order (loosely).
2450Use `Array.prototype.findIndex()` to find the appropriate index where the element should be inserted.
2451
2452```js
2453const sortedIndex = (arr, n) => {
2454 const isDescending = arr[0] > arr[arr.length - 1];
2455 const index = arr.findIndex(el => (isDescending ? n >= el : n <= el));
2456 return index === -1 ? arr.length : index;
2457};
2458```
2459
2460<details>
2461<summary>Examples</summary>
2462
2463```js
2464sortedIndex([5, 3, 2, 1], 4); // 1
2465sortedIndex([30, 50], 40); // 1
2466```
2467
2468</details>
2469
2470<br>[⬆ Back to top](#contents)
2471
2472### sortedIndexBy
2473
2474Returns the lowest index at which value should be inserted into array in order to maintain its sort order, based on a provided iterator function.
2475
2476Check if the array is sorted in descending order (loosely).
2477Use `Array.prototype.findIndex()` to find the appropriate index where the element should be inserted, based on the iterator function `fn`.
2478
2479```js
2480const sortedIndexBy = (arr, n, fn) => {
2481 const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]);
2482 const val = fn(n);
2483 const index = arr.findIndex(el => (isDescending ? val >= fn(el) : val <= fn(el)));
2484 return index === -1 ? arr.length : index;
2485};
2486```
2487
2488<details>
2489<summary>Examples</summary>
2490
2491```js
2492sortedIndexBy([{ x: 4 }, { x: 5 }], { x: 4 }, o => o.x); // 0
2493```
2494
2495</details>
2496
2497<br>[⬆ Back to top](#contents)
2498
2499### sortedLastIndex
2500
2501Returns the highest index at which value should be inserted into array in order to maintain its sort order.
2502
2503Check if the array is sorted in descending order (loosely).
2504Use `Array.prototype.reverse()` and `Array.prototype.findIndex()` to find the appropriate last index where the element should be inserted.
2505
2506```js
2507const sortedLastIndex = (arr, n) => {
2508 const isDescending = arr[0] > arr[arr.length - 1];
2509 const index = arr.reverse().findIndex(el => (isDescending ? n <= el : n >= el));
2510 return index === -1 ? 0 : arr.length - index;
2511};
2512```
2513
2514<details>
2515<summary>Examples</summary>
2516
2517```js
2518sortedLastIndex([10, 20, 30, 30, 40], 30); // 4
2519```
2520
2521</details>
2522
2523<br>[⬆ Back to top](#contents)
2524
2525### sortedLastIndexBy
2526
2527Returns the highest index at which value should be inserted into array in order to maintain its sort order, based on a provided iterator function.
2528
2529Check if the array is sorted in descending order (loosely).
2530Use `Array.prototype.map()` to apply the iterator function to all elements of the array.
2531Use `Array.prototype.reverse()` and `Array.prototype.findIndex()` to find the appropriate last index where the element should be inserted, based on the provided iterator function.
2532
2533```js
2534const sortedLastIndexBy = (arr, n, fn) => {
2535 const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]);
2536 const val = fn(n);
2537 const index = arr
2538 .map(fn)
2539 .reverse()
2540 .findIndex(el => (isDescending ? val <= el : val >= el));
2541 return index === -1 ? 0 : arr.length - index;
2542};
2543```
2544
2545<details>
2546<summary>Examples</summary>
2547
2548```js
2549sortedLastIndexBy([{ x: 4 }, { x: 5 }], { x: 4 }, o => o.x); // 1
2550```
2551
2552</details>
2553
2554<br>[⬆ Back to top](#contents)
2555
2556### stableSort ![advanced](/advanced.svg)
2557
2558Performs stable sorting of an array, preserving the initial indexes of items when their values are the same.
2559Does not mutate the original array, but returns a new array instead.
2560
2561Use `Array.prototype.map()` to pair each element of the input array with its corresponding index.
2562Use `Array.prototype.sort()` and a `compare` function to sort the list, preserving their initial order if the items compared are equal.
2563Use `Array.prototype.map()` to convert back to the initial array items.
2564
2565```js
2566const stableSort = (arr, compare) =>
2567 arr
2568 .map((item, index) => ({ item, index }))
2569 .sort((a, b) => compare(a.item, b.item) || a.index - b.index)
2570 .map(({ item }) => item);
2571```
2572
2573<details>
2574<summary>Examples</summary>
2575
2576```js
2577const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
2578const stable = stableSort(arr, () => 0); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2579```
2580
2581</details>
2582
2583<br>[⬆ Back to top](#contents)
2584
2585### symmetricDifference
2586
2587Returns the symmetric difference between two arrays, without filtering out duplicate values.
2588
2589Create a `Set` from each array, then use `Array.prototype.filter()` on each of them to only keep values not contained in the other.
2590
2591```js
2592const symmetricDifference = (a, b) => {
2593 const sA = new Set(a),
2594 sB = new Set(b);
2595 return [...a.filter(x => !sB.has(x)), ...b.filter(x => !sA.has(x))];
2596};
2597```
2598
2599<details>
2600<summary>Examples</summary>
2601
2602```js
2603symmetricDifference([1, 2, 3], [1, 2, 4]); // [3, 4]
2604symmetricDifference([1, 2, 2], [1, 3, 1]); // [2, 2, 3]
2605```
2606
2607</details>
2608
2609<br>[⬆ Back to top](#contents)
2610
2611### symmetricDifferenceBy
2612
2613Returns the symmetric difference between two arrays, after applying the provided function to each array element of both.
2614
2615Create a `Set` by applying `fn` to each array's elements, then use `Array.prototype.filter()` on each of them to only keep values not contained in the other.
2616
2617```js
2618const symmetricDifferenceBy = (a, b, fn) => {
2619 const sA = new Set(a.map(v => fn(v))),
2620 sB = new Set(b.map(v => fn(v)));
2621 return [...a.filter(x => !sB.has(fn(x))), ...b.filter(x => !sA.has(fn(x)))];
2622};
2623```
2624
2625<details>
2626<summary>Examples</summary>
2627
2628```js
2629symmetricDifferenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [ 1.2, 3.4 ]
2630```
2631
2632</details>
2633
2634<br>[⬆ Back to top](#contents)
2635
2636### symmetricDifferenceWith
2637
2638Returns the symmetric difference between two arrays, using a provided function as a comparator.
2639
2640Use `Array.prototype.filter()` and `Array.prototype.findIndex()` to find the appropriate values.
2641
2642```js
2643const symmetricDifferenceWith = (arr, val, comp) => [
2644 ...arr.filter(a => val.findIndex(b => comp(a, b)) === -1),
2645 ...val.filter(a => arr.findIndex(b => comp(a, b)) === -1)
2646];
2647```
2648
2649<details>
2650<summary>Examples</summary>
2651
2652```js
2653symmetricDifferenceWith(
2654 [1, 1.2, 1.5, 3, 0],
2655 [1.9, 3, 0, 3.9],
2656 (a, b) => Math.round(a) === Math.round(b)
2657); // [1, 1.2, 3.9]
2658```
2659
2660</details>
2661
2662<br>[⬆ Back to top](#contents)
2663
2664### tail
2665
2666Returns all elements in an array except for the first one.
2667
2668Return `Array.prototype.slice(1)` if the array's `length` is more than `1`, otherwise, return the whole array.
2669
2670```js
2671const tail = arr => (arr.length > 1 ? arr.slice(1) : arr);
2672```
2673
2674<details>
2675<summary>Examples</summary>
2676
2677```js
2678tail([1, 2, 3]); // [2,3]
2679tail([1]); // [1]
2680```
2681
2682</details>
2683
2684<br>[⬆ Back to top](#contents)
2685
2686### take
2687
2688Returns an array with n elements removed from the beginning.
2689
2690Use `Array.prototype.slice()` to create a slice of the array with `n` elements taken from the beginning.
2691
2692```js
2693const take = (arr, n = 1) => arr.slice(0, n);
2694```
2695
2696<details>
2697<summary>Examples</summary>
2698
2699```js
2700take([1, 2, 3], 5); // [1, 2, 3]
2701take([1, 2, 3], 0); // []
2702```
2703
2704</details>
2705
2706<br>[⬆ Back to top](#contents)
2707
2708### takeRight
2709
2710Returns an array with n elements removed from the end.
2711
2712Use `Array.prototype.slice()` to create a slice of the array with `n` elements taken from the end.
2713
2714```js
2715const takeRight = (arr, n = 1) => arr.slice(arr.length - n, arr.length);
2716```
2717
2718<details>
2719<summary>Examples</summary>
2720
2721```js
2722takeRight([1, 2, 3], 2); // [ 2, 3 ]
2723takeRight([1, 2, 3]); // [3]
2724```
2725
2726</details>
2727
2728<br>[⬆ Back to top](#contents)
2729
2730### takeRightWhile
2731
2732Removes elements from the end of an array until the passed function returns `true`. Returns the removed elements.
2733
2734Loop through the array, using a `Array.prototype.reduceRight()` and accumulating elements while the function returns falsy value.
2735
2736```js
2737const takeRightWhile = (arr, func) =>
2738 arr.reduceRight((acc, el) => (func(el) ? acc : [el, ...acc]), []);
2739```
2740
2741<details>
2742<summary>Examples</summary>
2743
2744```js
2745takeRightWhile([1, 2, 3, 4], n => n < 3); // [3, 4]
2746```
2747
2748</details>
2749
2750<br>[⬆ Back to top](#contents)
2751
2752### takeWhile
2753
2754Removes elements in an array until the passed function returns `true`. Returns the removed elements.
2755
2756Loop through the array, using a `for...of` loop over `Array.prototype.entries()` until the returned value from the function is `true`.
2757Return the removed elements, using `Array.prototype.slice()`.
2758
2759```js
2760const takeWhile = (arr, func) => {
2761 for (const [i, val] of arr.entries()) if (func(val)) return arr.slice(0, i);
2762 return arr;
2763};
2764```
2765
2766<details>
2767<summary>Examples</summary>
2768
2769```js
2770takeWhile([1, 2, 3, 4], n => n >= 3); // [1, 2]
2771```
2772
2773</details>
2774
2775<br>[⬆ Back to top](#contents)
2776
2777### toHash
2778
2779Reduces a given Array-like into a value hash (keyed data store).
2780
2781Given an Iterable or Array-like structure, call `Array.prototype.reduce.call()` on the provided object to step over it and return an Object, keyed by the reference value.
2782
2783```js
2784const toHash = (object, key) =>
2785 Array.prototype.reduce.call(
2786 object,
2787 (acc, data, index) => ((acc[!key ? index : data[key]] = data), acc),
2788 {}
2789 );
2790```
2791
2792<details>
2793<summary>Examples</summary>
2794
2795```js
2796toHash([4, 3, 2, 1]); // { 0: 4, 1: 3, 2: 2, 3: 1 }
2797toHash([{ a: 'label' }], 'a'); // { label: { a: 'label' } }
2798// A more in depth example:
2799let users = [{ id: 1, first: 'Jon' }, { id: 2, first: 'Joe' }, { id: 3, first: 'Moe' }];
2800let managers = [{ manager: 1, employees: [2, 3] }];
2801// We use function here because we want a bindable reference, but a closure referencing the hash would work, too.
2802managers.forEach(
2803 manager =>
2804 (manager.employees = manager.employees.map(function(id) {
2805 return this[id];
2806 }, toHash(users, 'id')))
2807);
2808managers; // [ { manager:1, employees: [ { id: 2, first: "Joe" }, { id: 3, first: "Moe" } ] } ]
2809```
2810
2811</details>
2812
2813<br>[⬆ Back to top](#contents)
2814
2815### union
2816
2817Returns every element that exists in any of the two arrays once.
2818
2819Create a `Set` with all values of `a` and `b` and convert to an array.
2820
2821```js
2822const union = (a, b) => Array.from(new Set([...a, ...b]));
2823```
2824
2825<details>
2826<summary>Examples</summary>
2827
2828```js
2829union([1, 2, 3], [4, 3, 2]); // [1,2,3,4]
2830```
2831
2832</details>
2833
2834<br>[⬆ Back to top](#contents)
2835
2836### unionBy
2837
2838Returns every element that exists in any of the two arrays once, after applying the provided function to each array element of both.
2839
2840Create a `Set` by applying all `fn` to all values of `a`.
2841Create a `Set` from `a` and all elements in `b` whose value, after applying `fn` does not match a value in the previously created set.
2842Return the last set converted to an array.
2843
2844```js
2845const unionBy = (a, b, fn) => {
2846 const s = new Set(a.map(fn));
2847 return Array.from(new Set([...a, ...b.filter(x => !s.has(fn(x)))]));
2848};
2849```
2850
2851<details>
2852<summary>Examples</summary>
2853
2854```js
2855unionBy([2.1], [1.2, 2.3], Math.floor); // [2.1, 1.2]
2856```
2857
2858</details>
2859
2860<br>[⬆ Back to top](#contents)
2861
2862### unionWith
2863
2864Returns every element that exists in any of the two arrays once, using a provided comparator function.
2865
2866Create a `Set` with all values of `a` and values in `b` for which the comparator finds no matches in `a`, using `Array.prototype.findIndex()`.
2867
2868```js
2869const unionWith = (a, b, comp) =>
2870 Array.from(new Set([...a, ...b.filter(x => a.findIndex(y => comp(x, y)) === -1)]));
2871```
2872
2873<details>
2874<summary>Examples</summary>
2875
2876```js
2877unionWith([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]
2878```
2879
2880</details>
2881
2882<br>[⬆ Back to top](#contents)
2883
2884### uniqueElements
2885
2886Returns all unique values of an array.
2887
2888Use ES6 `Set` and the `...rest` operator to discard all duplicated values.
2889
2890```js
2891const uniqueElements = arr => [...new Set(arr)];
2892```
2893
2894<details>
2895<summary>Examples</summary>
2896
2897```js
2898uniqueElements([1, 2, 2, 3, 4, 4, 5]); // [1, 2, 3, 4, 5]
2899```
2900
2901</details>
2902
2903<br>[⬆ Back to top](#contents)
2904
2905### uniqueElementsBy
2906
2907Returns all unique values of an array, based on a provided comparator function.
2908
2909Use `Array.prototype.reduce()` and `Array.prototype.some()` for an array containing only the first unique occurence of each value, based on the comparator function, `fn`.
2910The comparator function takes two arguments: the values of the two elements being compared.
2911
2912```js
2913const uniqueElementsBy = (arr, fn) =>
2914 arr.reduce((acc, v) => {
2915 if (!acc.some(x => fn(v, x))) acc.push(v);
2916 return acc;
2917 }, []);
2918```
2919
2920<details>
2921<summary>Examples</summary>
2922
2923```js
2924uniqueElementsBy(
2925 [
2926 { id: 0, value: 'a' },
2927 { id: 1, value: 'b' },
2928 { id: 2, value: 'c' },
2929 { id: 1, value: 'd' },
2930 { id: 0, value: 'e' }
2931 ],
2932 (a, b) => a.id == b.id
2933); // [ { id: 0, value: 'a' }, { id: 1, value: 'b' }, { id: 2, value: 'c' } ]
2934```
2935
2936</details>
2937
2938<br>[⬆ Back to top](#contents)
2939
2940### uniqueElementsByRight
2941
2942Returns all unique values of an array, based on a provided comparator function.
2943
2944Use `Array.prototype.reduce()` and `Array.prototype.some()` for an array containing only the last unique occurence of each value, based on the comparator function, `fn`.
2945The comparator function takes two arguments: the values of the two elements being compared.
2946
2947```js
2948const uniqueElementsByRight = (arr, fn) =>
2949 arr.reduceRight((acc, v) => {
2950 if (!acc.some(x => fn(v, x))) acc.push(v);
2951 return acc;
2952 }, []);
2953```
2954
2955<details>
2956<summary>Examples</summary>
2957
2958```js
2959uniqueElementsByRight(
2960 [
2961 { id: 0, value: 'a' },
2962 { id: 1, value: 'b' },
2963 { id: 2, value: 'c' },
2964 { id: 1, value: 'd' },
2965 { id: 0, value: 'e' }
2966 ],
2967 (a, b) => a.id == b.id
2968); // [ { id: 0, value: 'e' }, { id: 1, value: 'd' }, { id: 2, value: 'c' } ]
2969```
2970
2971</details>
2972
2973<br>[⬆ Back to top](#contents)
2974
2975### uniqueSymmetricDifference
2976
2977Returns the unique symmetric difference between two arrays, not containing duplicate values from either array.
2978
2979Use `Array.prototype.filter()` and `Array.prototype.includes()` on each array to remove values contained in the other, then create a `Set` from the results, removing duplicate values.
2980
2981```js
2982const uniqueSymmetricDifference = (a, b) => [
2983 ...new Set([...a.filter(v => !b.includes(v)), ...b.filter(v => !a.includes(v))])
2984];
2985```
2986
2987<details>
2988<summary>Examples</summary>
2989
2990```js
2991uniqueSymmetricDifference([1, 2, 3], [1, 2, 4]); // [3, 4]
2992uniqueSymmetricDifference([1, 2, 2], [1, 3, 1]); // [2, 3]
2993```
2994
2995</details>
2996
2997<br>[⬆ Back to top](#contents)
2998
2999### unzip
3000
3001Creates an array of arrays, ungrouping the elements in an array produced by [zip](#zip).
3002
3003Use `Math.max.apply()` to get the longest subarray in the array, `Array.prototype.map()` to make each element an array.
3004Use `Array.prototype.reduce()` and `Array.prototype.forEach()` to map grouped values to individual arrays.
3005
3006```js
3007const unzip = arr =>
3008 arr.reduce(
3009 (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc),
3010 Array.from({
3011 length: Math.max(...arr.map(x => x.length))
3012 }).map(x => [])
3013 );
3014```
3015
3016<details>
3017<summary>Examples</summary>
3018
3019```js
3020unzip([['a', 1, true], ['b', 2, false]]); // [['a', 'b'], [1, 2], [true, false]]
3021unzip([['a', 1, true], ['b', 2]]); // [['a', 'b'], [1, 2], [true]]
3022```
3023
3024</details>
3025
3026<br>[⬆ Back to top](#contents)
3027
3028### unzipWith ![advanced](/advanced.svg)
3029
3030Creates an array of elements, ungrouping the elements in an array produced by [zip](#zip) and applying the provided function.
3031
3032Use `Math.max.apply()` to get the longest subarray in the array, `Array.prototype.map()` to make each element an array.
3033Use `Array.prototype.reduce()` and `Array.prototype.forEach()` to map grouped values to individual arrays.
3034Use `Array.prototype.map()` and the spread operator (`...`) to apply `fn` to each individual group of elements.
3035
3036```js
3037const unzipWith = (arr, fn) =>
3038 arr
3039 .reduce(
3040 (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc),
3041 Array.from({
3042 length: Math.max(...arr.map(x => x.length))
3043 }).map(x => [])
3044 )
3045 .map(val => fn(...val));
3046```
3047
3048<details>
3049<summary>Examples</summary>
3050
3051```js
3052unzipWith([[1, 10, 100], [2, 20, 200]], (...args) => args.reduce((acc, v) => acc + v, 0)); // [3, 30, 300]
3053```
3054
3055</details>
3056
3057<br>[⬆ Back to top](#contents)
3058
3059### without
3060
3061Filters out the elements of an array, that have one of the specified values.
3062
3063Use `Array.prototype.filter()` to create an array excluding(using `!Array.includes()`) all given values.
3064
3065_(For a snippet that mutates the original array see [`pull`](#pull))_
3066
3067```js
3068const without = (arr, ...args) => arr.filter(v => !args.includes(v));
3069```
3070
3071<details>
3072<summary>Examples</summary>
3073
3074```js
3075without([2, 1, 2, 3], 1, 2); // [3]
3076```
3077
3078</details>
3079
3080<br>[⬆ Back to top](#contents)
3081
3082### xProd
3083
3084Creates a new array out of the two supplied by creating each possible pair from the arrays.
3085
3086Use `Array.prototype.reduce()`, `Array.prototype.map()` and `Array.prototype.concat()` to produce every possible pair from the elements of the two arrays and save them in an array.
3087
3088```js
3089const xProd = (a, b) => a.reduce((acc, x) => acc.concat(b.map(y => [x, y])), []);
3090```
3091
3092<details>
3093<summary>Examples</summary>
3094
3095```js
3096xProd([1, 2], ['a', 'b']); // [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]
3097```
3098
3099</details>
3100
3101<br>[⬆ Back to top](#contents)
3102
3103### zip
3104
3105Creates an array of elements, grouped based on the position in the original arrays.
3106
3107Use `Math.max.apply()` to get the longest array in the arguments.
3108Creates an array with that length as return value and use `Array.from()` with a map-function to create an array of grouped elements.
3109If lengths of the argument-arrays vary, `undefined` is used where no value could be found.
3110
3111```js
3112const zip = (...arrays) => {
3113 const maxLength = Math.max(...arrays.map(x => x.length));
3114 return Array.from({ length: maxLength }).map((_, i) => {
3115 return Array.from({ length: arrays.length }, (_, k) => arrays[k][i]);
3116 });
3117};
3118```
3119
3120<details>
3121<summary>Examples</summary>
3122
3123```js
3124zip(['a', 'b'], [1, 2], [true, false]); // [['a', 1, true], ['b', 2, false]]
3125zip(['a'], [1, 2], [true, false]); // [['a', 1, true], [undefined, 2, false]]
3126```
3127
3128</details>
3129
3130<br>[⬆ Back to top](#contents)
3131
3132### zipObject
3133
3134Given an array of valid property identifiers and an array of values, return an object associating the properties to the values.
3135
3136Since an object can have undefined values but not undefined property pointers, the array of properties is used to decide the structure of the resulting object using `Array.prototype.reduce()`.
3137
3138```js
3139const zipObject = (props, values) =>
3140 props.reduce((obj, prop, index) => ((obj[prop] = values[index]), obj), {});
3141```
3142
3143<details>
3144<summary>Examples</summary>
3145
3146```js
3147zipObject(['a', 'b', 'c'], [1, 2]); // {a: 1, b: 2, c: undefined}
3148zipObject(['a', 'b'], [1, 2, 3]); // {a: 1, b: 2}
3149```
3150
3151</details>
3152
3153<br>[⬆ Back to top](#contents)
3154
3155### zipWith ![advanced](/advanced.svg)
3156
3157Creates an array of elements, grouped based on the position in the original arrays and using function as the last value to specify how grouped values should be combined.
3158
3159Check if the last argument provided is a function.
3160Use `Math.max()` to get the longest array in the arguments.
3161Creates an array with that length as return value and use `Array.from()` with a map-function to create an array of grouped elements.
3162If lengths of the argument-arrays vary, `undefined` is used where no value could be found.
3163The function is invoked with the elements of each group `(...group)`.
3164
3165```js
3166const zipWith = (...array) => {
3167 const fn = typeof array[array.length - 1] === 'function' ? array.pop() : undefined;
3168 return Array.from(
3169 { length: Math.max(...array.map(a => a.length)) },
3170 (_, i) => (fn ? fn(...array.map(a => a[i])) : array.map(a => a[i]))
3171 );
3172};
3173```
3174
3175<details>
3176<summary>Examples</summary>
3177
3178```js
3179zipWith([1, 2], [10, 20], [100, 200], (a, b, c) => a + b + c); // [111,222]
3180zipWith(
3181 [1, 2, 3],
3182 [10, 20],
3183 [100, 200],
3184 (a, b, c) => (a != null ? a : 'a') + (b != null ? b : 'b') + (c != null ? c : 'c')
3185); // [111, 222, '3bc']
3186```
3187
3188</details>
3189
3190<br>[⬆ Back to top](#contents)
3191
3192
3193---
3194
3195## 🌐 Browser
3196
3197### arrayToHtmlList
3198
3199Converts the given array elements into `<li>` tags and appends them to the list of the given id.
3200
3201Use `Array.prototype.map()`, `document.querySelector()`, and an anonymous inner closure to create a list of html tags.
3202
3203```js
3204const arrayToHtmlList = (arr, listID) =>
3205 (el => (
3206 (el = document.querySelector('#' + listID)),
3207 (el.innerHTML += arr.map(item => `<li>${item}</li>`).join(''))
3208 ))();
3209```
3210
3211<details>
3212<summary>Examples</summary>
3213
3214```js
3215arrayToHtmlList(['item 1', 'item 2'], 'myListID');
3216```
3217
3218</details>
3219
3220<br>[⬆ Back to top](#contents)
3221
3222### bottomVisible
3223
3224Returns `true` if the bottom of the page is visible, `false` otherwise.
3225
3226Use `scrollY`, `scrollHeight` and `clientHeight` to determine if the bottom of the page is visible.
3227
3228```js
3229const bottomVisible = () =>
3230 document.documentElement.clientHeight + window.scrollY >=
3231 (document.documentElement.scrollHeight || document.documentElement.clientHeight);
3232```
3233
3234<details>
3235<summary>Examples</summary>
3236
3237```js
3238bottomVisible(); // true
3239```
3240
3241</details>
3242
3243<br>[⬆ Back to top](#contents)
3244
3245### copyToClipboard ![advanced](/advanced.svg)
3246
3247⚠️ **NOTICE:** The same functionality can be easily implemented by using the new asynchronous Clipboard API, which is still experimental but should be used in the future instead of this snippet. Find out more about it [here](https://github.com/w3c/clipboard-apis/blob/master/explainer.adoc#writing-to-the-clipboard).
3248
3249Copy a string to the clipboard.
3250Only works as a result of user action (i.e. inside a `click` event listener).
3251
3252Create a new `<textarea>` element, fill it with the supplied data and add it to the HTML document.
3253Use `Selection.getRangeAt()`to store the selected range (if any).
3254Use `document.execCommand('copy')` to copy to the clipboard.
3255Remove the `<textarea>` element from the HTML document.
3256Finally, use `Selection().addRange()` to recover the original selected range (if any).
3257
3258```js
3259const copyToClipboard = str => {
3260 const el = document.createElement('textarea');
3261 el.value = str;
3262 el.setAttribute('readonly', '');
3263 el.style.position = 'absolute';
3264 el.style.left = '-9999px';
3265 document.body.appendChild(el);
3266 const selected =
3267 document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false;
3268 el.select();
3269 document.execCommand('copy');
3270 document.body.removeChild(el);
3271 if (selected) {
3272 document.getSelection().removeAllRanges();
3273 document.getSelection().addRange(selected);
3274 }
3275};
3276```
3277
3278<details>
3279<summary>Examples</summary>
3280
3281```js
3282copyToClipboard('Lorem ipsum'); // 'Lorem ipsum' copied to clipboard.
3283```
3284
3285</details>
3286
3287<br>[⬆ Back to top](#contents)
3288
3289### counter ![advanced](/advanced.svg)
3290
3291Creates a counter with the specified range, step and duration for the specified selector.
3292
3293Check if `step` has the proper sign and change it accordingly.
3294Use `setInterval()` in combination with `Math.abs()` and `Math.floor()` to calculate the time between each new text draw.
3295Use `document.querySelector().innerHTML` to update the value of the selected element.
3296Omit the fourth parameter, `step`, to use a default step of `1`.
3297Omit the fifth parameter, `duration`, to use a default duration of `2000`ms.
3298
3299```js
3300const counter = (selector, start, end, step = 1, duration = 2000) => {
3301 let current = start,
3302 _step = (end - start) * step < 0 ? -step : step,
3303 timer = setInterval(() => {
3304 current += _step;
3305 document.querySelector(selector).innerHTML = current;
3306 if (current >= end) document.querySelector(selector).innerHTML = end;
3307 if (current >= end) clearInterval(timer);
3308 }, Math.abs(Math.floor(duration / (end - start))));
3309 return timer;
3310};
3311```
3312
3313<details>
3314<summary>Examples</summary>
3315
3316```js
3317counter('#my-id', 1, 1000, 5, 2000); // Creates a 2-second timer for the element with id="my-id"
3318```
3319
3320</details>
3321
3322<br>[⬆ Back to top](#contents)
3323
3324### createElement
3325
3326Creates an element from a string (without appending it to the document).
3327If the given string contains multiple elements, only the first one will be returned.
3328
3329Use `document.createElement()` to create a new element.
3330Set its `innerHTML` to the string supplied as the argument.
3331Use `ParentNode.firstElementChild` to return the element version of the string.
3332
3333```js
3334const createElement = str => {
3335 const el = document.createElement('div');
3336 el.innerHTML = str;
3337 return el.firstElementChild;
3338};
3339```
3340
3341<details>
3342<summary>Examples</summary>
3343
3344```js
3345const el = createElement(
3346 `<div class="container">
3347 <p>Hello!</p>
3348 </div>`
3349);
3350console.log(el.className); // 'container'
3351```
3352
3353</details>
3354
3355<br>[⬆ Back to top](#contents)
3356
3357### createEventHub ![advanced](/advanced.svg)
3358
3359Creates a pub/sub ([publish–subscribe](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern)) event hub with `emit`, `on`, and `off` methods.
3360
3361Use `Object.create(null)` to create an empty `hub` object that does not inherit properties from `Object.prototype`.
3362For `emit`, resolve the array of handlers based on the `event` argument and then run each one with `Array.prototype.forEach()` by passing in the data as an argument.
3363For `on`, create an array for the event if it does not yet exist, then use `Array.prototype.push()` to add the handler
3364to the array.
3365For `off`, use `Array.prototype.findIndex()` to find the index of the handler in the event array and remove it using `Array.prototype.splice()`.
3366
3367```js
3368const createEventHub = () => ({
3369 hub: Object.create(null),
3370 emit(event, data) {
3371 (this.hub[event] || []).forEach(handler => handler(data));
3372 },
3373 on(event, handler) {
3374 if (!this.hub[event]) this.hub[event] = [];
3375 this.hub[event].push(handler);
3376 },
3377 off(event, handler) {
3378 const i = (this.hub[event] || []).findIndex(h => h === handler);
3379 if (i > -1) this.hub[event].splice(i, 1);
3380 }
3381});
3382```
3383
3384<details>
3385<summary>Examples</summary>
3386
3387```js
3388const handler = data => console.log(data);
3389const hub = createEventHub();
3390let increment = 0;
3391
3392// Subscribe: listen for different types of events
3393hub.on('message', handler);
3394hub.on('message', () => console.log('Message event fired'));
3395hub.on('increment', () => increment++);
3396
3397// Publish: emit events to invoke all handlers subscribed to them, passing the data to them as an argument
3398hub.emit('message', 'hello world'); // logs 'hello world' and 'Message event fired'
3399hub.emit('message', { hello: 'world' }); // logs the object and 'Message event fired'
3400hub.emit('increment'); // `increment` variable is now 1
3401
3402// Unsubscribe: stop a specific handler from listening to the 'message' event
3403hub.off('message', handler);
3404```
3405
3406</details>
3407
3408<br>[⬆ Back to top](#contents)
3409
3410### currentURL
3411
3412Returns the current URL.
3413
3414Use `window.location.href` to get current URL.
3415
3416```js
3417const currentURL = () => window.location.href;
3418```
3419
3420<details>
3421<summary>Examples</summary>
3422
3423```js
3424currentURL(); // 'https://google.com'
3425```
3426
3427</details>
3428
3429<br>[⬆ Back to top](#contents)
3430
3431### detectDeviceType
3432
3433Detects wether the website is being opened in a mobile device or a desktop/laptop.
3434
3435Use a regular expression to test the `navigator.userAgent` property to figure out if the device is a mobile device or a desktop/laptop.
3436
3437```js
3438const detectDeviceType = () =>
3439 /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
3440 ? 'Mobile'
3441 : 'Desktop';
3442```
3443
3444<details>
3445<summary>Examples</summary>
3446
3447```js
3448detectDeviceType(); // "Mobile" or "Desktop"
3449```
3450
3451</details>
3452
3453<br>[⬆ Back to top](#contents)
3454
3455### elementContains
3456
3457Returns `true` if the `parent` element contains the `child` element, `false` otherwise.
3458
3459Check that `parent` is not the same element as `child`, use `parent.contains(child)` to check if the `parent` element contains the `child` element.
3460
3461```js
3462const elementContains = (parent, child) => parent !== child && parent.contains(child);
3463```
3464
3465<details>
3466<summary>Examples</summary>
3467
3468```js
3469elementContains(document.querySelector('head'), document.querySelector('title')); // true
3470elementContains(document.querySelector('body'), document.querySelector('body')); // false
3471```
3472
3473</details>
3474
3475<br>[⬆ Back to top](#contents)
3476
3477### elementIsVisibleInViewport ![advanced](/advanced.svg)
3478
3479Returns `true` if the element specified is visible in the viewport, `false` otherwise.
3480
3481Use `Element.getBoundingClientRect()` and the `window.inner(Width|Height)` values
3482to determine if a given element is visible in the viewport.
3483Omit the second argument to determine if the element is entirely visible, or specify `true` to determine if
3484it is partially visible.
3485
3486```js
3487const elementIsVisibleInViewport = (el, partiallyVisible = false) => {
3488 const { top, left, bottom, right } = el.getBoundingClientRect();
3489 const { innerHeight, innerWidth } = window;
3490 return partiallyVisible
3491 ? ((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) &&
3492 ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))
3493 : top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;
3494};
3495```
3496
3497<details>
3498<summary>Examples</summary>
3499
3500```js
3501// e.g. 100x100 viewport and a 10x10px element at position {top: -1, left: 0, bottom: 9, right: 10}
3502elementIsVisibleInViewport(el); // false - (not fully visible)
3503elementIsVisibleInViewport(el, true); // true - (partially visible)
3504```
3505
3506</details>
3507
3508<br>[⬆ Back to top](#contents)
3509
3510### getImages
3511
3512Fetches all images from within an element and puts them into an array
3513
3514Use `Element.prototype.getElementsByTagName()` to fetch all `<img>` elements inside the provided element, `Array.prototype.map()` to map every `src` attribute of their respective `<img>` element, then create a `Set` to eliminate duplicates and return the array.
3515
3516```js
3517const getImages = (el, includeDuplicates = false) => {
3518 const images = [...el.getElementsByTagName('img')].map(img => img.getAttribute('src'));
3519 return includeDuplicates ? images : [...new Set(images)];
3520};
3521```
3522
3523<details>
3524<summary>Examples</summary>
3525
3526```js
3527getImages(document, true); // ['image1.jpg', 'image2.png', 'image1.png', '...']
3528getImages(document, false); // ['image1.jpg', 'image2.png', '...']
3529```
3530
3531</details>
3532
3533<br>[⬆ Back to top](#contents)
3534
3535### getScrollPosition
3536
3537Returns the scroll position of the current page.
3538
3539Use `pageXOffset` and `pageYOffset` if they are defined, otherwise `scrollLeft` and `scrollTop`.
3540You can omit `el` to use a default value of `window`.
3541
3542```js
3543const getScrollPosition = (el = window) => ({
3544 x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft,
3545 y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop
3546});
3547```
3548
3549<details>
3550<summary>Examples</summary>
3551
3552```js
3553getScrollPosition(); // {x: 0, y: 200}
3554```
3555
3556</details>
3557
3558<br>[⬆ Back to top](#contents)
3559
3560### getStyle
3561
3562Returns the value of a CSS rule for the specified element.
3563
3564Use `Window.getComputedStyle()` to get the value of the CSS rule for the specified element.
3565
3566```js
3567const getStyle = (el, ruleName) => getComputedStyle(el)[ruleName];
3568```
3569
3570<details>
3571<summary>Examples</summary>
3572
3573```js
3574getStyle(document.querySelector('p'), 'font-size'); // '16px'
3575```
3576
3577</details>
3578
3579<br>[⬆ Back to top](#contents)
3580
3581### hasClass
3582
3583Returns `true` if the element has the specified class, `false` otherwise.
3584
3585Use `element.classList.contains()` to check if the element has the specified class.
3586
3587```js
3588const hasClass = (el, className) => el.classList.contains(className);
3589```
3590
3591<details>
3592<summary>Examples</summary>
3593
3594```js
3595hasClass(document.querySelector('p.special'), 'special'); // true
3596```
3597
3598</details>
3599
3600<br>[⬆ Back to top](#contents)
3601
3602### hashBrowser ![advanced](/advanced.svg)
3603
3604Creates a hash for a value using the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) algorithm. Returns a promise.
3605
3606Use the [SubtleCrypto](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto) API to create a hash for the given value.
3607
3608```js
3609const hashBrowser = val =>
3610 crypto.subtle.digest('SHA-256', new TextEncoder('utf-8').encode(val)).then(h => {
3611 let hexes = [],
3612 view = new DataView(h);
3613 for (let i = 0; i < view.byteLength; i += 4)
3614 hexes.push(('00000000' + view.getUint32(i).toString(16)).slice(-8));
3615 return hexes.join('');
3616 });
3617```
3618
3619<details>
3620<summary>Examples</summary>
3621
3622```js
3623hashBrowser(JSON.stringify({ a: 'a', b: [1, 2, 3, 4], foo: { c: 'bar' } })).then(console.log); // '04aa106279f5977f59f9067fa9712afc4aedc6f5862a8defc34552d8c7206393'
3624```
3625
3626</details>
3627
3628<br>[⬆ Back to top](#contents)
3629
3630### hide
3631
3632Hides all the elements specified.
3633
3634Use `NodeList.prototype.forEach()` to apply `display: none` to each element specified.
3635
3636```js
3637const hide = (...el) => [...el].forEach(e => (e.style.display = 'none'));
3638```
3639
3640<details>
3641<summary>Examples</summary>
3642
3643```js
3644hide(document.querySelectorAll('img')); // Hides all <img> elements on the page
3645```
3646
3647</details>
3648
3649<br>[⬆ Back to top](#contents)
3650
3651### httpsRedirect
3652
3653Redirects 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.
3654
3655Use `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.prototype.split()` and remove the protocol part of the URL.
3656
3657```js
3658const httpsRedirect = () => {
3659 if (location.protocol !== 'https:') location.replace('https://' + location.href.split('//')[1]);
3660};
3661```
3662
3663<details>
3664<summary>Examples</summary>
3665
3666```js
3667httpsRedirect(); // If you are on http://mydomain.com, you are redirected to https://mydomain.com
3668```
3669
3670</details>
3671
3672<br>[⬆ Back to top](#contents)
3673
3674### insertAfter
3675
3676Inserts an HTML string after the end of the specified element.
3677
3678Use `el.insertAdjacentHTML()` with a position of `'afterend'` to parse `htmlString` and insert it after the end of `el`.
3679
3680```js
3681const insertAfter = (el, htmlString) => el.insertAdjacentHTML('afterend', htmlString);
3682```
3683
3684<details>
3685<summary>Examples</summary>
3686
3687```js
3688insertAfter(document.getElementById('myId'), '<p>after</p>'); // <div id="myId">...</div> <p>after</p>
3689```
3690
3691</details>
3692
3693<br>[⬆ Back to top](#contents)
3694
3695### insertBefore
3696
3697Inserts an HTML string before the start of the specified element.
3698
3699Use `el.insertAdjacentHTML()` with a position of `'beforebegin'` to parse `htmlString` and insert it before the start of `el`.
3700
3701```js
3702const insertBefore = (el, htmlString) => el.insertAdjacentHTML('beforebegin', htmlString);
3703```
3704
3705<details>
3706<summary>Examples</summary>
3707
3708```js
3709insertBefore(document.getElementById('myId'), '<p>before</p>'); // <p>before</p> <div id="myId">...</div>
3710```
3711
3712</details>
3713
3714<br>[⬆ Back to top](#contents)
3715
3716### isBrowserTabFocused
3717
3718Returns `true` if the browser tab of the page is focused, `false` otherwise.
3719
3720Use the `Document.hidden` property, introduced by the Page Visibility API to check if the browser tab of the page is visible or hidden.
3721
3722```js
3723const isBrowserTabFocused = () => !document.hidden;
3724```
3725
3726<details>
3727<summary>Examples</summary>
3728
3729```js
3730isBrowserTabFocused(); // true
3731```
3732
3733</details>
3734
3735<br>[⬆ Back to top](#contents)
3736
3737### nodeListToArray
3738
3739Converts a `NodeList` to an array.
3740
3741Use spread operator inside new array to convert a `NodeList` to an array.
3742
3743```js
3744const nodeListToArray = nodeList => [...nodeList];
3745```
3746
3747<details>
3748<summary>Examples</summary>
3749
3750```js
3751nodeListToArray(document.childNodes); // [ <!DOCTYPE html>, html ]
3752```
3753
3754</details>
3755
3756<br>[⬆ Back to top](#contents)
3757
3758### observeMutations ![advanced](/advanced.svg)
3759
3760Returns a new MutationObserver and runs the provided callback for each mutation on the specified element.
3761
3762Use a [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) to observe mutations on the given element.
3763Use `Array.prototype.forEach()` to run the callback for each mutation that is observed.
3764Omit the third argument, `options`, to use the default [options](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver#MutationObserverInit) (all `true`).
3765
3766```js
3767const observeMutations = (element, callback, options) => {
3768 const observer = new MutationObserver(mutations => mutations.forEach(m => callback(m)));
3769 observer.observe(
3770 element,
3771 Object.assign(
3772 {
3773 childList: true,
3774 attributes: true,
3775 attributeOldValue: true,
3776 characterData: true,
3777 characterDataOldValue: true,
3778 subtree: true
3779 },
3780 options
3781 )
3782 );
3783 return observer;
3784};
3785```
3786
3787<details>
3788<summary>Examples</summary>
3789
3790```js
3791const obs = observeMutations(document, console.log); // Logs all mutations that happen on the page
3792obs.disconnect(); // Disconnects the observer and stops logging mutations on the page
3793```
3794
3795</details>
3796
3797<br>[⬆ Back to top](#contents)
3798
3799### off
3800
3801Removes an event listener from an element.
3802
3803Use `EventTarget.removeEventListener()` to remove an event listener from an element.
3804Omit the fourth argument `opts` to use `false` or specify it based on the options used when the event listener was added.
3805
3806```js
3807const off = (el, evt, fn, opts = false) => el.removeEventListener(evt, fn, opts);
3808```
3809
3810<details>
3811<summary>Examples</summary>
3812
3813```js
3814const fn = () => console.log('!');
3815document.body.addEventListener('click', fn);
3816off(document.body, 'click', fn); // no longer logs '!' upon clicking on the page
3817```
3818
3819</details>
3820
3821<br>[⬆ Back to top](#contents)
3822
3823### on
3824
3825Adds an event listener to an element with the ability to use event delegation.
3826
3827Use `EventTarget.addEventListener()` to add an event listener to an element. If there is a `target` property supplied to the options object, ensure the event target matches the target specified and then invoke the callback by supplying the correct `this` context.
3828Returns a reference to the custom delegator function, in order to be possible to use with [`off`](#off).
3829Omit `opts` to default to non-delegation behavior and event bubbling.
3830
3831```js
3832const on = (el, evt, fn, opts = {}) => {
3833 const delegatorFn = e => e.target.matches(opts.target) && fn.call(e.target, e);
3834 el.addEventListener(evt, opts.target ? delegatorFn : fn, opts.options || false);
3835 if (opts.target) return delegatorFn;
3836};
3837```
3838
3839<details>
3840<summary>Examples</summary>
3841
3842```js
3843const fn = () => console.log('!');
3844on(document.body, 'click', fn); // logs '!' upon clicking the body
3845on(document.body, 'click', fn, { target: 'p' }); // logs '!' upon clicking a `p` element child of the body
3846on(document.body, 'click', fn, { options: true }); // use capturing instead of bubbling
3847```
3848
3849</details>
3850
3851<br>[⬆ Back to top](#contents)
3852
3853### onUserInputChange ![advanced](/advanced.svg)
3854
3855Run the callback whenever the user input type changes (`mouse` or `touch`). Useful for enabling/disabling code depending on the input device. This process is dynamic and works with hybrid devices (e.g. touchscreen laptops).
3856
3857Use two event listeners. Assume `mouse` input initially and bind a `touchstart` event listener to the document.
3858On `touchstart`, add a `mousemove` event listener to listen for two consecutive `mousemove` events firing within 20ms, using `performance.now()`.
3859Run the callback with the input type as an argument in either of these situations.
3860
3861```js
3862const onUserInputChange = callback => {
3863 let type = 'mouse',
3864 lastTime = 0;
3865 const mousemoveHandler = () => {
3866 const now = performance.now();
3867 if (now - lastTime < 20)
3868 (type = 'mouse'), callback(type), document.removeEventListener('mousemove', mousemoveHandler);
3869 lastTime = now;
3870 };
3871 document.addEventListener('touchstart', () => {
3872 if (type === 'touch') return;
3873 (type = 'touch'), callback(type), document.addEventListener('mousemove', mousemoveHandler);
3874 });
3875};
3876```
3877
3878<details>
3879<summary>Examples</summary>
3880
3881```js
3882onUserInputChange(type => {
3883 console.log('The user is now using', type, 'as an input method.');
3884});
3885```
3886
3887</details>
3888
3889<br>[⬆ Back to top](#contents)
3890
3891### prefix
3892
3893Returns the prefixed version (if necessary) of a CSS property that the browser supports.
3894
3895Use `Array.prototype.findIndex()` on an array of vendor prefix strings to test if `document.body` has one of them defined in its `CSSStyleDeclaration` object, otherwise return `null`.
3896Use `String.prototype.charAt()` and `String.prototype.toUpperCase()` to capitalize the property, which will be appended to the vendor prefix string.
3897
3898```js
3899const prefix = prop => {
3900 const capitalizedProp = prop.charAt(0).toUpperCase() + prop.slice(1);
3901 const prefixes = ['', 'webkit', 'moz', 'ms', 'o'];
3902 const i = prefixes.findIndex(
3903 prefix => typeof document.body.style[prefix ? prefix + capitalizedProp : prop] !== 'undefined'
3904 );
3905 return i !== -1 ? (i === 0 ? prop : prefixes[i] + capitalizedProp) : null;
3906};
3907```
3908
3909<details>
3910<summary>Examples</summary>
3911
3912```js
3913prefix('appearance'); // 'appearance' on a supported browser, otherwise 'webkitAppearance', 'mozAppearance', 'msAppearance' or 'oAppearance'
3914```
3915
3916</details>
3917
3918<br>[⬆ Back to top](#contents)
3919
3920### recordAnimationFrames
3921
3922Invokes the provided callback on each animation frame.
3923
3924Use recursion.
3925Provided that `running` is `true`, continue invoking `window.requestAnimationFrame()` which invokes the provided callback.
3926Return an object with two methods `start` and `stop` to allow manual control of the recording.
3927Omit the second argument, `autoStart`, to implicitly call `start` when the function is invoked.
3928
3929```js
3930const recordAnimationFrames = (callback, autoStart = true) => {
3931 let running = true,
3932 raf;
3933 const stop = () => {
3934 running = false;
3935 cancelAnimationFrame(raf);
3936 };
3937 const start = () => {
3938 running = true;
3939 run();
3940 };
3941 const run = () => {
3942 raf = requestAnimationFrame(() => {
3943 callback();
3944 if (running) run();
3945 });
3946 };
3947 if (autoStart) start();
3948 return { start, stop };
3949};
3950```
3951
3952<details>
3953<summary>Examples</summary>
3954
3955```js
3956const cb = () => console.log('Animation frame fired');
3957const recorder = recordAnimationFrames(cb); // logs 'Animation frame fired' on each animation frame
3958recorder.stop(); // stops logging
3959recorder.start(); // starts again
3960const recorder2 = recordAnimationFrames(cb, false); // `start` needs to be explicitly called to begin recording frames
3961```
3962
3963</details>
3964
3965<br>[⬆ Back to top](#contents)
3966
3967### redirect
3968
3969Redirects to a specified URL.
3970
3971Use `window.location.href` or `window.location.replace()` to redirect to `url`.
3972Pass a second argument to simulate a link click (`true` - default) or an HTTP redirect (`false`).
3973
3974```js
3975const redirect = (url, asLink = true) =>
3976 asLink ? (window.location.href = url) : window.location.replace(url);
3977```
3978
3979<details>
3980<summary>Examples</summary>
3981
3982```js
3983redirect('https://google.com');
3984```
3985
3986</details>
3987
3988<br>[⬆ Back to top](#contents)
3989
3990### runAsync ![advanced](/advanced.svg)
3991
3992Runs a function in a separate thread by using a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers), allowing long running functions to not block the UI.
3993
3994Create a new `Worker` using a `Blob` object URL, the contents of which should be the stringified version of the supplied function.
3995Immediately post the return value of calling the function back.
3996Return a promise, listening for `onmessage` and `onerror` events and resolving the data posted back from the worker, or throwing an error.
3997
3998```js
3999const runAsync = fn => {
4000 const worker = new Worker(
4001 URL.createObjectURL(new Blob([`postMessage((${fn})());`]), {
4002 type: 'application/javascript; charset=utf-8'
4003 })
4004 );
4005 return new Promise((res, rej) => {
4006 worker.onmessage = ({ data }) => {
4007 res(data), worker.terminate();
4008 };
4009 worker.onerror = err => {
4010 rej(err), worker.terminate();
4011 };
4012 });
4013};
4014```
4015
4016<details>
4017<summary>Examples</summary>
4018
4019```js
4020const longRunningFunction = () => {
4021 let result = 0;
4022 for (let i = 0; i < 1000; i++)
4023 for (let j = 0; j < 700; j++) for (let k = 0; k < 300; k++) result = result + i + j + k;
4024
4025 return result;
4026};
4027/*
4028 NOTE: Since the function is running in a different context, closures are not supported.
4029 The function supplied to `runAsync` gets stringified, so everything becomes literal.
4030 All variables and functions must be defined inside.
4031*/
4032runAsync(longRunningFunction).then(console.log); // 209685000000
4033runAsync(() => 10 ** 3).then(console.log); // 1000
4034let outsideVariable = 50;
4035runAsync(() => typeof outsideVariable).then(console.log); // 'undefined'
4036```
4037
4038</details>
4039
4040<br>[⬆ Back to top](#contents)
4041
4042### scrollToTop
4043
4044Smooth-scrolls to the top of the page.
4045
4046Get distance from top using `document.documentElement.scrollTop` or `document.body.scrollTop`.
4047Scroll by a fraction of the distance from the top. Use `window.requestAnimationFrame()` to animate the scrolling.
4048
4049```js
4050const scrollToTop = () => {
4051 const c = document.documentElement.scrollTop || document.body.scrollTop;
4052 if (c > 0) {
4053 window.requestAnimationFrame(scrollToTop);
4054 window.scrollTo(0, c - c / 8);
4055 }
4056};
4057```
4058
4059<details>
4060<summary>Examples</summary>
4061
4062```js
4063scrollToTop();
4064```
4065
4066</details>
4067
4068<br>[⬆ Back to top](#contents)
4069
4070### setStyle
4071
4072Sets the value of a CSS rule for the specified element.
4073
4074Use `element.style` to set the value of the CSS rule for the specified element to `val`.
4075
4076```js
4077const setStyle = (el, ruleName, val) => (el.style[ruleName] = val);
4078```
4079
4080<details>
4081<summary>Examples</summary>
4082
4083```js
4084setStyle(document.querySelector('p'), 'font-size', '20px'); // The first <p> element on the page will have a font-size of 20px
4085```
4086
4087</details>
4088
4089<br>[⬆ Back to top](#contents)
4090
4091### show
4092
4093Shows all the elements specified.
4094
4095Use the spread operator (`...`) and `Array.prototype.forEach()` to clear the `display` property for each element specified.
4096
4097```js
4098const show = (...el) => [...el].forEach(e => (e.style.display = ''));
4099```
4100
4101<details>
4102<summary>Examples</summary>
4103
4104```js
4105show(...document.querySelectorAll('img')); // Shows all <img> elements on the page
4106```
4107
4108</details>
4109
4110<br>[⬆ Back to top](#contents)
4111
4112### smoothScroll
4113
4114Smoothly scrolls the element on which it's called into the visible area of the browser window.
4115
4116Use `.scrollIntoView` method to scroll the element.
4117Pass `{ behavior: 'smooth' }` to `.scrollIntoView` so it scrolls smoothly.
4118
4119```js
4120const smoothScroll = element =>
4121 document.querySelector(element).scrollIntoView({
4122 behavior: 'smooth'
4123 });
4124```
4125
4126<details>
4127<summary>Examples</summary>
4128
4129```js
4130smoothScroll('#fooBar'); // scrolls smoothly to the element with the id fooBar
4131smoothScroll('.fooBar'); // scrolls smoothly to the first element with a class of fooBar
4132```
4133
4134</details>
4135
4136<br>[⬆ Back to top](#contents)
4137
4138### toggleClass
4139
4140Toggle a class for an element.
4141
4142Use `element.classList.toggle()` to toggle the specified class for the element.
4143
4144```js
4145const toggleClass = (el, className) => el.classList.toggle(className);
4146```
4147
4148<details>
4149<summary>Examples</summary>
4150
4151```js
4152toggleClass(document.querySelector('p.special'), 'special'); // The paragraph will not have the 'special' class anymore
4153```
4154
4155</details>
4156
4157<br>[⬆ Back to top](#contents)
4158
4159### triggerEvent
4160
4161Triggers a specific event on a given element, optionally passing custom data.
4162
4163Use `new CustomEvent()` to create an event from the specified `eventType` and details.
4164Use `el.dispatchEvent()` to trigger the newly created event on the given element.
4165Omit the third argument, `detail`, if you do not want to pass custom data to the triggered event.
4166
4167```js
4168const triggerEvent = (el, eventType, detail) =>
4169 el.dispatchEvent(new CustomEvent(eventType, { detail }));
4170```
4171
4172<details>
4173<summary>Examples</summary>
4174
4175```js
4176triggerEvent(document.getElementById('myId'), 'click');
4177triggerEvent(document.getElementById('myId'), 'click', { username: 'bob' });
4178```
4179
4180</details>
4181
4182<br>[⬆ Back to top](#contents)
4183
4184### UUIDGeneratorBrowser
4185
4186Generates a UUID in a browser.
4187
4188Use `crypto` API to generate a UUID, compliant with [RFC4122](https://www.ietf.org/rfc/rfc4122.txt) version 4.
4189
4190```js
4191const UUIDGeneratorBrowser = () =>
4192 ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
4193 (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)
4194 );
4195```
4196
4197<details>
4198<summary>Examples</summary>
4199
4200```js
4201UUIDGeneratorBrowser(); // '7982fcfe-5721-4632-bede-6000885be57d'
4202```
4203
4204</details>
4205
4206<br>[⬆ Back to top](#contents)
4207
4208
4209---
4210
4211## ⏱️ Date
4212
4213### dayOfYear
4214
4215Gets the day of the year from a `Date` object.
4216
4217Use `new Date()` and `Date.prototype.getFullYear()` to get the first day of the year as a `Date` object, subtract it from the provided `date` and divide with the milliseconds in each day to get the result.
4218Use `Math.floor()` to appropriately round the resulting day count to an integer.
4219
4220```js
4221const dayOfYear = date =>
4222 Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 1000 / 60 / 60 / 24);
4223```
4224
4225<details>
4226<summary>Examples</summary>
4227
4228```js
4229dayOfYear(new Date()); // 272
4230```
4231
4232</details>
4233
4234<br>[⬆ Back to top](#contents)
4235
4236### formatDuration
4237
4238Returns the human readable format of the given number of milliseconds.
4239
4240Divide `ms` with the appropriate values to obtain the appropriate values for `day`, `hour`, `minute`, `second` and `millisecond`.
4241Use `Object.entries()` with `Array.prototype.filter()` to keep only non-zero values.
4242Use `Array.prototype.map()` to create the string for each value, pluralizing appropriately.
4243Use `String.prototype.join(', ')` to combine the values into a string.
4244
4245```js
4246const formatDuration = ms => {
4247 if (ms < 0) ms = -ms;
4248 const time = {
4249 day: Math.floor(ms / 86400000),
4250 hour: Math.floor(ms / 3600000) % 24,
4251 minute: Math.floor(ms / 60000) % 60,
4252 second: Math.floor(ms / 1000) % 60,
4253 millisecond: Math.floor(ms) % 1000
4254 };
4255 return Object.entries(time)
4256 .filter(val => val[1] !== 0)
4257 .map(([key, val]) => `${val} ${key}${val !== 1 ? 's' : ''}`)
4258 .join(', ');
4259};
4260```
4261
4262<details>
4263<summary>Examples</summary>
4264
4265```js
4266formatDuration(1001); // '1 second, 1 millisecond'
4267formatDuration(34325055574); // '397 days, 6 hours, 44 minutes, 15 seconds, 574 milliseconds'
4268```
4269
4270</details>
4271
4272<br>[⬆ Back to top](#contents)
4273
4274### getColonTimeFromDate
4275
4276Returns a string of the form `HH:MM:SS` from a `Date` object.
4277
4278Use `Date.prototype.toTimeString()` and `String.prototype.slice()` to get the `HH:MM:SS` part of a given `Date` object.
4279
4280```js
4281const getColonTimeFromDate = date => date.toTimeString().slice(0, 8);
4282```
4283
4284<details>
4285<summary>Examples</summary>
4286
4287```js
4288getColonTimeFromDate(new Date()); // "08:38:00"
4289```
4290
4291</details>
4292
4293<br>[⬆ Back to top](#contents)
4294
4295### getDaysDiffBetweenDates
4296
4297Returns the difference (in days) between two dates.
4298
4299Calculate the difference (in days) between two `Date` objects.
4300
4301```js
4302const getDaysDiffBetweenDates = (dateInitial, dateFinal) =>
4303 (dateFinal - dateInitial) / (1000 * 3600 * 24);
4304```
4305
4306<details>
4307<summary>Examples</summary>
4308
4309```js
4310getDaysDiffBetweenDates(new Date('2017-12-13'), new Date('2017-12-22')); // 9
4311```
4312
4313</details>
4314
4315<br>[⬆ Back to top](#contents)
4316
4317### getMeridiemSuffixOfInteger
4318
4319Converts an integer to a suffixed string, adding `am` or `pm` based on its value.
4320
4321Use the modulo operator (`%`) and conditional checks to transform an integer to a stringified 12-hour format with meridiem suffix.
4322
4323```js
4324const getMeridiemSuffixOfInteger = num =>
4325 num === 0 || num === 24
4326 ? 12 + 'am'
4327 : num === 12
4328 ? 12 + 'pm'
4329 : num < 12
4330 ? (num % 12) + 'am'
4331 : (num % 12) + 'pm';
4332```
4333
4334<details>
4335<summary>Examples</summary>
4336
4337```js
4338getMeridiemSuffixOfInteger(0); // "12am"
4339getMeridiemSuffixOfInteger(11); // "11am"
4340getMeridiemSuffixOfInteger(13); // "1pm"
4341getMeridiemSuffixOfInteger(25); // "1pm"
4342```
4343
4344</details>
4345
4346<br>[⬆ Back to top](#contents)
4347
4348### isAfterDate
4349
4350Check if a date is after another date.
4351
4352Use the greater than operator (`>`) to check if the first date comes after the second one.
4353
4354```js
4355const isAfterDate = (dateA, dateB) => dateA > dateB;
4356```
4357
4358<details>
4359<summary>Examples</summary>
4360
4361```js
4362isAfterDate(new Date(2010, 10, 21), new Date(2010, 10, 20)); // true
4363```
4364
4365</details>
4366
4367<br>[⬆ Back to top](#contents)
4368
4369### isBeforeDate
4370
4371Check if a date is before another date.
4372
4373Use the less than operator (`<`) to check if the first date comes before the second one.
4374
4375```js
4376const isBeforeDate = (dateA, dateB) => dateA < dateB;
4377```
4378
4379<details>
4380<summary>Examples</summary>
4381
4382```js
4383isBeforeDate(new Date(2010, 10, 20), new Date(2010, 10, 21)); // true
4384```
4385
4386</details>
4387
4388<br>[⬆ Back to top](#contents)
4389
4390### isSameDate
4391
4392Check if a date is the same as another date.
4393
4394Use `Date.prototype.toISOString()` and strict equality checking (`===`) to check if the first date is the same as the second one.
4395
4396```js
4397const isSameDate = (dateA, dateB) => dateA.toISOString() === dateB.toISOString();
4398```
4399
4400<details>
4401<summary>Examples</summary>
4402
4403```js
4404isSameDate(new Date(2010, 10, 20), new Date(2010, 10, 20)); // true
4405```
4406
4407</details>
4408
4409<br>[⬆ Back to top](#contents)
4410
4411### maxDate
4412
4413Returns the maximum of the given dates.
4414
4415Use `Math.max.apply()` to find the maximum date value, `new Date()` to convert it to a `Date` object.
4416
4417```js
4418const maxDate = (...dates) => new Date(Math.max.apply(null, ...dates));
4419```
4420
4421<details>
4422<summary>Examples</summary>
4423
4424```js
4425const array = [
4426 new Date(2017, 4, 13),
4427 new Date(2018, 2, 12),
4428 new Date(2016, 0, 10),
4429 new Date(2016, 0, 9)
4430];
4431maxDate(array); // 2018-03-11T22:00:00.000Z
4432```
4433
4434</details>
4435
4436<br>[⬆ Back to top](#contents)
4437
4438### minDate
4439
4440Returns the minimum of the given dates.
4441
4442Use `Math.min.apply()` to find the minimum date value, `new Date()` to convert it to a `Date` object.
4443
4444```js
4445const minDate = (...dates) => new Date(Math.min.apply(null, ...dates));
4446```
4447
4448<details>
4449<summary>Examples</summary>
4450
4451```js
4452const array = [
4453 new Date(2017, 4, 13),
4454 new Date(2018, 2, 12),
4455 new Date(2016, 0, 10),
4456 new Date(2016, 0, 9)
4457];
4458minDate(array); // 2016-01-08T22:00:00.000Z
4459```
4460
4461</details>
4462
4463<br>[⬆ Back to top](#contents)
4464
4465### tomorrow
4466
4467Results in a string representation of tomorrow's date.
4468
4469Use `new Date()` to get today's date, adding one day using `Date.getDate()` and `Date.setDate()`, and converting the Date object to a string.
4470
4471```js
4472const tomorrow = (long = false) => {
4473 let t = new Date();
4474 t.setDate(t.getDate() + 1);
4475 const ret = `${t.getFullYear()}-${String(t.getMonth() + 1).padStart(2, '0')}-${String(
4476 t.getDate()
4477 ).padStart(2, '0')}`;
4478 return !long ? ret : `${ret}T00:00:00`;
4479};
4480```
4481
4482<details>
4483<summary>Examples</summary>
4484
4485```js
4486tomorrow(); // 2017-12-27 (if current date is 2017-12-26)
4487tomorrow(true); // 2017-12-27T00:00:00 (if current date is 2017-12-26)
4488```
4489
4490</details>
4491
4492<br>[⬆ Back to top](#contents)
4493
4494
4495---
4496
4497## 🎛️ Function
4498
4499### attempt
4500
4501Attempts to invoke a function with the provided arguments, returning either the result or the caught error object.
4502
4503Use a `try... catch` block to return either the result of the function or an appropriate error.
4504
4505```js
4506const attempt = (fn, ...args) => {
4507 try {
4508 return fn(...args);
4509 } catch (e) {
4510 return e instanceof Error ? e : new Error(e);
4511 }
4512};
4513```
4514
4515<details>
4516<summary>Examples</summary>
4517
4518```js
4519var elements = attempt(function(selector) {
4520 return document.querySelectorAll(selector);
4521}, '>_>');
4522if (elements instanceof Error) elements = []; // elements = []
4523```
4524
4525</details>
4526
4527<br>[⬆ Back to top](#contents)
4528
4529### bind
4530
4531Creates a function that invokes `fn` with a given context, optionally adding any additional supplied parameters to the beginning of the arguments.
4532
4533Return a `function` that uses `Function.prototype.apply()` to apply the given `context` to `fn`.
4534Use `Array.prototype.concat()` to prepend any additional supplied parameters to the arguments.
4535
4536```js
4537const bind = (fn, context, ...boundArgs) => (...args) => fn.apply(context, [...boundArgs, ...args]);
4538```
4539
4540<details>
4541<summary>Examples</summary>
4542
4543```js
4544function greet(greeting, punctuation) {
4545 return greeting + ' ' + this.user + punctuation;
4546}
4547const freddy = { user: 'fred' };
4548const freddyBound = bind(greet, freddy);
4549console.log(freddyBound('hi', '!')); // 'hi fred!'
4550```
4551
4552</details>
4553
4554<br>[⬆ Back to top](#contents)
4555
4556### bindKey
4557
4558Creates a function that invokes the method at a given key of an object, optionally adding any additional supplied parameters to the beginning of the arguments.
4559
4560Return a `function` that uses `Function.prototype.apply()` to bind `context[fn]` to `context`.
4561Use the spread operator (`...`) to prepend any additional supplied parameters to the arguments.
4562
4563```js
4564const bindKey = (context, fn, ...boundArgs) => (...args) =>
4565 context[fn].apply(context, [...boundArgs, ...args]);
4566```
4567
4568<details>
4569<summary>Examples</summary>
4570
4571```js
4572const freddy = {
4573 user: 'fred',
4574 greet: function(greeting, punctuation) {
4575 return greeting + ' ' + this.user + punctuation;
4576 }
4577};
4578const freddyBound = bindKey(freddy, 'greet');
4579console.log(freddyBound('hi', '!')); // 'hi fred!'
4580```
4581
4582</details>
4583
4584<br>[⬆ Back to top](#contents)
4585
4586### chainAsync
4587
4588Chains asynchronous functions.
4589
4590Loop through an array of functions containing asynchronous events, calling `next` when each asynchronous event has completed.
4591
4592```js
4593const chainAsync = fns => {
4594 let curr = 0;
4595 const next = () => fns[curr++](next);
4596 next();
4597};
4598```
4599
4600<details>
4601<summary>Examples</summary>
4602
4603```js
4604chainAsync([
4605 next => {
4606 console.log('0 seconds');
4607 setTimeout(next, 1000);
4608 },
4609 next => {
4610 console.log('1 second');
4611 }
4612]);
4613```
4614
4615</details>
4616
4617<br>[⬆ Back to top](#contents)
4618
4619### compose
4620
4621Performs right-to-left function composition.
4622
4623Use `Array.prototype.reduce()` to perform right-to-left function composition.
4624The last (rightmost) function can accept one or more arguments; the remaining functions must be unary.
4625
4626```js
4627const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));
4628```
4629
4630<details>
4631<summary>Examples</summary>
4632
4633```js
4634const add5 = x => x + 5;
4635const multiply = (x, y) => x * y;
4636const multiplyAndAdd5 = compose(
4637 add5,
4638 multiply
4639);
4640multiplyAndAdd5(5, 2); // 15
4641```
4642
4643</details>
4644
4645<br>[⬆ Back to top](#contents)
4646
4647### composeRight
4648
4649Performs left-to-right function composition.
4650
4651Use `Array.prototype.reduce()` to perform left-to-right function composition.
4652The first (leftmost) function can accept one or more arguments; the remaining functions must be unary.
4653
4654```js
4655const composeRight = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)));
4656```
4657
4658<details>
4659<summary>Examples</summary>
4660
4661```js
4662const add = (x, y) => x + y;
4663const square = x => x * x;
4664const addAndSquare = composeRight(add, square);
4665addAndSquare(1, 2); // 9
4666```
4667
4668</details>
4669
4670<br>[⬆ Back to top](#contents)
4671
4672### converge
4673
4674Accepts a converging function and a list of branching functions and returns a function that applies each branching function to the arguments and the results of the branching functions are passed as arguments to the converging function.
4675
4676Use `Array.prototype.map()` and `Function.prototype.apply()` to apply each function to the given arguments.
4677Use the spread operator (`...`) to call `coverger` with the results of all other functions.
4678
4679```js
4680const converge = (converger, fns) => (...args) => converger(...fns.map(fn => fn.apply(null, args)));
4681```
4682
4683<details>
4684<summary>Examples</summary>
4685
4686```js
4687const average = converge((a, b) => a / b, [
4688 arr => arr.reduce((a, v) => a + v, 0),
4689 arr => arr.length
4690]);
4691average([1, 2, 3, 4, 5, 6, 7]); // 4
4692```
4693
4694</details>
4695
4696<br>[⬆ Back to top](#contents)
4697
4698### curry
4699
4700Curries a function.
4701
4702Use recursion.
4703If the number of provided arguments (`args`) is sufficient, call the passed function `fn`.
4704Otherwise, return a curried function `fn` that expects the rest of the arguments.
4705If you want to curry a function that accepts a variable number of arguments (a variadic function, e.g. `Math.min()`), you can optionally pass the number of arguments to the second parameter `arity`.
4706
4707```js
4708const curry = (fn, arity = fn.length, ...args) =>
4709 arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args);
4710```
4711
4712<details>
4713<summary>Examples</summary>
4714
4715```js
4716curry(Math.pow)(2)(10); // 1024
4717curry(Math.min, 3)(10)(50)(2); // 2
4718```
4719
4720</details>
4721
4722<br>[⬆ Back to top](#contents)
4723
4724### debounce
4725
4726Creates a debounced function that delays invoking the provided function until at least `ms` milliseconds have elapsed since the last time it was invoked.
4727
4728Each time the debounced function is invoked, clear the current pending timeout with `clearTimeout()` and use `setTimeout()` to create a new timeout that delays invoking the function until at least `ms` milliseconds has elapsed. Use `Function.prototype.apply()` to apply the `this` context to the function and provide the necessary arguments.
4729Omit the second argument, `ms`, to set the timeout at a default of 0 ms.
4730
4731```js
4732const debounce = (fn, ms = 0) => {
4733 let timeoutId;
4734 return function(...args) {
4735 clearTimeout(timeoutId);
4736 timeoutId = setTimeout(() => fn.apply(this, args), ms);
4737 };
4738};
4739```
4740
4741<details>
4742<summary>Examples</summary>
4743
4744```js
4745window.addEventListener(
4746 'resize',
4747 debounce(() => {
4748 console.log(window.innerWidth);
4749 console.log(window.innerHeight);
4750 }, 250)
4751); // Will log the window dimensions at most every 250ms
4752```
4753
4754</details>
4755
4756<br>[⬆ Back to top](#contents)
4757
4758### defer
4759
4760Defers invoking a function until the current call stack has cleared.
4761
4762Use `setTimeout()` with a timeout of 1ms to add a new event to the browser event queue and allow the rendering engine to complete its work. Use the spread (`...`) operator to supply the function with an arbitrary number of arguments.
4763
4764```js
4765const defer = (fn, ...args) => setTimeout(fn, 1, ...args);
4766```
4767
4768<details>
4769<summary>Examples</summary>
4770
4771```js
4772// Example A:
4773defer(console.log, 'a'), console.log('b'); // logs 'b' then 'a'
4774
4775// Example B:
4776document.querySelector('#someElement').innerHTML = 'Hello';
4777longRunningFunction(); // Browser will not update the HTML until this has finished
4778defer(longRunningFunction); // Browser will update the HTML then run the function
4779```
4780
4781</details>
4782
4783<br>[⬆ Back to top](#contents)
4784
4785### delay
4786
4787Invokes the provided function after `wait` milliseconds.
4788
4789Use `setTimeout()` to delay execution of `fn`.
4790Use the spread (`...`) operator to supply the function with an arbitrary number of arguments.
4791
4792```js
4793const delay = (fn, wait, ...args) => setTimeout(fn, wait, ...args);
4794```
4795
4796<details>
4797<summary>Examples</summary>
4798
4799```js
4800delay(
4801 function(text) {
4802 console.log(text);
4803 },
4804 1000,
4805 'later'
4806); // Logs 'later' after one second.
4807```
4808
4809</details>
4810
4811<br>[⬆ Back to top](#contents)
4812
4813### functionName
4814
4815Logs the name of a function.
4816
4817Use `console.debug()` and the `name` property of the passed method to log the method's name to the `debug` channel of the console.
4818
4819```js
4820const functionName = fn => (console.debug(fn.name), fn);
4821```
4822
4823<details>
4824<summary>Examples</summary>
4825
4826```js
4827functionName(Math.max); // max (logged in debug channel of console)
4828```
4829
4830</details>
4831
4832<br>[⬆ Back to top](#contents)
4833
4834### hz
4835
4836Returns the number of times a function executed per second.
4837`hz` is the unit for `hertz`, the unit of frequency defined as one cycle per second.
4838
4839Use `performance.now()` to get the difference in milliseconds before and after the iteration loop to calculate the time elapsed executing the function `iterations` times.
4840Return the number of cycles per second by converting milliseconds to seconds and dividing it by the time elapsed.
4841Omit the second argument, `iterations`, to use the default of 100 iterations.
4842
4843```js
4844const hz = (fn, iterations = 100) => {
4845 const before = performance.now();
4846 for (let i = 0; i < iterations; i++) fn();
4847 return (1000 * iterations) / (performance.now() - before);
4848};
4849```
4850
4851<details>
4852<summary>Examples</summary>
4853
4854```js
4855// 10,000 element array
4856const numbers = Array(10000)
4857 .fill()
4858 .map((_, i) => i);
4859
4860// Test functions with the same goal: sum up the elements in the array
4861const sumReduce = () => numbers.reduce((acc, n) => acc + n, 0);
4862const sumForLoop = () => {
4863 let sum = 0;
4864 for (let i = 0; i < numbers.length; i++) sum += numbers[i];
4865 return sum;
4866};
4867
4868// `sumForLoop` is nearly 10 times faster
4869Math.round(hz(sumReduce)); // 572
4870Math.round(hz(sumForLoop)); // 4784
4871```
4872
4873</details>
4874
4875<br>[⬆ Back to top](#contents)
4876
4877### memoize ![advanced](/advanced.svg)
4878
4879Returns the memoized (cached) function.
4880
4881Create an empty cache by instantiating a new `Map` object.
4882Return 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.
4883Allow access to the `cache` by setting it as a property on the returned function.
4884
4885```js
4886const memoize = fn => {
4887 const cache = new Map();
4888 const cached = function(val) {
4889 return cache.has(val) ? cache.get(val) : cache.set(val, fn.call(this, val)) && cache.get(val);
4890 };
4891 cached.cache = cache;
4892 return cached;
4893};
4894```
4895
4896<details>
4897<summary>Examples</summary>
4898
4899```js
4900// See the `anagrams` snippet.
4901const anagramsCached = memoize(anagrams);
4902anagramsCached('javascript'); // takes a long time
4903anagramsCached('javascript'); // returns virtually instantly since it's now cached
4904console.log(anagramsCached.cache); // The cached anagrams map
4905```
4906
4907</details>
4908
4909<br>[⬆ Back to top](#contents)
4910
4911### negate
4912
4913Negates a predicate function.
4914
4915Take a predicate function and apply the not operator (`!`) to it with its arguments.
4916
4917```js
4918const negate = func => (...args) => !func(...args);
4919```
4920
4921<details>
4922<summary>Examples</summary>
4923
4924```js
4925[1, 2, 3, 4, 5, 6].filter(negate(n => n % 2 === 0)); // [ 1, 3, 5 ]
4926```
4927
4928</details>
4929
4930<br>[⬆ Back to top](#contents)
4931
4932### once
4933
4934Ensures a function is called only once.
4935
4936Utilizing 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.
4937Allow the function to be supplied with an arbitrary number of arguments using the rest/spread (`...`) operator.
4938
4939```js
4940const once = fn => {
4941 let called = false;
4942 return function(...args) {
4943 if (called) return;
4944 called = true;
4945 return fn.apply(this, args);
4946 };
4947};
4948```
4949
4950<details>
4951<summary>Examples</summary>
4952
4953```js
4954const startApp = function(event) {
4955 console.log(this, event); // document.body, MouseEvent
4956};
4957document.body.addEventListener('click', once(startApp)); // only runs `startApp` once upon click
4958```
4959
4960</details>
4961
4962<br>[⬆ Back to top](#contents)
4963
4964### partial
4965
4966Creates a function that invokes `fn` with `partials` prepended to the arguments it receives.
4967
4968Use the spread operator (`...`) to prepend `partials` to the list of arguments of `fn`.
4969
4970```js
4971const partial = (fn, ...partials) => (...args) => fn(...partials, ...args);
4972```
4973
4974<details>
4975<summary>Examples</summary>
4976
4977```js
4978const greet = (greeting, name) => greeting + ' ' + name + '!';
4979const greetHello = partial(greet, 'Hello');
4980greetHello('John'); // 'Hello John!'
4981```
4982
4983</details>
4984
4985<br>[⬆ Back to top](#contents)
4986
4987### partialRight
4988
4989Creates a function that invokes `fn` with `partials` appended to the arguments it receives.
4990
4991Use the spread operator (`...`) to append `partials` to the list of arguments of `fn`.
4992
4993```js
4994const partialRight = (fn, ...partials) => (...args) => fn(...args, ...partials);
4995```
4996
4997<details>
4998<summary>Examples</summary>
4999
5000```js
5001const greet = (greeting, name) => greeting + ' ' + name + '!';
5002const greetJohn = partialRight(greet, 'John');
5003greetJohn('Hello'); // 'Hello John!'
5004```
5005
5006</details>
5007
5008<br>[⬆ Back to top](#contents)
5009
5010### runPromisesInSeries
5011
5012Runs an array of promises in series.
5013
5014Use `Array.prototype.reduce()` to create a promise chain, where each promise returns the next promise when resolved.
5015
5016```js
5017const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve());
5018```
5019
5020<details>
5021<summary>Examples</summary>
5022
5023```js
5024const delay = d => new Promise(r => setTimeout(r, d));
5025runPromisesInSeries([() => delay(1000), () => delay(2000)]); // Executes each promise sequentially, taking a total of 3 seconds to complete
5026```
5027
5028</details>
5029
5030<br>[⬆ Back to top](#contents)
5031
5032### sleep
5033
5034Delays the execution of an asynchronous function.
5035
5036Delay executing part of an `async` function, by putting it to sleep, returning a `Promise`.
5037
5038```js
5039const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
5040```
5041
5042<details>
5043<summary>Examples</summary>
5044
5045```js
5046async function sleepyWork() {
5047 console.log("I'm going to sleep for 1 second.");
5048 await sleep(1000);
5049 console.log('I woke up after 1 second.');
5050}
5051```
5052
5053</details>
5054
5055<br>[⬆ Back to top](#contents)
5056
5057### throttle ![advanced](/advanced.svg)
5058
5059Creates a throttled function that only invokes the provided function at most once per every `wait` milliseconds
5060
5061Use `setTimeout()` and `clearTimeout()` to throttle the given method, `fn`.
5062Use `Function.prototype.apply()` to apply the `this` context to the function and provide the necessary `arguments`.
5063Use `Date.now()` to keep track of the last time the throttled function was invoked.
5064Omit the second argument, `wait`, to set the timeout at a default of 0 ms.
5065
5066```js
5067const throttle = (fn, wait) => {
5068 let inThrottle, lastFn, lastTime;
5069 return function() {
5070 const context = this,
5071 args = arguments;
5072 if (!inThrottle) {
5073 fn.apply(context, args);
5074 lastTime = Date.now();
5075 inThrottle = true;
5076 } else {
5077 clearTimeout(lastFn);
5078 lastFn = setTimeout(function() {
5079 if (Date.now() - lastTime >= wait) {
5080 fn.apply(context, args);
5081 lastTime = Date.now();
5082 }
5083 }, Math.max(wait - (Date.now() - lastTime), 0));
5084 }
5085 };
5086};
5087```
5088
5089<details>
5090<summary>Examples</summary>
5091
5092```js
5093window.addEventListener(
5094 'resize',
5095 throttle(function(evt) {
5096 console.log(window.innerWidth);
5097 console.log(window.innerHeight);
5098 }, 250)
5099); // Will log the window dimensions at most every 250ms
5100```
5101
5102</details>
5103
5104<br>[⬆ Back to top](#contents)
5105
5106### times
5107
5108Iterates over a callback `n` times
5109
5110Use `Function.call()` to call `fn` `n` times or until it returns `false`.
5111Omit the last argument, `context`, to use an `undefined` object (or the global object in non-strict mode).
5112
5113```js
5114const times = (n, fn, context = undefined) => {
5115 let i = 0;
5116 while (fn.call(context, i) !== false && ++i < n) {}
5117};
5118```
5119
5120<details>
5121<summary>Examples</summary>
5122
5123```js
5124var output = '';
5125times(5, i => (output += i));
5126console.log(output); // 01234
5127```
5128
5129</details>
5130
5131<br>[⬆ Back to top](#contents)
5132
5133### uncurry
5134
5135Uncurries a function up to depth `n`.
5136
5137Return a variadic function.
5138Use `Array.prototype.reduce()` on the provided arguments to call each subsequent curry level of the function.
5139If the `length` of the provided arguments is less than `n` throw an error.
5140Otherwise, call `fn` with the proper amount of arguments, using `Array.prototype.slice(0, n)`.
5141Omit the second argument, `n`, to uncurry up to depth `1`.
5142
5143```js
5144const uncurry = (fn, n = 1) => (...args) => {
5145 const next = acc => args => args.reduce((x, y) => x(y), acc);
5146 if (n > args.length) throw new RangeError('Arguments too few!');
5147 return next(fn)(args.slice(0, n));
5148};
5149```
5150
5151<details>
5152<summary>Examples</summary>
5153
5154```js
5155const add = x => y => z => x + y + z;
5156const uncurriedAdd = uncurry(add, 3);
5157uncurriedAdd(1, 2, 3); // 6
5158```
5159
5160</details>
5161
5162<br>[⬆ Back to top](#contents)
5163
5164### unfold
5165
5166Builds an array, using an iterator function and an initial seed value.
5167
5168Use a `while` loop and `Array.prototype.push()` to call the function repeatedly until it returns `false`.
5169The iterator function accepts one argument (`seed`) and must always return an array with two elements ([`value`, `nextSeed`]) or `false` to terminate.
5170
5171```js
5172const unfold = (fn, seed) => {
5173 let result = [],
5174 val = [null, seed];
5175 while ((val = fn(val[1]))) result.push(val[0]);
5176 return result;
5177};
5178```
5179
5180<details>
5181<summary>Examples</summary>
5182
5183```js
5184var f = n => (n > 50 ? false : [-n, n + 10]);
5185unfold(f, 10); // [-10, -20, -30, -40, -50]
5186```
5187
5188</details>
5189
5190<br>[⬆ Back to top](#contents)
5191
5192### when
5193
5194Tests a value, `x`, against a predicate function. If `true`, return `fn(x)`. Else, return `x`.
5195
5196Return a function expecting a single value, `x`, that returns the appropriate value based on `pred`.
5197
5198```js
5199const when = (pred, whenTrue) => x => (pred(x) ? whenTrue(x) : x);
5200```
5201
5202<details>
5203<summary>Examples</summary>
5204
5205```js
5206const doubleEvenNumbers = when(x => x % 2 === 0, x => x * 2);
5207doubleEvenNumbers(2); // 4
5208doubleEvenNumbers(1); // 1
5209```
5210
5211</details>
5212
5213<br>[⬆ Back to top](#contents)
5214
5215
5216---
5217
5218## ➗ Math
5219
5220### approximatelyEqual
5221
5222Checks if two numbers are approximately equal to each other.
5223
5224Use `Math.abs()` to compare the absolute difference of the two values to `epsilon`.
5225Omit the third parameter, `epsilon`, to use a default value of `0.001`.
5226
5227```js
5228const approximatelyEqual = (v1, v2, epsilon = 0.001) => Math.abs(v1 - v2) < epsilon;
5229```
5230
5231<details>
5232<summary>Examples</summary>
5233
5234```js
5235approximatelyEqual(Math.PI / 2.0, 1.5708); // true
5236```
5237
5238</details>
5239
5240<br>[⬆ Back to top](#contents)
5241
5242### average
5243
5244Returns the average of two or more numbers.
5245
5246Use `Array.prototype.reduce()` to add each value to an accumulator, initialized with a value of `0`, divide by the `length` of the array.
5247
5248```js
5249const average = (...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length;
5250```
5251
5252<details>
5253<summary>Examples</summary>
5254
5255```js
5256average(...[1, 2, 3]); // 2
5257average(1, 2, 3); // 2
5258```
5259
5260</details>
5261
5262<br>[⬆ Back to top](#contents)
5263
5264### averageBy
5265
5266Returns the average of an array, after mapping each element to a value using the provided function.
5267
5268Use `Array.prototype.map()` to map each element to the value returned by `fn`, `Array.prototype.reduce()` to add each value to an accumulator, initialized with a value of `0`, divide by the `length` of the array.
5269
5270```js
5271const averageBy = (arr, fn) =>
5272 arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0) /
5273 arr.length;
5274```
5275
5276<details>
5277<summary>Examples</summary>
5278
5279```js
5280averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], o => o.n); // 5
5281averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], 'n'); // 5
5282```
5283
5284</details>
5285
5286<br>[⬆ Back to top](#contents)
5287
5288### binomialCoefficient
5289
5290Evaluates the binomial coefficient of two integers `n` and `k`.
5291
5292Use `Number.isNaN()` to check if any of the two values is `NaN`.
5293Check if `k` is less than `0`, greater than or equal to `n`, equal to `1` or `n - 1` and return the appropriate result.
5294Check if `n - k` is less than `k` and switch their values accordingly.
5295Loop from `2` through `k` and calculate the binomial coefficient.
5296Use `Math.round()` to account for rounding errors in the calculation.
5297
5298```js
5299const binomialCoefficient = (n, k) => {
5300 if (Number.isNaN(n) || Number.isNaN(k)) return NaN;
5301 if (k < 0 || k > n) return 0;
5302 if (k === 0 || k === n) return 1;
5303 if (k === 1 || k === n - 1) return n;
5304 if (n - k < k) k = n - k;
5305 let res = n;
5306 for (let j = 2; j <= k; j++) res *= (n - j + 1) / j;
5307 return Math.round(res);
5308};
5309```
5310
5311<details>
5312<summary>Examples</summary>
5313
5314```js
5315binomialCoefficient(8, 2); // 28
5316```
5317
5318</details>
5319
5320<br>[⬆ Back to top](#contents)
5321
5322### clampNumber
5323
5324Clamps `num` within the inclusive range specified by the boundary values `a` and `b`.
5325
5326If `num` falls within the range, return `num`.
5327Otherwise, return the nearest number in the range.
5328
5329```js
5330const clampNumber = (num, a, b) => Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b));
5331```
5332
5333<details>
5334<summary>Examples</summary>
5335
5336```js
5337clampNumber(2, 3, 5); // 3
5338clampNumber(1, -1, -5); // -1
5339```
5340
5341</details>
5342
5343<br>[⬆ Back to top](#contents)
5344
5345### degreesToRads
5346
5347Converts an angle from degrees to radians.
5348
5349Use `Math.PI` and the degree to radian formula to convert the angle from degrees to radians.
5350
5351```js
5352const degreesToRads = deg => (deg * Math.PI) / 180.0;
5353```
5354
5355<details>
5356<summary>Examples</summary>
5357
5358```js
5359degreesToRads(90.0); // ~1.5708
5360```
5361
5362</details>
5363
5364<br>[⬆ Back to top](#contents)
5365
5366### digitize
5367
5368Converts a number to an array of digits.
5369
5370Convert the number to a string, using the spread operator (`...`) to build an array.
5371Use `Array.prototype.map()` and `parseInt()` to transform each value to an integer.
5372
5373```js
5374const digitize = n => [...`${n}`].map(i => parseInt(i));
5375```
5376
5377<details>
5378<summary>Examples</summary>
5379
5380```js
5381digitize(123); // [1, 2, 3]
5382```
5383
5384</details>
5385
5386<br>[⬆ Back to top](#contents)
5387
5388### distance
5389
5390Returns the distance between two points.
5391
5392Use `Math.hypot()` to calculate the Euclidean distance between two points.
5393
5394```js
5395const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0);
5396```
5397
5398<details>
5399<summary>Examples</summary>
5400
5401```js
5402distance(1, 1, 2, 3); // 2.23606797749979
5403```
5404
5405</details>
5406
5407<br>[⬆ Back to top](#contents)
5408
5409### elo ![advanced](/advanced.svg)
5410
5411Computes the new ratings between two or more opponents using the [Elo rating system](https://en.wikipedia.org/wiki/Elo_rating_system). It takes an array
5412of pre-ratings and returns an array containing post-ratings.
5413The array should be ordered from best performer to worst performer (winner -> loser).
5414
5415Use the exponent `**` operator and math operators to compute the expected score (chance of winning).
5416of each opponent and compute the new rating for each.
5417Loop through the ratings, using each permutation to compute the post-Elo rating for each player in a pairwise fashion.
5418Omit the second argument to use the default `kFactor` of 32.
5419
5420```js
5421const elo = ([...ratings], kFactor = 32, selfRating) => {
5422 const [a, b] = ratings;
5423 const expectedScore = (self, opponent) => 1 / (1 + 10 ** ((opponent - self) / 400));
5424 const newRating = (rating, i) =>
5425 (selfRating || rating) + kFactor * (i - expectedScore(i ? a : b, i ? b : a));
5426 if (ratings.length === 2) return [newRating(a, 1), newRating(b, 0)];
5427
5428 for (let i = 0, len = ratings.length; i < len; i++) {
5429 let j = i;
5430 while (j < len - 1) {
5431 j++;
5432 [ratings[i], ratings[j]] = elo([ratings[i], ratings[j]], kFactor);
5433 }
5434 }
5435 return ratings;
5436};
5437```
5438
5439<details>
5440<summary>Examples</summary>
5441
5442```js
5443// Standard 1v1s
5444elo([1200, 1200]); // [1216, 1184]
5445elo([1200, 1200], 64); // [1232, 1168]
5446// 4 player FFA, all same rank
5447elo([1200, 1200, 1200, 1200]).map(Math.round); // [1246, 1215, 1185, 1154]
5448/*
5449For teams, each rating can adjusted based on own team's average rating vs.
5450average rating of opposing team, with the score being added to their
5451own individual rating by supplying it as the third argument.
5452*/
5453```
5454
5455</details>
5456
5457<br>[⬆ Back to top](#contents)
5458
5459### factorial
5460
5461Calculates the factorial of a number.
5462
5463Use recursion.
5464If `n` is less than or equal to `1`, return `1`.
5465Otherwise, return the product of `n` and the factorial of `n - 1`.
5466Throws an exception if `n` is a negative number.
5467
5468```js
5469const factorial = n =>
5470 n < 0
5471 ? (() => {
5472 throw new TypeError('Negative numbers are not allowed!');
5473 })()
5474 : n <= 1
5475 ? 1
5476 : n * factorial(n - 1);
5477```
5478
5479<details>
5480<summary>Examples</summary>
5481
5482```js
5483factorial(6); // 720
5484```
5485
5486</details>
5487
5488<br>[⬆ Back to top](#contents)
5489
5490### fibonacci
5491
5492Generates an array, containing the Fibonacci sequence, up until the nth term.
5493
5494Create an empty array of the specific length, initializing the first two values (`0` and `1`).
5495Use `Array.prototype.reduce()` to add values into the array, using the sum of the last two values, except for the first two.
5496
5497```js
5498const fibonacci = n =>
5499 Array.from({ length: n }).reduce(
5500 (acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i),
5501 []
5502 );
5503```
5504
5505<details>
5506<summary>Examples</summary>
5507
5508```js
5509fibonacci(6); // [0, 1, 1, 2, 3, 5]
5510```
5511
5512</details>
5513
5514<br>[⬆ Back to top](#contents)
5515
5516### gcd
5517
5518Calculates the greatest common divisor between two or more numbers/arrays.
5519
5520The inner `_gcd` function uses recursion.
5521Base case is when `y` equals `0`. In this case, return `x`.
5522Otherwise, return the GCD of `y` and the remainder of the division `x/y`.
5523
5524```js
5525const gcd = (...arr) => {
5526 const _gcd = (x, y) => (!y ? x : gcd(y, x % y));
5527 return [...arr].reduce((a, b) => _gcd(a, b));
5528};
5529```
5530
5531<details>
5532<summary>Examples</summary>
5533
5534```js
5535gcd(8, 36); // 4
5536gcd(...[12, 8, 32]); // 4
5537```
5538
5539</details>
5540
5541<br>[⬆ Back to top](#contents)
5542
5543### geometricProgression
5544
5545Initializes an array containing the numbers in the specified range where `start` and `end` are inclusive and the ratio between two terms is `step`.
5546Returns an error if `step` equals `1`.
5547
5548Use `Array.from()`, `Math.log()` and `Math.floor()` to create an array of the desired length, `Array.prototype.map()` to fill with the desired values in a range.
5549Omit the second argument, `start`, to use a default value of `1`.
5550Omit the third argument, `step`, to use a default value of `2`.
5551
5552```js
5553const geometricProgression = (end, start = 1, step = 2) =>
5554 Array.from({ length: Math.floor(Math.log(end / start) / Math.log(step)) + 1 }).map(
5555 (v, i) => start * step ** i
5556 );
5557```
5558
5559<details>
5560<summary>Examples</summary>
5561
5562```js
5563geometricProgression(256); // [1, 2, 4, 8, 16, 32, 64, 128, 256]
5564geometricProgression(256, 3); // [3, 6, 12, 24, 48, 96, 192]
5565geometricProgression(256, 1, 4); // [1, 4, 16, 64, 256]
5566```
5567
5568</details>
5569
5570<br>[⬆ Back to top](#contents)
5571
5572### hammingDistance
5573
5574Calculates the Hamming distance between two values.
5575
5576Use XOR operator (`^`) to find the bit difference between the two numbers, convert to a binary string using `toString(2)`.
5577Count and return the number of `1`s in the string, using `match(/1/g)`.
5578
5579```js
5580const hammingDistance = (num1, num2) => ((num1 ^ num2).toString(2).match(/1/g) || '').length;
5581```
5582
5583<details>
5584<summary>Examples</summary>
5585
5586```js
5587hammingDistance(2, 3); // 1
5588```
5589
5590</details>
5591
5592<br>[⬆ Back to top](#contents)
5593
5594### inRange
5595
5596Checks if the given number falls within the given range.
5597
5598Use arithmetic comparison to check if the given number is in the specified range.
5599If the second parameter, `end`, is not specified, the range is considered to be from `0` to `start`.
5600
5601```js
5602const inRange = (n, start, end = null) => {
5603 if (end && start > end) [end, start] = [start, end];
5604 return end == null ? n >= 0 && n < start : n >= start && n < end;
5605};
5606```
5607
5608<details>
5609<summary>Examples</summary>
5610
5611```js
5612inRange(3, 2, 5); // true
5613inRange(3, 4); // true
5614inRange(2, 3, 5); // false
5615inRange(3, 2); // false
5616```
5617
5618</details>
5619
5620<br>[⬆ Back to top](#contents)
5621
5622### isDivisible
5623
5624Checks if the first numeric argument is divisible by the second one.
5625
5626Use the modulo operator (`%`) to check if the remainder is equal to `0`.
5627
5628```js
5629const isDivisible = (dividend, divisor) => dividend % divisor === 0;
5630```
5631
5632<details>
5633<summary>Examples</summary>
5634
5635```js
5636isDivisible(6, 3); // true
5637```
5638
5639</details>
5640
5641<br>[⬆ Back to top](#contents)
5642
5643### isEven
5644
5645Returns `true` if the given number is even, `false` otherwise.
5646
5647Checks whether a number is odd or even using the modulo (`%`) operator.
5648Returns `true` if the number is even, `false` if the number is odd.
5649
5650```js
5651const isEven = num => num % 2 === 0;
5652```
5653
5654<details>
5655<summary>Examples</summary>
5656
5657```js
5658isEven(3); // false
5659```
5660
5661</details>
5662
5663<br>[⬆ Back to top](#contents)
5664
5665### isNegativeZero
5666
5667Checks if the given value is equal to negative zero (`-0`).
5668
5669Checks whether a passed value is equal to `0` and if `1` divided by the value equals `-Infinity`.
5670
5671```js
5672const isNegativeZero = val => val === 0 && 1 / val === -Infinity;
5673```
5674
5675<details>
5676<summary>Examples</summary>
5677
5678```js
5679isNegativeZero(-0); // true
5680isNegativeZero(0); // false
5681```
5682
5683</details>
5684
5685<br>[⬆ Back to top](#contents)
5686
5687### isPrime
5688
5689Checks if the provided integer is a prime number.
5690
5691Check numbers from `2` to the square root of the given number.
5692Return `false` if any of them divides the given number, else return `true`, unless the number is less than `2`.
5693
5694```js
5695const isPrime = num => {
5696 const boundary = Math.floor(Math.sqrt(num));
5697 for (var i = 2; i <= boundary; i++) if (num % i === 0) return false;
5698 return num >= 2;
5699};
5700```
5701
5702<details>
5703<summary>Examples</summary>
5704
5705```js
5706isPrime(11); // true
5707```
5708
5709</details>
5710
5711<br>[⬆ Back to top](#contents)
5712
5713### lcm
5714
5715Returns the least common multiple of two or more numbers.
5716
5717Use the greatest common divisor (GCD) formula and the fact that `lcm(x,y) = x * y / gcd(x,y)` to determine the least common multiple.
5718The GCD formula uses recursion.
5719
5720```js
5721const lcm = (...arr) => {
5722 const gcd = (x, y) => (!y ? x : gcd(y, x % y));
5723 const _lcm = (x, y) => (x * y) / gcd(x, y);
5724 return [...arr].reduce((a, b) => _lcm(a, b));
5725};
5726```
5727
5728<details>
5729<summary>Examples</summary>
5730
5731```js
5732lcm(12, 7); // 84
5733lcm(...[1, 3, 4, 5]); // 60
5734```
5735
5736</details>
5737
5738<br>[⬆ Back to top](#contents)
5739
5740### luhnCheck ![advanced](/advanced.svg)
5741
5742Implementation 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.
5743
5744Use `String.prototype.split('')`, `Array.prototype.reverse()` and `Array.prototype.map()` in combination with `parseInt()` to obtain an array of digits.
5745Use `Array.prototype.splice(0,1)` to obtain the last digit.
5746Use `Array.prototype.reduce()` to implement the Luhn Algorithm.
5747Return `true` if `sum` is divisible by `10`, `false` otherwise.
5748
5749
5750```js
5751const luhnCheck = num => {
5752 let arr = (num + '')
5753 .split('')
5754 .reverse()
5755 .map(x => parseInt(x));
5756 let lastDigit = arr.splice(0, 1)[0];
5757 let sum = arr.reduce((acc, val, i) => (i % 2 !== 0 ? acc + val : acc + ((val * 2) % 9) || 9), 0);
5758 sum += lastDigit;
5759 return sum % 10 === 0;
5760};
5761```
5762
5763<details>
5764<summary>Examples</summary>
5765
5766```js
5767luhnCheck('4485275742308327'); // true
5768luhnCheck(6011329933655299); // false
5769luhnCheck(123456789); // false
5770```
5771
5772</details>
5773
5774<br>[⬆ Back to top](#contents)
5775
5776### maxBy
5777
5778Returns the maximum value of an array, after mapping each element to a value using the provided function.
5779
5780Use `Array.prototype.map()` to map each element to the value returned by `fn`, `Math.max()` to get the maximum value.
5781
5782```js
5783const maxBy = (arr, fn) => Math.max(...arr.map(typeof fn === 'function' ? fn : val => val[fn]));
5784```
5785
5786<details>
5787<summary>Examples</summary>
5788
5789```js
5790maxBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], o => o.n); // 8
5791maxBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], 'n'); // 8
5792```
5793
5794</details>
5795
5796<br>[⬆ Back to top](#contents)
5797
5798### median
5799
5800Returns the median of an array of numbers.
5801
5802Find the middle of the array, use `Array.prototype.sort()` to sort the values.
5803Return the number at the midpoint if `length` is odd, otherwise the average of the two middle numbers.
5804
5805```js
5806const median = arr => {
5807 const mid = Math.floor(arr.length / 2),
5808 nums = [...arr].sort((a, b) => a - b);
5809 return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2;
5810};
5811```
5812
5813<details>
5814<summary>Examples</summary>
5815
5816```js
5817median([5, 6, 50, 1, -5]); // 5
5818```
5819
5820</details>
5821
5822<br>[⬆ Back to top](#contents)
5823
5824### minBy
5825
5826Returns the minimum value of an array, after mapping each element to a value using the provided function.
5827
5828Use `Array.prototype.map()` to map each element to the value returned by `fn`, `Math.min()` to get the maximum value.
5829
5830```js
5831const minBy = (arr, fn) => Math.min(...arr.map(typeof fn === 'function' ? fn : val => val[fn]));
5832```
5833
5834<details>
5835<summary>Examples</summary>
5836
5837```js
5838minBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], o => o.n); // 2
5839minBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], 'n'); // 2
5840```
5841
5842</details>
5843
5844<br>[⬆ Back to top](#contents)
5845
5846### percentile
5847
5848Uses the percentile formula to calculate how many numbers in the given array are less or equal to the given value.
5849
5850Use `Array.prototype.reduce()` to calculate how many numbers are below the value and how many are the same value and apply the percentile formula.
5851
5852```js
5853const percentile = (arr, val) =>
5854 (100 * arr.reduce((acc, v) => acc + (v < val ? 1 : 0) + (v === val ? 0.5 : 0), 0)) / arr.length;
5855```
5856
5857<details>
5858<summary>Examples</summary>
5859
5860```js
5861percentile([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 6); // 55
5862```
5863
5864</details>
5865
5866<br>[⬆ Back to top](#contents)
5867
5868### powerset
5869
5870Returns the powerset of a given array of numbers.
5871
5872Use `Array.prototype.reduce()` combined with `Array.prototype.map()` to iterate over elements and combine into an array containing all combinations.
5873
5874```js
5875const powerset = arr => arr.reduce((a, v) => a.concat(a.map(r => [v].concat(r))), [[]]);
5876```
5877
5878<details>
5879<summary>Examples</summary>
5880
5881```js
5882powerset([1, 2]); // [[], [1], [2], [2, 1]]
5883```
5884
5885</details>
5886
5887<br>[⬆ Back to top](#contents)
5888
5889### primes
5890
5891Generates primes up to a given number, using the Sieve of Eratosthenes.
5892
5893Generate an array from `2` to the given number. Use `Array.prototype.filter()` to filter out the values divisible by any number from `2` to the square root of the provided number.
5894
5895```js
5896const primes = num => {
5897 let arr = Array.from({ length: num - 1 }).map((x, i) => i + 2),
5898 sqroot = Math.floor(Math.sqrt(num)),
5899 numsTillSqroot = Array.from({ length: sqroot - 1 }).map((x, i) => i + 2);
5900 numsTillSqroot.forEach(x => (arr = arr.filter(y => y % x !== 0 || y === x)));
5901 return arr;
5902};
5903```
5904
5905<details>
5906<summary>Examples</summary>
5907
5908```js
5909primes(10); // [2,3,5,7]
5910```
5911
5912</details>
5913
5914<br>[⬆ Back to top](#contents)
5915
5916### radsToDegrees
5917
5918Converts an angle from radians to degrees.
5919
5920Use `Math.PI` and the radian to degree formula to convert the angle from radians to degrees.
5921
5922```js
5923const radsToDegrees = rad => (rad * 180.0) / Math.PI;
5924```
5925
5926<details>
5927<summary>Examples</summary>
5928
5929```js
5930radsToDegrees(Math.PI / 2); // 90
5931```
5932
5933</details>
5934
5935<br>[⬆ Back to top](#contents)
5936
5937### randomIntArrayInRange
5938
5939Returns an array of n random integers in the specified range.
5940
5941Use `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.
5942
5943```js
5944const randomIntArrayInRange = (min, max, n = 1) =>
5945 Array.from({ length: n }, () => Math.floor(Math.random() * (max - min + 1)) + min);
5946```
5947
5948<details>
5949<summary>Examples</summary>
5950
5951```js
5952randomIntArrayInRange(12, 35, 10); // [ 34, 14, 27, 17, 30, 27, 20, 26, 21, 14 ]
5953```
5954
5955</details>
5956
5957<br>[⬆ Back to top](#contents)
5958
5959### randomIntegerInRange
5960
5961Returns a random integer in the specified range.
5962
5963Use `Math.random()` to generate a random number and map it to the desired range, using `Math.floor()` to make it an integer.
5964
5965```js
5966const randomIntegerInRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
5967```
5968
5969<details>
5970<summary>Examples</summary>
5971
5972```js
5973randomIntegerInRange(0, 5); // 2
5974```
5975
5976</details>
5977
5978<br>[⬆ Back to top](#contents)
5979
5980### randomNumberInRange
5981
5982Returns a random number in the specified range.
5983
5984Use `Math.random()` to generate a random value, map it to the desired range using multiplication.
5985
5986```js
5987const randomNumberInRange = (min, max) => Math.random() * (max - min) + min;
5988```
5989
5990<details>
5991<summary>Examples</summary>
5992
5993```js
5994randomNumberInRange(2, 10); // 6.0211363285087005
5995```
5996
5997</details>
5998
5999<br>[⬆ Back to top](#contents)
6000
6001### round
6002
6003Rounds a number to a specified amount of digits.
6004
6005Use `Math.round()` and template literals to round the number to the specified number of digits.
6006Omit the second argument, `decimals` to round to an integer.
6007
6008```js
6009const round = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`);
6010```
6011
6012<details>
6013<summary>Examples</summary>
6014
6015```js
6016round(1.005, 2); // 1.01
6017```
6018
6019</details>
6020
6021<br>[⬆ Back to top](#contents)
6022
6023### sdbm
6024
6025Hashes the input string into a whole number.
6026
6027Use `String.prototype.split('')` and `Array.prototype.reduce()` to create a hash of the input string, utilizing bit shifting.
6028
6029```js
6030const sdbm = str => {
6031 let arr = str.split('');
6032 return arr.reduce(
6033 (hashCode, currentVal) =>
6034 (hashCode = currentVal.charCodeAt(0) + (hashCode << 6) + (hashCode << 16) - hashCode),
6035 0
6036 );
6037};
6038```
6039
6040<details>
6041<summary>Examples</summary>
6042
6043```js
6044sdbm('name'); // -3521204949
6045```
6046
6047</details>
6048
6049<br>[⬆ Back to top](#contents)
6050
6051### standardDeviation
6052
6053Returns the standard deviation of an array of numbers.
6054
6055Use `Array.prototype.reduce()` to calculate the mean, variance and the sum of the variance of the values, the variance of the values, then
6056determine the standard deviation.
6057You can omit the second argument to get the sample standard deviation or set it to `true` to get the population standard deviation.
6058
6059```js
6060const standardDeviation = (arr, usePopulation = false) => {
6061 const mean = arr.reduce((acc, val) => acc + val, 0) / arr.length;
6062 return Math.sqrt(
6063 arr.reduce((acc, val) => acc.concat((val - mean) ** 2), []).reduce((acc, val) => acc + val, 0) /
6064 (arr.length - (usePopulation ? 0 : 1))
6065 );
6066};
6067```
6068
6069<details>
6070<summary>Examples</summary>
6071
6072```js
6073standardDeviation([10, 2, 38, 23, 38, 23, 21]); // 13.284434142114991 (sample)
6074standardDeviation([10, 2, 38, 23, 38, 23, 21], true); // 12.29899614287479 (population)
6075```
6076
6077</details>
6078
6079<br>[⬆ Back to top](#contents)
6080
6081### sum
6082
6083Returns the sum of two or more numbers/arrays.
6084
6085Use `Array.prototype.reduce()` to add each value to an accumulator, initialized with a value of `0`.
6086
6087```js
6088const sum = (...arr) => [...arr].reduce((acc, val) => acc + val, 0);
6089```
6090
6091<details>
6092<summary>Examples</summary>
6093
6094```js
6095sum(1, 2, 3, 4); // 10
6096sum(...[1, 2, 3, 4]); // 10
6097```
6098
6099</details>
6100
6101<br>[⬆ Back to top](#contents)
6102
6103### sumBy
6104
6105Returns the sum of an array, after mapping each element to a value using the provided function.
6106
6107Use `Array.prototype.map()` to map each element to the value returned by `fn`, `Array.prototype.reduce()` to add each value to an accumulator, initialized with a value of `0`.
6108
6109```js
6110const sumBy = (arr, fn) =>
6111 arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0);
6112```
6113
6114<details>
6115<summary>Examples</summary>
6116
6117```js
6118sumBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], o => o.n); // 20
6119sumBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], 'n'); // 20
6120```
6121
6122</details>
6123
6124<br>[⬆ Back to top](#contents)
6125
6126### sumPower
6127
6128Returns the sum of the powers of all the numbers from `start` to `end` (both inclusive).
6129
6130Use `Array.prototype.fill()` to create an array of all the numbers in the target range, `Array.prototype.map()` and the exponent operator (`**`) to raise them to `power` and `Array.prototype.reduce()` to add them together.
6131Omit the second argument, `power`, to use a default power of `2`.
6132Omit the third argument, `start`, to use a default starting value of `1`.
6133
6134```js
6135const sumPower = (end, power = 2, start = 1) =>
6136 Array(end + 1 - start)
6137 .fill(0)
6138 .map((x, i) => (i + start) ** power)
6139 .reduce((a, b) => a + b, 0);
6140```
6141
6142<details>
6143<summary>Examples</summary>
6144
6145```js
6146sumPower(10); // 385
6147sumPower(10, 3); // 3025
6148sumPower(10, 3, 5); // 2925
6149```
6150
6151</details>
6152
6153<br>[⬆ Back to top](#contents)
6154
6155### toSafeInteger
6156
6157Converts a value to a safe integer.
6158
6159Use `Math.max()` and `Math.min()` to find the closest safe value.
6160Use `Math.round()` to convert to an integer.
6161
6162```js
6163const toSafeInteger = num =>
6164 Math.round(Math.max(Math.min(num, Number.MAX_SAFE_INTEGER), Number.MIN_SAFE_INTEGER));
6165```
6166
6167<details>
6168<summary>Examples</summary>
6169
6170```js
6171toSafeInteger('3.2'); // 3
6172toSafeInteger(Infinity); // 9007199254740991
6173```
6174
6175</details>
6176
6177<br>[⬆ Back to top](#contents)
6178
6179
6180---
6181
6182## 📦 Node
6183
6184### atob
6185
6186Decodes a string of data which has been encoded using base-64 encoding.
6187
6188Create a `Buffer` for the given string with base-64 encoding and use `Buffer.toString('binary')` to return the decoded string.
6189
6190```js
6191const atob = str => Buffer.from(str, 'base64').toString('binary');
6192```
6193
6194<details>
6195<summary>Examples</summary>
6196
6197```js
6198atob('Zm9vYmFy'); // 'foobar'
6199```
6200
6201</details>
6202
6203<br>[⬆ Back to top](#contents)
6204
6205### btoa
6206
6207Creates 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.
6208
6209Create a `Buffer` for the given string with binary encoding and use `Buffer.toString('base64')` to return the encoded string.
6210
6211```js
6212const btoa = str => Buffer.from(str, 'binary').toString('base64');
6213```
6214
6215<details>
6216<summary>Examples</summary>
6217
6218```js
6219btoa('foobar'); // 'Zm9vYmFy'
6220```
6221
6222</details>
6223
6224<br>[⬆ Back to top](#contents)
6225
6226### colorize
6227
6228Add special characters to text to print in color in the console (combined with `console.log()`).
6229
6230Use template literals and special characters to add the appropriate color code to the string output.
6231For background colors, add a special character that resets the background color at the end of the string.
6232
6233```js
6234const colorize = (...args) => ({
6235 black: `\x1b[30m${args.join(' ')}`,
6236 red: `\x1b[31m${args.join(' ')}`,
6237 green: `\x1b[32m${args.join(' ')}`,
6238 yellow: `\x1b[33m${args.join(' ')}`,
6239 blue: `\x1b[34m${args.join(' ')}`,
6240 magenta: `\x1b[35m${args.join(' ')}`,
6241 cyan: `\x1b[36m${args.join(' ')}`,
6242 white: `\x1b[37m${args.join(' ')}`,
6243 bgBlack: `\x1b[40m${args.join(' ')}\x1b[0m`,
6244 bgRed: `\x1b[41m${args.join(' ')}\x1b[0m`,
6245 bgGreen: `\x1b[42m${args.join(' ')}\x1b[0m`,
6246 bgYellow: `\x1b[43m${args.join(' ')}\x1b[0m`,
6247 bgBlue: `\x1b[44m${args.join(' ')}\x1b[0m`,
6248 bgMagenta: `\x1b[45m${args.join(' ')}\x1b[0m`,
6249 bgCyan: `\x1b[46m${args.join(' ')}\x1b[0m`,
6250 bgWhite: `\x1b[47m${args.join(' ')}\x1b[0m`
6251});
6252```
6253
6254<details>
6255<summary>Examples</summary>
6256
6257```js
6258console.log(colorize('foo').red); // 'foo' (red letters)
6259console.log(colorize('foo', 'bar').bgBlue); // 'foo bar' (blue background)
6260console.log(colorize(colorize('foo').yellow, colorize('foo').green).bgWhite); // 'foo bar' (first word in yellow letters, second word in green letters, white background for both)
6261```
6262
6263</details>
6264
6265<br>[⬆ Back to top](#contents)
6266
6267### hasFlags
6268
6269Check if the current process's arguments contain the specified flags.
6270
6271Use `Array.prototype.every()` and `Array.prototype.includes()` to check if `process.argv` contains all the specified flags.
6272Use a regular expression to test if the specified flags are prefixed with `-` or `--` and prefix them accordingly.
6273
6274```js
6275const hasFlags = (...flags) =>
6276 flags.every(flag => process.argv.includes(/^-{1,2}/.test(flag) ? flag : '--' + flag));
6277```
6278
6279<details>
6280<summary>Examples</summary>
6281
6282```js
6283// node myScript.js -s --test --cool=true
6284hasFlags('-s'); // true
6285hasFlags('--test', 'cool=true', '-s'); // true
6286hasFlags('special'); // false
6287```
6288
6289</details>
6290
6291<br>[⬆ Back to top](#contents)
6292
6293### hashNode
6294
6295Creates a hash for a value using the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) algorithm. Returns a promise.
6296
6297Use `crypto` API to create a hash for the given value.
6298
6299```js
6300const crypto = require('crypto');
6301const hashNode = val =>
6302 new Promise(resolve =>
6303 setTimeout(
6304 () =>
6305 resolve(
6306 crypto
6307 .createHash('sha256')
6308 .update(val)
6309 .digest('hex')
6310 ),
6311 0
6312 )
6313 );
6314```
6315
6316<details>
6317<summary>Examples</summary>
6318
6319```js
6320hashNode(JSON.stringify({ a: 'a', b: [1, 2, 3, 4], foo: { c: 'bar' } })).then(console.log); // '04aa106279f5977f59f9067fa9712afc4aedc6f5862a8defc34552d8c7206393'
6321```
6322
6323</details>
6324
6325<br>[⬆ Back to top](#contents)
6326
6327### isDuplexStream
6328
6329Checks if the given argument is a duplex (readable and writable) stream.
6330
6331Check if the value is different from `null`, use `typeof` to check if a value is of type `object` and the `pipe` property is of type `function`.
6332Additionally check if the `typeof` the `_read`, `_write` and `_readableState`, `_writableState` properties are `function` and `object` respectively.
6333
6334```js
6335const isDuplexStream = val =>
6336 val !== null &&
6337 typeof val === 'object' &&
6338 typeof val.pipe === 'function' &&
6339 typeof val._read === 'function' &&
6340 typeof val._readableState === 'object' &&
6341 typeof val._write === 'function' &&
6342 typeof val._writableState === 'object';
6343```
6344
6345<details>
6346<summary>Examples</summary>
6347
6348```js
6349const Stream = require('stream');
6350isDuplexStream(new Stream.Duplex()); // true
6351```
6352
6353</details>
6354
6355<br>[⬆ Back to top](#contents)
6356
6357### isReadableStream
6358
6359Checks if the given argument is a readable stream.
6360
6361Check if the value is different from `null`, use `typeof` to check if the value is of type `object` and the `pipe` property is of type `function`.
6362Additionally check if the `typeof` the `_read` and `_readableState` properties are `function` and `object` respectively.
6363
6364```js
6365const isReadableStream = val =>
6366 val !== null &&
6367 typeof val === 'object' &&
6368 typeof val.pipe === 'function' &&
6369 typeof val._read === 'function' &&
6370 typeof val._readableState === 'object';
6371```
6372
6373<details>
6374<summary>Examples</summary>
6375
6376```js
6377const fs = require('fs');
6378isReadableStream(fs.createReadStream('test.txt')); // true
6379```
6380
6381</details>
6382
6383<br>[⬆ Back to top](#contents)
6384
6385### isStream
6386
6387Checks if the given argument is a stream.
6388
6389Check if the value is different from `null`, use `typeof` to check if the value is of type `object` and the `pipe` property is of type `function`.
6390
6391```js
6392const isStream = val => val !== null && typeof val === 'object' && typeof val.pipe === 'function';
6393```
6394
6395<details>
6396<summary>Examples</summary>
6397
6398```js
6399const fs = require('fs');
6400isStream(fs.createReadStream('test.txt')); // true
6401```
6402
6403</details>
6404
6405<br>[⬆ Back to top](#contents)
6406
6407### isTravisCI
6408
6409Checks if the current environment is [Travis CI](https://travis-ci.org/).
6410
6411Checks if the current environment has the `TRAVIS` and `CI` environment variables ([reference](https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables)).
6412
6413```js
6414const isTravisCI = () => 'TRAVIS' in process.env && 'CI' in process.env;
6415```
6416
6417<details>
6418<summary>Examples</summary>
6419
6420```js
6421isTravisCI(); // true (if code is running on Travis CI)
6422```
6423
6424</details>
6425
6426<br>[⬆ Back to top](#contents)
6427
6428### isWritableStream
6429
6430Checks if the given argument is a writable stream.
6431
6432Check if the value is different from `null`, use `typeof` to check if the value is of type `object` and the `pipe` property is of type `function`.
6433Additionally check if the `typeof` the `_write` and `_writableState` properties are `function` and `object` respectively.
6434
6435```js
6436const isWritableStream = val =>
6437 val !== null &&
6438 typeof val === 'object' &&
6439 typeof val.pipe === 'function' &&
6440 typeof val._write === 'function' &&
6441 typeof val._writableState === 'object';
6442```
6443
6444<details>
6445<summary>Examples</summary>
6446
6447```js
6448const fs = require('fs');
6449isWritableStream(fs.createWriteStream('test.txt')); // true
6450```
6451
6452</details>
6453
6454<br>[⬆ Back to top](#contents)
6455
6456### JSONToFile
6457
6458Writes a JSON object to a file.
6459
6460Use `fs.writeFile()`, template literals and `JSON.stringify()` to write a `json` object to a `.json` file.
6461
6462```js
6463const fs = require('fs');
6464const JSONToFile = (obj, filename) =>
6465 fs.writeFile(`${filename}.json`, JSON.stringify(obj, null, 2));
6466```
6467
6468<details>
6469<summary>Examples</summary>
6470
6471```js
6472JSONToFile({ test: 'is passed' }, 'testJsonFile'); // writes the object to 'testJsonFile.json'
6473```
6474
6475</details>
6476
6477<br>[⬆ Back to top](#contents)
6478
6479### readFileLines
6480
6481Returns an array of lines from the specified file.
6482
6483Use `readFileSync` function in `fs` node package to create a `Buffer` from a file.
6484convert buffer to string using `toString(encoding)` function.
6485creating an array from contents of file by `split`ing file content line by line (each `\n`).
6486
6487```js
6488const fs = require('fs');
6489const readFileLines = filename =>
6490 fs
6491 .readFileSync(filename)
6492 .toString('UTF8')
6493 .split('\n');
6494```
6495
6496<details>
6497<summary>Examples</summary>
6498
6499```js
6500/*
6501contents of test.txt :
6502 line1
6503 line2
6504 line3
6505 ___________________________
6506*/
6507let arr = readFileLines('test.txt');
6508console.log(arr); // ['line1', 'line2', 'line3']
6509```
6510
6511
6512</details>
6513
6514<br>[⬆ Back to top](#contents)
6515
6516### untildify
6517
6518Converts a tilde path to an absolute path.
6519
6520Use `String.prototype.replace()` with a regular expression and `OS.homedir()` to replace the `~` in the start of the path with the home directory.
6521
6522```js
6523const untildify = str => str.replace(/^~($|\/|\\)/, `${require('os').homedir()}$1`);
6524```
6525
6526<details>
6527<summary>Examples</summary>
6528
6529```js
6530untildify('~/node'); // '/Users/aUser/node'
6531```
6532
6533</details>
6534
6535<br>[⬆ Back to top](#contents)
6536
6537### UUIDGeneratorNode
6538
6539Generates a UUID in Node.JS.
6540
6541Use `crypto` API to generate a UUID, compliant with [RFC4122](https://www.ietf.org/rfc/rfc4122.txt) version 4.
6542
6543```js
6544const crypto = require('crypto');
6545const UUIDGeneratorNode = () =>
6546 ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
6547 (c ^ (crypto.randomBytes(1)[0] & (15 >> (c / 4)))).toString(16)
6548 );
6549```
6550
6551<details>
6552<summary>Examples</summary>
6553
6554```js
6555UUIDGeneratorNode(); // '79c7c136-60ee-40a2-beb2-856f1feabefc'
6556```
6557
6558</details>
6559
6560<br>[⬆ Back to top](#contents)
6561
6562
6563---
6564
6565## 🗃️ Object
6566
6567### bindAll
6568
6569Binds methods of an object to the object itself, overwriting the existing method.
6570
6571Use `Array.prototype.forEach()` to return a `function` that uses `Function.prototype.apply()` to apply the given context (`obj`) to `fn` for each function specified.
6572
6573```js
6574const bindAll = (obj, ...fns) =>
6575 fns.forEach(
6576 fn => (
6577 (f = obj[fn]),
6578 (obj[fn] = function() {
6579 return f.apply(obj);
6580 })
6581 )
6582 );
6583```
6584
6585<details>
6586<summary>Examples</summary>
6587
6588```js
6589var view = {
6590 label: 'docs',
6591 click: function() {
6592 console.log('clicked ' + this.label);
6593 }
6594};
6595bindAll(view, 'click');
6596jQuery(element).on('click', view.click); // Logs 'clicked docs' when clicked.
6597```
6598
6599</details>
6600
6601<br>[⬆ Back to top](#contents)
6602
6603### deepClone
6604
6605Creates a deep clone of an object.
6606
6607Use recursion.
6608Use `Object.assign()` and an empty object (`{}`) to create a shallow clone of the original.
6609Use `Object.keys()` and `Array.prototype.forEach()` to determine which key-value pairs need to be deep cloned.
6610
6611```js
6612const deepClone = obj => {
6613 let clone = Object.assign({}, obj);
6614 Object.keys(clone).forEach(
6615 key => (clone[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
6616 );
6617 return Array.isArray(obj) ? (clone.length = obj.length) && Array.from(clone) : clone;
6618};
6619```
6620
6621<details>
6622<summary>Examples</summary>
6623
6624```js
6625const a = { foo: 'bar', obj: { a: 1, b: 2 } };
6626const b = deepClone(a); // a !== b, a.obj !== b.obj
6627```
6628
6629</details>
6630
6631<br>[⬆ Back to top](#contents)
6632
6633### deepFreeze
6634
6635Deep freezes an object.
6636
6637Calls `Object.freeze(obj)` recursively on all unfrozen properties of passed object that are `instanceof` object.
6638
6639```js
6640const deepFreeze = obj =>
6641 Object.keys(obj).forEach(
6642 prop =>
6643 !(obj[prop] instanceof Object) || Object.isFrozen(obj[prop]) ? null : deepFreeze(obj[prop])
6644 ) || Object.freeze(obj);
6645```
6646
6647<details>
6648<summary>Examples</summary>
6649
6650```js
6651'use strict';
6652
6653const o = deepFreeze([1, [2, 3]]);
6654
6655o[0] = 3; // not allowed
6656o[1][0] = 4; // not allowed as well
6657```
6658
6659</details>
6660
6661<br>[⬆ Back to top](#contents)
6662
6663### defaults
6664
6665Assigns default values for all properties in an object that are `undefined`.
6666
6667Use `Object.assign()` to create a new empty object and copy the original one to maintain key order, use `Array.prototype.reverse()` and the spread operator `...` to combine the default values from left to right, finally use `obj` again to overwrite properties that originally had a value.
6668
6669```js
6670const defaults = (obj, ...defs) => Object.assign({}, obj, ...defs.reverse(), obj);
6671```
6672
6673<details>
6674<summary>Examples</summary>
6675
6676```js
6677defaults({ a: 1 }, { b: 2 }, { b: 6 }, { a: 3 }); // { a: 1, b: 2 }
6678```
6679
6680</details>
6681
6682<br>[⬆ Back to top](#contents)
6683
6684### dig
6685
6686Returns the target value in a nested JSON object, based on the given key.
6687
6688Use the `in` operator to check if `target` exists in `obj`.
6689If found, return the value of `obj[target]`, otherwise use `Object.values(obj)` and `Array.prototype.reduce()` to recursively call `dig` on each nested object until the first matching key/value pair is found.
6690
6691```js
6692const dig = (obj, target) =>
6693 target in obj
6694 ? obj[target]
6695 : Object.values(obj).reduce((acc, val) => {
6696 if (acc !== undefined) return acc;
6697 if (typeof val === 'object') return dig(val, target);
6698 }, undefined);
6699```
6700
6701<details>
6702<summary>Examples</summary>
6703
6704```js
6705const data = {
6706 level1: {
6707 level2: {
6708 level3: 'some data'
6709 }
6710 }
6711};
6712dig(data, 'level3'); // 'some data'
6713dig(data, 'level4'); // undefined
6714```
6715
6716</details>
6717
6718<br>[⬆ Back to top](#contents)
6719
6720### equals ![advanced](/advanced.svg)
6721
6722Performs a deep comparison between two values to determine if they are equivalent.
6723
6724Check if the two values are identical, if they are both `Date` objects with the same time, using `Date.getTime()` or if they are both non-object values with an equivalent value (strict comparison).
6725Check if only one value is `null` or `undefined` or if their prototypes differ.
6726If none of the above conditions are met, use `Object.keys()` to check if both values have the same number of keys, then use `Array.prototype.every()` to check if every key in the first value exists in the second one and if they are equivalent by calling this method recursively.
6727
6728```js
6729const equals = (a, b) => {
6730 if (a === b) return true;
6731 if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
6732 if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')) return a === b;
6733 if (a === null || a === undefined || b === null || b === undefined) return false;
6734 if (a.prototype !== b.prototype) return false;
6735 let keys = Object.keys(a);
6736 if (keys.length !== Object.keys(b).length) return false;
6737 return keys.every(k => equals(a[k], b[k]));
6738};
6739```
6740
6741<details>
6742<summary>Examples</summary>
6743
6744```js
6745equals({ a: [2, { e: 3 }], b: [4], c: 'foo' }, { a: [2, { e: 3 }], b: [4], c: 'foo' }); // true
6746```
6747
6748</details>
6749
6750<br>[⬆ Back to top](#contents)
6751
6752### findKey
6753
6754Returns the first key that satisfies the provided testing function. Otherwise `undefined` is returned.
6755
6756Use `Object.keys(obj)` to get all the properties of the object, `Array.prototype.find()` to test the provided function for each key-value pair. The callback receives three arguments - the value, the key and the object.
6757
6758```js
6759const findKey = (obj, fn) => Object.keys(obj).find(key => fn(obj[key], key, obj));
6760```
6761
6762<details>
6763<summary>Examples</summary>
6764
6765```js
6766findKey(
6767 {
6768 barney: { age: 36, active: true },
6769 fred: { age: 40, active: false },
6770 pebbles: { age: 1, active: true }
6771 },
6772 o => o['active']
6773); // 'barney'
6774```
6775
6776</details>
6777
6778<br>[⬆ Back to top](#contents)
6779
6780### findLastKey
6781
6782Returns the last key that satisfies the provided testing function.
6783Otherwise `undefined` is returned.
6784
6785Use `Object.keys(obj)` to get all the properties of the object, `Array.prototype.reverse()` to reverse their order and `Array.prototype.find()` to test the provided function for each key-value pair.
6786The callback receives three arguments - the value, the key and the object.
6787
6788```js
6789const findLastKey = (obj, fn) =>
6790 Object.keys(obj)
6791 .reverse()
6792 .find(key => fn(obj[key], key, obj));
6793```
6794
6795<details>
6796<summary>Examples</summary>
6797
6798```js
6799findLastKey(
6800 {
6801 barney: { age: 36, active: true },
6802 fred: { age: 40, active: false },
6803 pebbles: { age: 1, active: true }
6804 },
6805 o => o['active']
6806); // 'pebbles'
6807```
6808
6809</details>
6810
6811<br>[⬆ Back to top](#contents)
6812
6813### flattenObject
6814
6815Flatten an object with the paths for keys.
6816
6817Use recursion.
6818Use `Object.keys(obj)` combined with `Array.prototype.reduce()` to convert every leaf node to a flattened path node.
6819If the value of a key is an object, the function calls itself with the appropriate `prefix` to create the path using `Object.assign()`.
6820Otherwise, it adds the appropriate prefixed key-value pair to the accumulator object.
6821You should always omit the second argument, `prefix`, unless you want every key to have a prefix.
6822
6823```js
6824const flattenObject = (obj, prefix = '') =>
6825 Object.keys(obj).reduce((acc, k) => {
6826 const pre = prefix.length ? prefix + '.' : '';
6827 if (typeof obj[k] === 'object') Object.assign(acc, flattenObject(obj[k], pre + k));
6828 else acc[pre + k] = obj[k];
6829 return acc;
6830 }, {});
6831```
6832
6833<details>
6834<summary>Examples</summary>
6835
6836```js
6837flattenObject({ a: { b: { c: 1 } }, d: 1 }); // { 'a.b.c': 1, d: 1 }
6838```
6839
6840</details>
6841
6842<br>[⬆ Back to top](#contents)
6843
6844### forOwn
6845
6846Iterates over all own properties of an object, running a callback for each one.
6847
6848Use `Object.keys(obj)` to get all the properties of the object, `Array.prototype.forEach()` to run the provided function for each key-value pair. The callback receives three arguments - the value, the key and the object.
6849
6850```js
6851const forOwn = (obj, fn) => Object.keys(obj).forEach(key => fn(obj[key], key, obj));
6852```
6853
6854<details>
6855<summary>Examples</summary>
6856
6857```js
6858forOwn({ foo: 'bar', a: 1 }, v => console.log(v)); // 'bar', 1
6859```
6860
6861</details>
6862
6863<br>[⬆ Back to top](#contents)
6864
6865### forOwnRight
6866
6867Iterates over all own properties of an object in reverse, running a callback for each one.
6868
6869Use `Object.keys(obj)` to get all the properties of the object, `Array.prototype.reverse()` to reverse their order and `Array.prototype.forEach()` to run the provided function for each key-value pair. The callback receives three arguments - the value, the key and the object.
6870
6871```js
6872const forOwnRight = (obj, fn) =>
6873 Object.keys(obj)
6874 .reverse()
6875 .forEach(key => fn(obj[key], key, obj));
6876```
6877
6878<details>
6879<summary>Examples</summary>
6880
6881```js
6882forOwnRight({ foo: 'bar', a: 1 }, v => console.log(v)); // 1, 'bar'
6883```
6884
6885</details>
6886
6887<br>[⬆ Back to top](#contents)
6888
6889### functions
6890
6891Returns an array of function property names from own (and optionally inherited) enumerable properties of an object.
6892
6893Use `Object.keys(obj)` to iterate over the object's own properties.
6894If `inherited` is `true`, use `Object.get.PrototypeOf(obj)` to also get the object's inherited properties.
6895Use `Array.prototype.filter()` to keep only those properties that are functions.
6896Omit the second argument, `inherited`, to not include inherited properties by default.
6897
6898```js
6899const functions = (obj, inherited = false) =>
6900 (inherited
6901 ? [...Object.keys(obj), ...Object.keys(Object.getPrototypeOf(obj))]
6902 : Object.keys(obj)
6903 ).filter(key => typeof obj[key] === 'function');
6904```
6905
6906<details>
6907<summary>Examples</summary>
6908
6909```js
6910function Foo() {
6911 this.a = () => 1;
6912 this.b = () => 2;
6913}
6914Foo.prototype.c = () => 3;
6915functions(new Foo()); // ['a', 'b']
6916functions(new Foo(), true); // ['a', 'b', 'c']
6917```
6918
6919</details>
6920
6921<br>[⬆ Back to top](#contents)
6922
6923### get
6924
6925Retrieve a set of properties indicated by the given selectors from an object.
6926
6927Use `Array.prototype.map()` for each selector, `String.prototype.replace()` to replace square brackets with dots, `String.prototype.split('.')` to split each selector, `Array.prototype.filter()` to remove empty values and `Array.prototype.reduce()` to get the value indicated by it.
6928
6929```js
6930const get = (from, ...selectors) =>
6931 [...selectors].map(s =>
6932 s
6933 .replace(/\[([^\[\]]*)\]/g, '.$1.')
6934 .split('.')
6935 .filter(t => t !== '')
6936 .reduce((prev, cur) => prev && prev[cur], from)
6937 );
6938```
6939
6940<details>
6941<summary>Examples</summary>
6942
6943```js
6944const obj = { selector: { to: { val: 'val to select' } }, target: [1, 2, { a: 'test' }] };
6945get(obj, 'selector.to.val', 'target[0]', 'target[2].a'); // ['val to select', 1, 'test']
6946```
6947
6948</details>
6949
6950<br>[⬆ Back to top](#contents)
6951
6952### invertKeyValues
6953
6954Inverts the key-value pairs of an object, without mutating it. The corresponding inverted value of each inverted key is an array of keys responsible for generating the inverted value. If a function is supplied, it is applied to each inverted key.
6955
6956Use `Object.keys()` and `Array.prototype.reduce()` to invert the key-value pairs of an object and apply the function provided (if any).
6957Omit the second argument, `fn`, to get the inverted keys without applying a function to them.
6958
6959```js
6960const invertKeyValues = (obj, fn) =>
6961 Object.keys(obj).reduce((acc, key) => {
6962 const val = fn ? fn(obj[key]) : obj[key];
6963 acc[val] = acc[val] || [];
6964 acc[val].push(key);
6965 return acc;
6966 }, {});
6967```
6968
6969<details>
6970<summary>Examples</summary>
6971
6972```js
6973invertKeyValues({ a: 1, b: 2, c: 1 }); // { 1: [ 'a', 'c' ], 2: [ 'b' ] }
6974invertKeyValues({ a: 1, b: 2, c: 1 }, value => 'group' + value); // { group1: [ 'a', 'c' ], group2: [ 'b' ] }
6975```
6976
6977</details>
6978
6979<br>[⬆ Back to top](#contents)
6980
6981### lowercaseKeys
6982
6983Creates a new object from the specified object, where all the keys are in lowercase.
6984
6985Use `Object.keys()` and `Array.prototype.reduce()` to create a new object from the specified object.
6986Convert each key in the original object to lowercase, using `String.toLowerCase()`.
6987
6988```js
6989const lowercaseKeys = obj =>
6990 Object.keys(obj).reduce((acc, key) => {
6991 acc[key.toLowerCase()] = obj[key];
6992 return acc;
6993 }, {});
6994```
6995
6996<details>
6997<summary>Examples</summary>
6998
6999```js
7000const myObj = { Name: 'Adam', sUrnAME: 'Smith' };
7001const myObjLower = lowercaseKeys(myObj); // {name: 'Adam', surname: 'Smith'};
7002```
7003
7004</details>
7005
7006<br>[⬆ Back to top](#contents)
7007
7008### mapKeys
7009
7010Creates an object with keys generated by running the provided function for each key and the same values as the provided object.
7011
7012Use `Object.keys(obj)` to iterate over the object's keys.
7013Use `Array.prototype.reduce()` to create a new object with the same values and mapped keys using `fn`.
7014
7015```js
7016const mapKeys = (obj, fn) =>
7017 Object.keys(obj).reduce((acc, k) => {
7018 acc[fn(obj[k], k, obj)] = obj[k];
7019 return acc;
7020 }, {});
7021```
7022
7023<details>
7024<summary>Examples</summary>
7025
7026```js
7027mapKeys({ a: 1, b: 2 }, (val, key) => key + val); // { a1: 1, b2: 2 }
7028```
7029
7030</details>
7031
7032<br>[⬆ Back to top](#contents)
7033
7034### mapValues
7035
7036Creates an object with the same keys as the provided object and values generated by running the provided function for each value.
7037
7038Use `Object.keys(obj)` to iterate over the object's keys.
7039Use `Array.prototype.reduce()` to create a new object with the same keys and mapped values using `fn`.
7040
7041```js
7042const mapValues = (obj, fn) =>
7043 Object.keys(obj).reduce((acc, k) => {
7044 acc[k] = fn(obj[k], k, obj);
7045 return acc;
7046 }, {});
7047```
7048
7049<details>
7050<summary>Examples</summary>
7051
7052```js
7053const users = {
7054 fred: { user: 'fred', age: 40 },
7055 pebbles: { user: 'pebbles', age: 1 }
7056};
7057mapValues(users, u => u.age); // { fred: 40, pebbles: 1 }
7058```
7059
7060</details>
7061
7062<br>[⬆ Back to top](#contents)
7063
7064### matches
7065
7066Compares two objects to determine if the first one contains equivalent property values to the second one.
7067
7068Use `Object.keys(source)` to get all the keys of the second object, then `Array.prototype.every()`, `Object.hasOwnProperty()` and strict comparison to determine if all keys exist in the first object and have the same values.
7069
7070```js
7071const matches = (obj, source) =>
7072 Object.keys(source).every(key => obj.hasOwnProperty(key) && obj[key] === source[key]);
7073```
7074
7075<details>
7076<summary>Examples</summary>
7077
7078```js
7079matches({ age: 25, hair: 'long', beard: true }, { hair: 'long', beard: true }); // true
7080matches({ hair: 'long', beard: true }, { age: 25, hair: 'long', beard: true }); // false
7081```
7082
7083</details>
7084
7085<br>[⬆ Back to top](#contents)
7086
7087### matchesWith
7088
7089Compares two objects to determine if the first one contains equivalent property values to the second one, based on a provided function.
7090
7091Use `Object.keys(source)` to get all the keys of the second object, then `Array.prototype.every()`, `Object.hasOwnProperty()` and the provided function to determine if all keys exist in the first object and have equivalent values.
7092If no function is provided, the values will be compared using the equality operator.
7093
7094```js
7095const matchesWith = (obj, source, fn) =>
7096 Object.keys(source).every(
7097 key =>
7098 obj.hasOwnProperty(key) && fn
7099 ? fn(obj[key], source[key], key, obj, source)
7100 : obj[key] == source[key]
7101 );
7102```
7103
7104<details>
7105<summary>Examples</summary>
7106
7107```js
7108const isGreeting = val => /^h(?:i|ello)$/.test(val);
7109matchesWith(
7110 { greeting: 'hello' },
7111 { greeting: 'hi' },
7112 (oV, sV) => isGreeting(oV) && isGreeting(sV)
7113); // true
7114```
7115
7116</details>
7117
7118<br>[⬆ Back to top](#contents)
7119
7120### merge
7121
7122Creates a new object from the combination of two or more objects.
7123
7124Use `Array.prototype.reduce()` combined with `Object.keys(obj)` to iterate over all objects and keys.
7125Use `hasOwnProperty()` and `Array.prototype.concat()` to append values for keys existing in multiple objects.
7126
7127```js
7128const merge = (...objs) =>
7129 [...objs].reduce(
7130 (acc, obj) =>
7131 Object.keys(obj).reduce((a, k) => {
7132 acc[k] = acc.hasOwnProperty(k) ? [].concat(acc[k]).concat(obj[k]) : obj[k];
7133 return acc;
7134 }, {}),
7135 {}
7136 );
7137```
7138
7139<details>
7140<summary>Examples</summary>
7141
7142```js
7143const object = {
7144 a: [{ x: 2 }, { y: 4 }],
7145 b: 1
7146};
7147const other = {
7148 a: { z: 3 },
7149 b: [2, 3],
7150 c: 'foo'
7151};
7152merge(object, other); // { a: [ { x: 2 }, { y: 4 }, { z: 3 } ], b: [ 1, 2, 3 ], c: 'foo' }
7153```
7154
7155</details>
7156
7157<br>[⬆ Back to top](#contents)
7158
7159### nest
7160
7161Given a flat array of objects linked to one another, it will nest them recursively.
7162Useful for nesting comments, such as the ones on reddit.com.
7163
7164Use recursion.
7165Use `Array.prototype.filter()` to filter the items where the `id` matches the `link`, then `Array.prototype.map()` to map each one to a new object that has a `children` property which recursively nests the items based on which ones are children of the current item.
7166Omit the second argument, `id`, to default to `null` which indicates the object is not linked to another one (i.e. it is a top level object).
7167Omit the third argument, `link`, to use `'parent_id'` as the default property which links the object to another one by its `id`.
7168
7169```js
7170const nest = (items, id = null, link = 'parent_id') =>
7171 items
7172 .filter(item => item[link] === id)
7173 .map(item => ({ ...item, children: nest(items, item.id) }));
7174```
7175
7176<details>
7177<summary>Examples</summary>
7178
7179```js
7180// One top level comment
7181const comments = [
7182 { id: 1, parent_id: null },
7183 { id: 2, parent_id: 1 },
7184 { id: 3, parent_id: 1 },
7185 { id: 4, parent_id: 2 },
7186 { id: 5, parent_id: 4 }
7187];
7188const nestedComments = nest(comments); // [{ id: 1, parent_id: null, children: [...] }]
7189```
7190
7191
7192</details>
7193
7194<br>[⬆ Back to top](#contents)
7195
7196### objectFromPairs
7197
7198Creates an object from the given key-value pairs.
7199
7200Use `Array.prototype.reduce()` to create and combine key-value pairs.
7201
7202```js
7203const objectFromPairs = arr => arr.reduce((a, [key, val]) => ((a[key] = val), a), {});
7204```
7205
7206<details>
7207<summary>Examples</summary>
7208
7209```js
7210objectFromPairs([['a', 1], ['b', 2]]); // {a: 1, b: 2}
7211```
7212
7213</details>
7214
7215<br>[⬆ Back to top](#contents)
7216
7217### objectToPairs
7218
7219Creates an array of key-value pair arrays from an object.
7220
7221Use `Object.keys()` and `Array.prototype.map()` to iterate over the object's keys and produce an array with key-value pairs.
7222
7223```js
7224const objectToPairs = obj => Object.keys(obj).map(k => [k, obj[k]]);
7225```
7226
7227<details>
7228<summary>Examples</summary>
7229
7230```js
7231objectToPairs({ a: 1, b: 2 }); // [ ['a', 1], ['b', 2] ]
7232```
7233
7234</details>
7235
7236<br>[⬆ Back to top](#contents)
7237
7238### omit
7239
7240Omits the key-value pairs corresponding to the given keys from an object.
7241
7242Use `Object.keys(obj)`, `Array.prototype.filter()` and `Array.prototype.includes()` to remove the provided keys.
7243Use `Array.prototype.reduce()` to convert the filtered keys back to an object with the corresponding key-value pairs.
7244
7245```js
7246const omit = (obj, arr) =>
7247 Object.keys(obj)
7248 .filter(k => !arr.includes(k))
7249 .reduce((acc, key) => ((acc[key] = obj[key]), acc), {});
7250```
7251
7252<details>
7253<summary>Examples</summary>
7254
7255```js
7256omit({ a: 1, b: '2', c: 3 }, ['b']); // { 'a': 1, 'c': 3 }
7257```
7258
7259</details>
7260
7261<br>[⬆ Back to top](#contents)
7262
7263### omitBy
7264
7265Creates an object composed of the properties the given function returns falsey for. The function is invoked with two arguments: (value, key).
7266
7267Use `Object.keys(obj)` and `Array.prototype.filter()`to remove the keys for which `fn` returns a truthy value.
7268Use `Array.prototype.reduce()` to convert the filtered keys back to an object with the corresponding key-value pairs.
7269
7270```js
7271const omitBy = (obj, fn) =>
7272 Object.keys(obj)
7273 .filter(k => !fn(obj[k], k))
7274 .reduce((acc, key) => ((acc[key] = obj[key]), acc), {});
7275```
7276
7277<details>
7278<summary>Examples</summary>
7279
7280```js
7281omitBy({ a: 1, b: '2', c: 3 }, x => typeof x === 'number'); // { b: '2' }
7282```
7283
7284</details>
7285
7286<br>[⬆ Back to top](#contents)
7287
7288### orderBy
7289
7290Returns a sorted array of objects ordered by properties and orders.
7291
7292Uses `Array.prototype.sort()`, `Array.prototype.reduce()` on the `props` array with a default value of `0`, use array destructuring to swap the properties position depending on the order passed.
7293If no `orders` array is passed it sort by `'asc'` by default.
7294
7295```js
7296const orderBy = (arr, props, orders) =>
7297 [...arr].sort((a, b) =>
7298 props.reduce((acc, prop, i) => {
7299 if (acc === 0) {
7300 const [p1, p2] = orders && orders[i] === 'desc' ? [b[prop], a[prop]] : [a[prop], b[prop]];
7301 acc = p1 > p2 ? 1 : p1 < p2 ? -1 : 0;
7302 }
7303 return acc;
7304 }, 0)
7305 );
7306```
7307
7308<details>
7309<summary>Examples</summary>
7310
7311```js
7312const users = [{ name: 'fred', age: 48 }, { name: 'barney', age: 36 }, { name: 'fred', age: 40 }];
7313orderBy(users, ['name', 'age'], ['asc', 'desc']); // [{name: 'barney', age: 36}, {name: 'fred', age: 48}, {name: 'fred', age: 40}]
7314orderBy(users, ['name', 'age']); // [{name: 'barney', age: 36}, {name: 'fred', age: 40}, {name: 'fred', age: 48}]
7315```
7316
7317</details>
7318
7319<br>[⬆ Back to top](#contents)
7320
7321### pick
7322
7323Picks the key-value pairs corresponding to the given keys from an object.
7324
7325Use `Array.prototype.reduce()` to convert the filtered/picked keys back to an object with the corresponding key-value pairs if the key exists in the object.
7326
7327```js
7328const pick = (obj, arr) =>
7329 arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[curr]), acc), {});
7330```
7331
7332<details>
7333<summary>Examples</summary>
7334
7335```js
7336pick({ a: 1, b: '2', c: 3 }, ['a', 'c']); // { 'a': 1, 'c': 3 }
7337```
7338
7339</details>
7340
7341<br>[⬆ Back to top](#contents)
7342
7343### pickBy
7344
7345Creates an object composed of the properties the given function returns truthy for. The function is invoked with two arguments: (value, key).
7346
7347Use `Object.keys(obj)` and `Array.prototype.filter()`to remove the keys for which `fn` returns a falsey value.
7348Use `Array.prototype.reduce()` to convert the filtered keys back to an object with the corresponding key-value pairs.
7349
7350```js
7351const pickBy = (obj, fn) =>
7352 Object.keys(obj)
7353 .filter(k => fn(obj[k], k))
7354 .reduce((acc, key) => ((acc[key] = obj[key]), acc), {});
7355```
7356
7357<details>
7358<summary>Examples</summary>
7359
7360```js
7361pickBy({ a: 1, b: '2', c: 3 }, x => typeof x === 'number'); // { 'a': 1, 'c': 3 }
7362```
7363
7364</details>
7365
7366<br>[⬆ Back to top](#contents)
7367
7368### renameKeys
7369
7370Replaces the names of multiple object keys with the values provided.
7371
7372Use `Object.keys()` in combination with `Array.prototype.reduce()` and the spread operator (`...`) to get the object's keys and rename them according to `keysMap`.
7373
7374```js
7375const renameKeys = (keysMap, obj) =>
7376 Object.keys(obj).reduce(
7377 (acc, key) => ({
7378 ...acc,
7379 ...{ [keysMap[key] || key]: obj[key] }
7380 }),
7381 {}
7382 );
7383```
7384
7385<details>
7386<summary>Examples</summary>
7387
7388```js
7389const obj = { name: 'Bobo', job: 'Front-End Master', shoeSize: 100 };
7390renameKeys({ name: 'firstName', job: 'passion' }, obj); // { firstName: 'Bobo', passion: 'Front-End Master', shoeSize: 100 }
7391```
7392
7393</details>
7394
7395<br>[⬆ Back to top](#contents)
7396
7397### shallowClone
7398
7399Creates a shallow clone of an object.
7400
7401Use `Object.assign()` and an empty object (`{}`) to create a shallow clone of the original.
7402
7403```js
7404const shallowClone = obj => Object.assign({}, obj);
7405```
7406
7407<details>
7408<summary>Examples</summary>
7409
7410```js
7411const a = { x: true, y: 1 };
7412const b = shallowClone(a); // a !== b
7413```
7414
7415</details>
7416
7417<br>[⬆ Back to top](#contents)
7418
7419### size
7420
7421Get size of arrays, objects or strings.
7422
7423Get type of `val` (`array`, `object` or `string`).
7424Use `length` property for arrays.
7425Use `length` or `size` value if available or number of keys for objects.
7426Use `size` of a [`Blob` object](https://developer.mozilla.org/en-US/docs/Web/API/Blob) created from `val` for strings.
7427
7428Split strings into array of characters with `split('')` and return its length.
7429
7430```js
7431const size = val =>
7432 Array.isArray(val)
7433 ? val.length
7434 : val && typeof val === 'object'
7435 ? val.size || val.length || Object.keys(val).length
7436 : typeof val === 'string'
7437 ? new Blob([val]).size
7438 : 0;
7439```
7440
7441<details>
7442<summary>Examples</summary>
7443
7444```js
7445size([1, 2, 3, 4, 5]); // 5
7446size('size'); // 4
7447size({ one: 1, two: 2, three: 3 }); // 3
7448```
7449
7450</details>
7451
7452<br>[⬆ Back to top](#contents)
7453
7454### transform
7455
7456Applies a function against an accumulator and each key in the object (from left to right).
7457
7458Use `Object.keys(obj)` to iterate over each key in the object, `Array.prototype.reduce()` to call the apply the specified function against the given accumulator.
7459
7460```js
7461const transform = (obj, fn, acc) => Object.keys(obj).reduce((a, k) => fn(a, obj[k], k, obj), acc);
7462```
7463
7464<details>
7465<summary>Examples</summary>
7466
7467```js
7468transform(
7469 { a: 1, b: 2, c: 1 },
7470 (r, v, k) => {
7471 (r[v] || (r[v] = [])).push(k);
7472 return r;
7473 },
7474 {}
7475); // { '1': ['a', 'c'], '2': ['b'] }
7476```
7477
7478</details>
7479
7480<br>[⬆ Back to top](#contents)
7481
7482### truthCheckCollection
7483
7484Checks if the predicate (second argument) is truthy on all elements of a collection (first argument).
7485
7486Use `Array.prototype.every()` to check if each passed object has the specified property and if it returns a truthy value.
7487
7488```js
7489const truthCheckCollection = (collection, pre) => collection.every(obj => obj[pre]);
7490```
7491
7492<details>
7493<summary>Examples</summary>
7494
7495```js
7496truthCheckCollection([{ user: 'Tinky-Winky', sex: 'male' }, { user: 'Dipsy', sex: 'male' }], 'sex'); // true
7497```
7498
7499</details>
7500
7501<br>[⬆ Back to top](#contents)
7502
7503### unflattenObject ![advanced](/advanced.svg)
7504
7505Unflatten an object with the paths for keys.
7506
7507Use `Object.keys(obj)` combined with `Array.prototype.reduce()` to convert flattened path node to a leaf node.
7508If the value of a key contains a dot delimiter (`.`), use `Array.prototype.split('.')`, string transformations and `JSON.parse()` to create an object, then `Object.assign()` to create the leaf node.
7509Otherwise, add the appropriate key-value pair to the accumulator object.
7510
7511```js
7512const unflattenObject = obj =>
7513 Object.keys(obj).reduce((acc, k) => {
7514 if (k.indexOf('.') !== -1) {
7515 const keys = k.split('.');
7516 Object.assign(
7517 acc,
7518 JSON.parse(
7519 '{' +
7520 keys.map((v, i) => (i !== keys.length - 1 ? `"${v}":{` : `"${v}":`)).join('') +
7521 obj[k] +
7522 '}'.repeat(keys.length)
7523 )
7524 );
7525 } else acc[k] = obj[k];
7526 return acc;
7527 }, {});
7528```
7529
7530<details>
7531<summary>Examples</summary>
7532
7533```js
7534unflattenObject({ 'a.b.c': 1, d: 1 }); // { a: { b: { c: 1 } }, d: 1 }
7535```
7536
7537</details>
7538
7539<br>[⬆ Back to top](#contents)
7540
7541
7542---
7543
7544## 📜 String
7545
7546### byteSize
7547
7548Returns the length of a string in bytes.
7549
7550Convert a given string to a [`Blob` Object](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and find its `size`.
7551
7552```js
7553const byteSize = str => new Blob([str]).size;
7554```
7555
7556<details>
7557<summary>Examples</summary>
7558
7559```js
7560byteSize('😀'); // 4
7561byteSize('Hello World'); // 11
7562```
7563
7564</details>
7565
7566<br>[⬆ Back to top](#contents)
7567
7568### capitalize
7569
7570Capitalizes the first letter of a string.
7571
7572Use array destructuring and `String.prototype.toUpperCase()` to capitalize first letter, `...rest` to get array of characters after first letter and then `Array.prototype.join('')` to make it a string again.
7573Omit the `lowerRest` parameter to keep the rest of the string intact, or set it to `true` to convert to lowercase.
7574
7575```js
7576const capitalize = ([first, ...rest], lowerRest = false) =>
7577 first.toUpperCase() + (lowerRest ? rest.join('').toLowerCase() : rest.join(''));
7578```
7579
7580<details>
7581<summary>Examples</summary>
7582
7583```js
7584capitalize('fooBar'); // 'FooBar'
7585capitalize('fooBar', true); // 'Foobar'
7586```
7587
7588</details>
7589
7590<br>[⬆ Back to top](#contents)
7591
7592### capitalizeEveryWord
7593
7594Capitalizes the first letter of every word in a string.
7595
7596Use `String.prototype.replace()` to match the first character of each word and `String.prototype.toUpperCase()` to capitalize it.
7597
7598```js
7599const capitalizeEveryWord = str => str.replace(/\b[a-z]/g, char => char.toUpperCase());
7600```
7601
7602<details>
7603<summary>Examples</summary>
7604
7605```js
7606capitalizeEveryWord('hello world!'); // 'Hello World!'
7607```
7608
7609</details>
7610
7611<br>[⬆ Back to top](#contents)
7612
7613### CSVToArray
7614
7615Converts a comma-separated values (CSV) string to a 2D array.
7616
7617Use `Array.prototype.slice()` and `Array.prototype.indexOf('\n')` to remove the first row (title row) if `omitFirstRow` is `true`.
7618Use `String.prototype.split('\n')` to create a string for each row, then `String.prototype.split(delimiter)` to separate the values in each row.
7619Omit the second argument, `delimiter`, to use a default delimiter of `,`.
7620Omit the third argument, `omitFirstRow`, to include the first row (title row) of the CSV string.
7621
7622```js
7623const CSVToArray = (data, delimiter = ',', omitFirstRow = false) =>
7624 data
7625 .slice(omitFirstRow ? data.indexOf('\n') + 1 : 0)
7626 .split('\n')
7627 .map(v => v.split(delimiter));
7628```
7629
7630<details>
7631<summary>Examples</summary>
7632
7633```js
7634CSVToArray('a,b\nc,d'); // [['a','b'],['c','d']];
7635CSVToArray('a;b\nc;d', ';'); // [['a','b'],['c','d']];
7636CSVToArray('col1,col2\na,b\nc,d', ',', true); // [['a','b'],['c','d']];
7637```
7638
7639</details>
7640
7641<br>[⬆ Back to top](#contents)
7642
7643### CSVToJSON ![advanced](/advanced.svg)
7644
7645Converts a comma-separated values (CSV) string to a 2D array of objects.
7646The first row of the string is used as the title row.
7647
7648Use `Array.prototype.slice()` and `Array.prototype.indexOf('\n')` and `String.prototype.split(delimiter)` to separate the first row (title row) into values.
7649Use `String.prototype.split('\n')` to create a string for each row, then `Array.prototype.map()` and `String.prototype.split(delimiter)` to separate the values in each row.
7650Use `Array.prototype.reduce()` to create an object for each row's values, with the keys parsed from the title row.
7651Omit the second argument, `delimiter`, to use a default delimiter of `,`.
7652
7653```js
7654const CSVToJSON = (data, delimiter = ',') => {
7655 const titles = data.slice(0, data.indexOf('\n')).split(delimiter);
7656 return data
7657 .slice(data.indexOf('\n') + 1)
7658 .split('\n')
7659 .map(v => {
7660 const values = v.split(delimiter);
7661 return titles.reduce((obj, title, index) => ((obj[title] = values[index]), obj), {});
7662 });
7663};
7664```
7665
7666<details>
7667<summary>Examples</summary>
7668
7669```js
7670CSVToJSON('col1,col2\na,b\nc,d'); // [{'col1': 'a', 'col2': 'b'}, {'col1': 'c', 'col2': 'd'}];
7671CSVToJSON('col1;col2\na;b\nc;d', ';'); // [{'col1': 'a', 'col2': 'b'}, {'col1': 'c', 'col2': 'd'}];
7672```
7673
7674</details>
7675
7676<br>[⬆ Back to top](#contents)
7677
7678### decapitalize
7679
7680Decapitalizes the first letter of a string.
7681
7682Use array destructuring and `String.toLowerCase()` to decapitalize first letter, `...rest` to get array of characters after first letter and then `Array.prototype.join('')` to make it a string again.
7683Omit the `upperRest` parameter to keep the rest of the string intact, or set it to `true` to convert to uppercase.
7684
7685```js
7686const decapitalize = ([first, ...rest], upperRest = false) =>
7687 first.toLowerCase() + (upperRest ? rest.join('').toUpperCase() : rest.join(''));
7688```
7689
7690<details>
7691<summary>Examples</summary>
7692
7693```js
7694decapitalize('FooBar'); // 'fooBar'
7695decapitalize('FooBar', true); // 'fOOBAR'
7696```
7697
7698</details>
7699
7700<br>[⬆ Back to top](#contents)
7701
7702### escapeHTML
7703
7704Escapes a string for use in HTML.
7705
7706Use `String.prototype.replace()` with a regexp that matches the characters that need to be escaped, using a callback function to replace each character instance with its associated escaped character using a dictionary (object).
7707
7708```js
7709const escapeHTML = str =>
7710 str.replace(
7711 /[&<>'"]/g,
7712 tag =>
7713 ({
7714 '&': '&amp;',
7715 '<': '&lt;',
7716 '>': '&gt;',
7717 "'": '&#39;',
7718 '"': '&quot;'
7719 }[tag] || tag)
7720 );
7721```
7722
7723<details>
7724<summary>Examples</summary>
7725
7726```js
7727escapeHTML('<a href="#">Me & you</a>'); // '&lt;a href=&quot;#&quot;&gt;Me &amp; you&lt;/a&gt;'
7728```
7729
7730</details>
7731
7732<br>[⬆ Back to top](#contents)
7733
7734### escapeRegExp
7735
7736Escapes a string to use in a regular expression.
7737
7738Use `String.prototype.replace()` to escape special characters.
7739
7740```js
7741const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
7742```
7743
7744<details>
7745<summary>Examples</summary>
7746
7747```js
7748escapeRegExp('(test)'); // \\(test\\)
7749```
7750
7751</details>
7752
7753<br>[⬆ Back to top](#contents)
7754
7755### fromCamelCase
7756
7757Converts a string from camelcase.
7758
7759Use `String.prototype.replace()` to remove underscores, hyphens, and spaces and convert words to camelcase.
7760Omit the second argument to use a default `separator` of `_`.
7761
7762```js
7763const fromCamelCase = (str, separator = '_') =>
7764 str
7765 .replace(/([a-z\d])([A-Z])/g, '$1' + separator + '$2')
7766 .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + separator + '$2')
7767 .toLowerCase();
7768```
7769
7770<details>
7771<summary>Examples</summary>
7772
7773```js
7774fromCamelCase('someDatabaseFieldName', ' '); // 'some database field name'
7775fromCamelCase('someLabelThatNeedsToBeCamelized', '-'); // 'some-label-that-needs-to-be-camelized'
7776fromCamelCase('someJavascriptProperty', '_'); // 'some_javascript_property'
7777```
7778
7779</details>
7780
7781<br>[⬆ Back to top](#contents)
7782
7783### indentString
7784
7785Indents each line in the provided string.
7786
7787Use `String.replace` and a regular expression to add the character specified by `indent` `count` times at the start of each line.
7788Omit the third parameter, `indent`, to use a default indentation character of `' '`.
7789
7790```js
7791const indentString = (str, count, indent = ' ') => str.replace(/^/gm, indent.repeat(count));
7792```
7793
7794<details>
7795<summary>Examples</summary>
7796
7797```js
7798indentString('Lorem\nIpsum', 2); // ' Lorem\n Ipsum'
7799indentString('Lorem\nIpsum', 2, '_'); // '__Lorem\n__Ipsum'
7800```
7801
7802</details>
7803
7804<br>[⬆ Back to top](#contents)
7805
7806### isAbsoluteURL
7807
7808Returns `true` if the given string is an absolute URL, `false` otherwise.
7809
7810Use a regular expression to test if the string is an absolute URL.
7811
7812```js
7813const isAbsoluteURL = str => /^[a-z][a-z0-9+.-]*:/.test(str);
7814```
7815
7816<details>
7817<summary>Examples</summary>
7818
7819```js
7820isAbsoluteURL('https://google.com'); // true
7821isAbsoluteURL('ftp://www.myserver.net'); // true
7822isAbsoluteURL('/foo/bar'); // false
7823```
7824
7825</details>
7826
7827<br>[⬆ Back to top](#contents)
7828
7829### isAnagram
7830
7831Checks if a string is an anagram of another string (case-insensitive, ignores spaces, punctuation and special characters).
7832
7833Use `String.toLowerCase()`, `String.prototype.replace()` with an appropriate regular expression to remove unnecessary characters, `String.prototype.split('')`, `Array.prototype.sort()` and `Array.prototype.join('')` on both strings to normalize them, then check if their normalized forms are equal.
7834
7835```js
7836const isAnagram = (str1, str2) => {
7837 const normalize = str =>
7838 str
7839 .toLowerCase()
7840 .replace(/[^a-z0-9]/gi, '')
7841 .split('')
7842 .sort()
7843 .join('');
7844 return normalize(str1) === normalize(str2);
7845};
7846```
7847
7848<details>
7849<summary>Examples</summary>
7850
7851```js
7852isAnagram('iceman', 'cinema'); // true
7853```
7854
7855</details>
7856
7857<br>[⬆ Back to top](#contents)
7858
7859### isLowerCase
7860
7861Checks if a string is lower case.
7862
7863Convert the given string to lower case, using `String.toLowerCase()` and compare it to the original.
7864
7865```js
7866const isLowerCase = str => str === str.toLowerCase();
7867```
7868
7869<details>
7870<summary>Examples</summary>
7871
7872```js
7873isLowerCase('abc'); // true
7874isLowerCase('a3@$'); // true
7875isLowerCase('Ab4'); // false
7876```
7877
7878</details>
7879
7880<br>[⬆ Back to top](#contents)
7881
7882### isUpperCase
7883
7884Checks if a string is upper case.
7885
7886Convert the given string to upper case, using `String.prototype.toUpperCase()` and compare it to the original.
7887
7888
7889```js
7890const isUpperCase = str => str === str.toUpperCase();
7891```
7892
7893<details>
7894<summary>Examples</summary>
7895
7896```js
7897isUpperCase('ABC'); // true
7898isLowerCase('A3@$'); // true
7899isLowerCase('aB4'); // false
7900```
7901
7902</details>
7903
7904<br>[⬆ Back to top](#contents)
7905
7906### mapString
7907
7908Creates a new string with the results of calling a provided function on every character in the calling string.
7909
7910Use `String.prototype.split('')` and `Array.prototype.map()` to call the provided function, `fn`, for each character in `str`.
7911Use `Array.prototype.join('')` to recombine the array of characters into a string.
7912The callback function, `fn`, takes three arguments (the current character, the index of the current character and the string `mapString` was called upon).
7913
7914```js
7915const mapString = (str, fn) =>
7916 str
7917 .split('')
7918 .map((c, i) => fn(c, i, str))
7919 .join('');
7920```
7921
7922<details>
7923<summary>Examples</summary>
7924
7925```js
7926mapString('lorem ipsum', c => c.toUpperCase()); // 'LOREM IPSUM'
7927```
7928
7929</details>
7930
7931<br>[⬆ Back to top](#contents)
7932
7933### mask
7934
7935Replaces all but the last `num` of characters with the specified mask character.
7936
7937Use `String.prototype.slice()` to grab the portion of the characters that will remain unmasked and use `String.padStart()` to fill the beginning of the string with the mask character up to the original length.
7938Omit the second argument, `num`, to keep a default of `4` characters unmasked. If `num` is negative, the unmasked characters will be at the start of the string.
7939Omit the third argument, `mask`, to use a default character of `'*'` for the mask.
7940
7941```js
7942const mask = (cc, num = 4, mask = '*') => `${cc}`.slice(-num).padStart(`${cc}`.length, mask);
7943```
7944
7945<details>
7946<summary>Examples</summary>
7947
7948```js
7949mask(1234567890); // '******7890'
7950mask(1234567890, 3); // '*******890'
7951mask(1234567890, -4, '$'); // '$$$$567890'
7952```
7953
7954</details>
7955
7956<br>[⬆ Back to top](#contents)
7957
7958### pad
7959
7960Pads a string on both sides with the specified character, if it's shorter than the specified length.
7961
7962Use `String.padStart()` and `String.padEnd()` to pad both sides of the given string.
7963Omit the third argument, `char`, to use the whitespace character as the default padding character.
7964
7965```js
7966const pad = (str, length, char = ' ') =>
7967 str.padStart((str.length + length) / 2, char).padEnd(length, char);
7968```
7969
7970<details>
7971<summary>Examples</summary>
7972
7973```js
7974pad('cat', 8); // ' cat '
7975pad(String(42), 6, '0'); // '004200'
7976pad('foobar', 3); // 'foobar'
7977```
7978
7979</details>
7980
7981<br>[⬆ Back to top](#contents)
7982
7983### palindrome
7984
7985Returns `true` if the given string is a palindrome, `false` otherwise.
7986
7987Convert the string to `String.prototype.toLowerCase()` and use `String.prototype.replace()` to remove non-alphanumeric characters from it.
7988Then, use the spread operator (`...`) to split the string into individual characters, `Array.prototype.reverse()`, `String.prototype.join('')` and compare it to the original, unreversed string, after converting it to `String.prototype.toLowerCase()`.
7989
7990```js
7991const palindrome = str => {
7992 const s = str.toLowerCase().replace(/[\W_]/g, '');
7993 return s === [...s].reverse().join('');
7994};
7995```
7996
7997<details>
7998<summary>Examples</summary>
7999
8000```js
8001palindrome('taco cat'); // true
8002```
8003
8004</details>
8005
8006<br>[⬆ Back to top](#contents)
8007
8008### pluralize
8009
8010Returns the singular or plural form of the word based on the input number. If the first argument is an `object`, it will use a closure by returning a function that can auto-pluralize words that don't simply end in `s` if the supplied dictionary contains the word.
8011
8012If `num` is either `-1` or `1`, return the singular form of the word. If `num` is any other number, return the plural form. Omit the third argument to use the default of the singular word + `s`, or supply a custom pluralized word when necessary. If the first argument is an `object`, utilize a closure by returning a function which can use the supplied dictionary to resolve the correct plural form of the word.
8013
8014```js
8015const pluralize = (val, word, plural = word + 's') => {
8016 const _pluralize = (num, word, plural = word + 's') =>
8017 [1, -1].includes(Number(num)) ? word : plural;
8018 if (typeof val === 'object') return (num, word) => _pluralize(num, word, val[word]);
8019 return _pluralize(val, word, plural);
8020};
8021```
8022
8023<details>
8024<summary>Examples</summary>
8025
8026```js
8027pluralize(0, 'apple'); // 'apples'
8028pluralize(1, 'apple'); // 'apple'
8029pluralize(2, 'apple'); // 'apples'
8030pluralize(2, 'person', 'people'); // 'people'
8031
8032const PLURALS = {
8033 person: 'people',
8034 radius: 'radii'
8035};
8036const autoPluralize = pluralize(PLURALS);
8037autoPluralize(2, 'person'); // 'people'
8038```
8039
8040</details>
8041
8042<br>[⬆ Back to top](#contents)
8043
8044### removeNonASCII
8045
8046Removes non-printable ASCII characters.
8047
8048Use a regular expression to remove non-printable ASCII characters.
8049
8050```js
8051const removeNonASCII = str => str.replace(/[^\x20-\x7E]/g, '');
8052```
8053
8054<details>
8055<summary>Examples</summary>
8056
8057```js
8058removeNonASCII('äÄçÇéÉêlorem-ipsumöÖÐþúÚ'); // 'lorem-ipsum'
8059```
8060
8061</details>
8062
8063<br>[⬆ Back to top](#contents)
8064
8065### reverseString
8066
8067Reverses a string.
8068
8069Use the spread operator (`...`) and `Array.prototype.reverse()` to reverse the order of the characters in the string.
8070Combine characters to get a string using `String.prototype.join('')`.
8071
8072```js
8073const reverseString = str => [...str].reverse().join('');
8074```
8075
8076<details>
8077<summary>Examples</summary>
8078
8079```js
8080reverseString('foobar'); // 'raboof'
8081```
8082
8083</details>
8084
8085<br>[⬆ Back to top](#contents)
8086
8087### sortCharactersInString
8088
8089Alphabetically sorts the characters in a string.
8090
8091Use the spread operator (`...`), `Array.prototype.sort()` and `String.localeCompare()` to sort the characters in `str`, recombine using `String.prototype.join('')`.
8092
8093```js
8094const sortCharactersInString = str => [...str].sort((a, b) => a.localeCompare(b)).join('');
8095```
8096
8097<details>
8098<summary>Examples</summary>
8099
8100```js
8101sortCharactersInString('cabbage'); // 'aabbceg'
8102```
8103
8104</details>
8105
8106<br>[⬆ Back to top](#contents)
8107
8108### splitLines
8109
8110Splits a multiline string into an array of lines.
8111
8112Use `String.prototype.split()` and a regular expression to match line breaks and create an array.
8113
8114```js
8115const splitLines = str => str.split(/\r?\n/);
8116```
8117
8118<details>
8119<summary>Examples</summary>
8120
8121```js
8122splitLines('This\nis a\nmultiline\nstring.\n'); // ['This', 'is a', 'multiline', 'string.' , '']
8123```
8124
8125</details>
8126
8127<br>[⬆ Back to top](#contents)
8128
8129### stringPermutations ![advanced](/advanced.svg)
8130
8131⚠️ **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.
8132
8133Generates all permutations of a string (contains duplicates).
8134
8135Use recursion.
8136For each letter in the given string, create all the partial permutations for the rest of its letters.
8137Use `Array.prototype.map()` to combine the letter with each partial permutation, then `Array.prototype.reduce()` to combine all permutations in one array.
8138Base cases are for string `length` equal to `2` or `1`.
8139
8140```js
8141const stringPermutations = str => {
8142 if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str];
8143 return str
8144 .split('')
8145 .reduce(
8146 (acc, letter, i) =>
8147 acc.concat(stringPermutations(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val)),
8148 []
8149 );
8150};
8151```
8152
8153<details>
8154<summary>Examples</summary>
8155
8156```js
8157stringPermutations('abc'); // ['abc','acb','bac','bca','cab','cba']
8158```
8159
8160</details>
8161
8162<br>[⬆ Back to top](#contents)
8163
8164### stripHTMLTags
8165
8166Removes HTML/XML tags from string.
8167
8168Use a regular expression to remove HTML/XML tags from a string.
8169
8170```js
8171const stripHTMLTags = str => str.replace(/<[^>]*>/g, '');
8172```
8173
8174<details>
8175<summary>Examples</summary>
8176
8177```js
8178stripHTMLTags('<p><em>lorem</em> <strong>ipsum</strong></p>'); // 'lorem ipsum'
8179```
8180
8181</details>
8182
8183<br>[⬆ Back to top](#contents)
8184
8185### toCamelCase
8186
8187Converts a string to camelcase.
8188
8189Break the string into words and combine them capitalizing the first letter of each word, using a regexp.
8190
8191```js
8192const toCamelCase = str => {
8193 let s =
8194 str &&
8195 str
8196 .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
8197 .map(x => x.slice(0, 1).toUpperCase() + x.slice(1).toLowerCase())
8198 .join('');
8199 return s.slice(0, 1).toLowerCase() + s.slice(1);
8200};
8201```
8202
8203<details>
8204<summary>Examples</summary>
8205
8206```js
8207toCamelCase('some_database_field_name'); // 'someDatabaseFieldName'
8208toCamelCase('Some label that needs to be camelized'); // 'someLabelThatNeedsToBeCamelized'
8209toCamelCase('some-javascript-property'); // 'someJavascriptProperty'
8210toCamelCase('some-mixed_string with spaces_underscores-and-hyphens'); // 'someMixedStringWithSpacesUnderscoresAndHyphens'
8211```
8212
8213</details>
8214
8215<br>[⬆ Back to top](#contents)
8216
8217### toKebabCase
8218
8219Converts a string to kebab case.
8220
8221Break the string into words and combine them adding `-` as a separator, using a regexp.
8222
8223```js
8224const toKebabCase = str =>
8225 str &&
8226 str
8227 .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
8228 .map(x => x.toLowerCase())
8229 .join('-');
8230```
8231
8232<details>
8233<summary>Examples</summary>
8234
8235```js
8236toKebabCase('camelCase'); // 'camel-case'
8237toKebabCase('some text'); // 'some-text'
8238toKebabCase('some-mixed_string With spaces_underscores-and-hyphens'); // 'some-mixed-string-with-spaces-underscores-and-hyphens'
8239toKebabCase('AllThe-small Things'); // "all-the-small-things"
8240toKebabCase('IAmListeningToFMWhileLoadingDifferentURLOnMyBrowserAndAlsoEditingSomeXMLAndHTML'); // "i-am-listening-to-fm-while-loading-different-url-on-my-browser-and-also-editing-xml-and-html"
8241```
8242
8243</details>
8244
8245<br>[⬆ Back to top](#contents)
8246
8247### toSnakeCase
8248
8249Converts a string to snake case.
8250
8251Break the string into words and combine them adding `_` as a separator, using a regexp.
8252
8253```js
8254const toSnakeCase = str =>
8255 str &&
8256 str
8257 .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
8258 .map(x => x.toLowerCase())
8259 .join('_');
8260```
8261
8262<details>
8263<summary>Examples</summary>
8264
8265```js
8266toSnakeCase('camelCase'); // 'camel_case'
8267toSnakeCase('some text'); // 'some_text'
8268toSnakeCase('some-mixed_string With spaces_underscores-and-hyphens'); // 'some_mixed_string_with_spaces_underscores_and_hyphens'
8269toSnakeCase('AllThe-small Things'); // "all_the_smal_things"
8270toSnakeCase('IAmListeningToFMWhileLoadingDifferentURLOnMyBrowserAndAlsoEditingSomeXMLAndHTML'); // "i_am_listening_to_fm_while_loading_different_url_on_my_browser_and_also_editing_some_xml_and_html"
8271```
8272
8273</details>
8274
8275<br>[⬆ Back to top](#contents)
8276
8277### toTitleCase
8278
8279Converts a string to title case.
8280
8281Break the string into words, using a regexp, and combine them capitalizing the first letter of each word and adding a whitespace between them.
8282
8283```js
8284const toTitleCase = str =>
8285 str
8286 .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
8287 .map(x => x.charAt(0).toUpperCase() + x.slice(1))
8288 .join(' ');
8289```
8290
8291<details>
8292<summary>Examples</summary>
8293
8294```js
8295toTitleCase('some_database_field_name'); // 'Some Database Field Name'
8296toTitleCase('Some label that needs to be title-cased'); // 'Some Label That Needs To Be Title Cased'
8297toTitleCase('some-package-name'); // 'Some Package Name'
8298toTitleCase('some-mixed_string with spaces_underscores-and-hyphens'); // 'Some Mixed String With Spaces Underscores And Hyphens'
8299```
8300
8301</details>
8302
8303<br>[⬆ Back to top](#contents)
8304
8305### truncateString
8306
8307Truncates a string up to a specified length.
8308
8309Determine if the string's `length` is greater than `num`.
8310Return the string truncated to the desired length, with `'...'` appended to the end or the original string.
8311
8312```js
8313const truncateString = (str, num) =>
8314 str.length > num ? str.slice(0, num > 3 ? num - 3 : num) + '...' : str;
8315```
8316
8317<details>
8318<summary>Examples</summary>
8319
8320```js
8321truncateString('boomerang', 7); // 'boom...'
8322```
8323
8324</details>
8325
8326<br>[⬆ Back to top](#contents)
8327
8328### unescapeHTML
8329
8330Unescapes escaped HTML characters.
8331
8332Use `String.prototype.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).
8333
8334```js
8335const unescapeHTML = str =>
8336 str.replace(
8337 /&amp;|&lt;|&gt;|&#39;|&quot;/g,
8338 tag =>
8339 ({
8340 '&amp;': '&',
8341 '&lt;': '<',
8342 '&gt;': '>',
8343 '&#39;': "'",
8344 '&quot;': '"'
8345 }[tag] || tag)
8346 );
8347```
8348
8349<details>
8350<summary>Examples</summary>
8351
8352```js
8353unescapeHTML('&lt;a href=&quot;#&quot;&gt;Me &amp; you&lt;/a&gt;'); // '<a href="#">Me & you</a>'
8354```
8355
8356</details>
8357
8358<br>[⬆ Back to top](#contents)
8359
8360### URLJoin ![advanced](/advanced.svg)
8361
8362Joins all given URL segments together, then normalizes the resulting URL.
8363
8364Use `String.prototype.join('/')` to combine URL segments, then a series of `String.prototype.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).
8365
8366```js
8367const URLJoin = (...args) =>
8368 args
8369 .join('/')
8370 .replace(/[\/]+/g, '/')
8371 .replace(/^(.+):\//, '$1://')
8372 .replace(/^file:/, 'file:/')
8373 .replace(/\/(\?|&|#[^!])/g, '$1')
8374 .replace(/\?/g, '&')
8375 .replace('&', '?');
8376```
8377
8378<details>
8379<summary>Examples</summary>
8380
8381```js
8382URLJoin('http://www.google.com', 'a', '/b/cd', '?foo=123', '?bar=foo'); // 'http://www.google.com/a/b/cd?foo=123&bar=foo'
8383```
8384
8385</details>
8386
8387<br>[⬆ Back to top](#contents)
8388
8389### words
8390
8391Converts a given string into an array of words.
8392
8393Use `String.prototype.split()` with a supplied pattern (defaults to non-alpha as a regexp) to convert to an array of strings. Use `Array.prototype.filter()` to remove any empty strings.
8394Omit the second argument to use the default regexp.
8395
8396```js
8397const words = (str, pattern = /[^a-zA-Z-]+/) => str.split(pattern).filter(Boolean);
8398```
8399
8400<details>
8401<summary>Examples</summary>
8402
8403```js
8404words('I love javaScript!!'); // ["I", "love", "javaScript"]
8405words('python, javaScript & coffee'); // ["python", "javaScript", "coffee"]
8406```
8407
8408</details>
8409
8410<br>[⬆ Back to top](#contents)
8411
8412
8413---
8414
8415## 📃 Type
8416
8417### getType
8418
8419Returns the native type of a value.
8420
8421Returns lowercased constructor name of value, `"undefined"` or `"null"` if value is `undefined` or `null`.
8422
8423```js
8424const getType = v =>
8425 v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name.toLowerCase();
8426```
8427
8428<details>
8429<summary>Examples</summary>
8430
8431```js
8432getType(new Set([1, 2, 3])); // 'set'
8433```
8434
8435</details>
8436
8437<br>[⬆ Back to top](#contents)
8438
8439### is
8440
8441Checks if the provided value is of the specified type.
8442
8443Ensure the value is not `undefined` or `null` using `Array.prototype.includes()`, and compare the `constructor` property on the value with `type` to check if the provided value is of the specified `type`.
8444
8445```js
8446const is = (type, val) => ![, null].includes(val) && val.constructor === type;
8447```
8448
8449<details>
8450<summary>Examples</summary>
8451
8452```js
8453is(Array, [1]); // true
8454is(ArrayBuffer, new ArrayBuffer()); // true
8455is(Map, new Map()); // true
8456is(RegExp, /./g); // true
8457is(Set, new Set()); // true
8458is(WeakMap, new WeakMap()); // true
8459is(WeakSet, new WeakSet()); // true
8460is(String, ''); // true
8461is(String, new String('')); // true
8462is(Number, 1); // true
8463is(Number, new Number(1)); // true
8464is(Boolean, true); // true
8465is(Boolean, new Boolean(true)); // true
8466```
8467
8468</details>
8469
8470<br>[⬆ Back to top](#contents)
8471
8472### isArrayLike
8473
8474Checks if the provided argument is array-like (i.e. is iterable).
8475
8476Check if the provided argument is not `null` and that its `Symbol.iterator` property is a function.
8477
8478```js
8479const isArrayLike = obj => obj != null && typeof obj[Symbol.iterator] === 'function';
8480```
8481
8482<details>
8483<summary>Examples</summary>
8484
8485```js
8486isArrayLike(document.querySelectorAll('.className')); // true
8487isArrayLike('abc'); // true
8488isArrayLike(null); // false
8489```
8490
8491</details>
8492
8493<br>[⬆ Back to top](#contents)
8494
8495### isBoolean
8496
8497Checks if the given argument is a native boolean element.
8498
8499Use `typeof` to check if a value is classified as a boolean primitive.
8500
8501```js
8502const isBoolean = val => typeof val === 'boolean';
8503```
8504
8505<details>
8506<summary>Examples</summary>
8507
8508```js
8509isBoolean(null); // false
8510isBoolean(false); // true
8511```
8512
8513</details>
8514
8515<br>[⬆ Back to top](#contents)
8516
8517### isEmpty
8518
8519Returns 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.
8520
8521Check if the provided value is `null` or if its `length` is equal to `0`.
8522
8523```js
8524const isEmpty = val => val == null || !(Object.keys(val) || val).length;
8525```
8526
8527<details>
8528<summary>Examples</summary>
8529
8530```js
8531isEmpty(new Map()); // true
8532isEmpty(new Set()); // true
8533isEmpty([]); // true
8534isEmpty({}); // true
8535isEmpty(''); // true
8536isEmpty([1, 2]); // false
8537isEmpty({ a: 1, b: 2 }); // false
8538isEmpty('text'); // false
8539isEmpty(123); // true - type is not considered a collection
8540isEmpty(true); // true - type is not considered a collection
8541```
8542
8543</details>
8544
8545<br>[⬆ Back to top](#contents)
8546
8547### isFunction
8548
8549Checks if the given argument is a function.
8550
8551Use `typeof` to check if a value is classified as a function primitive.
8552
8553```js
8554const isFunction = val => typeof val === 'function';
8555```
8556
8557<details>
8558<summary>Examples</summary>
8559
8560```js
8561isFunction('x'); // false
8562isFunction(x => x); // true
8563```
8564
8565</details>
8566
8567<br>[⬆ Back to top](#contents)
8568
8569### isNil
8570
8571Returns `true` if the specified value is `null` or `undefined`, `false` otherwise.
8572
8573Use the strict equality operator to check if the value and of `val` are equal to `null` or `undefined`.
8574
8575```js
8576const isNil = val => val === undefined || val === null;
8577```
8578
8579<details>
8580<summary>Examples</summary>
8581
8582```js
8583isNil(null); // true
8584isNil(undefined); // true
8585```
8586
8587</details>
8588
8589<br>[⬆ Back to top](#contents)
8590
8591### isNull
8592
8593Returns `true` if the specified value is `null`, `false` otherwise.
8594
8595Use the strict equality operator to check if the value and of `val` are equal to `null`.
8596
8597```js
8598const isNull = val => val === null;
8599```
8600
8601<details>
8602<summary>Examples</summary>
8603
8604```js
8605isNull(null); // true
8606```
8607
8608</details>
8609
8610<br>[⬆ Back to top](#contents)
8611
8612### isNumber
8613
8614Checks if the given argument is a number.
8615
8616Use `typeof` to check if a value is classified as a number primitive.
8617
8618```js
8619const isNumber = val => typeof val === 'number';
8620```
8621
8622<details>
8623<summary>Examples</summary>
8624
8625```js
8626isNumber('1'); // false
8627isNumber(1); // true
8628```
8629
8630</details>
8631
8632<br>[⬆ Back to top](#contents)
8633
8634### isObject
8635
8636Returns a boolean determining if the passed value is an object or not.
8637
8638Uses the `Object` constructor to create an object wrapper for the given value.
8639If 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.
8640
8641```js
8642const isObject = obj => obj === Object(obj);
8643```
8644
8645<details>
8646<summary>Examples</summary>
8647
8648```js
8649isObject([1, 2, 3, 4]); // true
8650isObject([]); // true
8651isObject(['Hello!']); // true
8652isObject({ a: 1 }); // true
8653isObject({}); // true
8654isObject(true); // false
8655```
8656
8657</details>
8658
8659<br>[⬆ Back to top](#contents)
8660
8661### isObjectLike
8662
8663Checks if a value is object-like.
8664
8665Check if the provided value is not `null` and its `typeof` is equal to `'object'`.
8666
8667```js
8668const isObjectLike = val => val !== null && typeof val === 'object';
8669```
8670
8671<details>
8672<summary>Examples</summary>
8673
8674```js
8675isObjectLike({}); // true
8676isObjectLike([1, 2, 3]); // true
8677isObjectLike(x => x); // false
8678isObjectLike(null); // false
8679```
8680
8681</details>
8682
8683<br>[⬆ Back to top](#contents)
8684
8685### isPlainObject
8686
8687Checks if the provided value is an object created by the Object constructor.
8688
8689Check if the provided value is truthy, use `typeof` to check if it is an object and `Object.constructor` to make sure the constructor is equal to `Object`.
8690
8691```js
8692const isPlainObject = val => !!val && typeof val === 'object' && val.constructor === Object;
8693```
8694
8695<details>
8696<summary>Examples</summary>
8697
8698```js
8699isPlainObject({ a: 1 }); // true
8700isPlainObject(new Map()); // false
8701```
8702
8703</details>
8704
8705<br>[⬆ Back to top](#contents)
8706
8707### isPrimitive
8708
8709Returns a boolean determining if the passed value is primitive or not.
8710
8711Create an object from `val` and compare it with `val` to determine if the passed value is primitive (i.e. not equal to the created object).
8712
8713```js
8714const isPrimitive = val => Object(val) !== val;
8715```
8716
8717<details>
8718<summary>Examples</summary>
8719
8720```js
8721isPrimitive(null); // true
8722isPrimitive(50); // true
8723isPrimitive('Hello!'); // true
8724isPrimitive(false); // true
8725isPrimitive(Symbol()); // true
8726isPrimitive([]); // false
8727```
8728
8729</details>
8730
8731<br>[⬆ Back to top](#contents)
8732
8733### isPromiseLike
8734
8735Returns `true` if an object looks like a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), `false` otherwise.
8736
8737Check if the object is not `null`, its `typeof` matches either `object` or `function` and if it has a `.then` property, which is also a `function`.
8738
8739```js
8740const isPromiseLike = obj =>
8741 obj !== null &&
8742 (typeof obj === 'object' || typeof obj === 'function') &&
8743 typeof obj.then === 'function';
8744```
8745
8746<details>
8747<summary>Examples</summary>
8748
8749```js
8750isPromiseLike({
8751 then: function() {
8752 return '';
8753 }
8754}); // true
8755isPromiseLike(null); // false
8756isPromiseLike({}); // false
8757```
8758
8759</details>
8760
8761<br>[⬆ Back to top](#contents)
8762
8763### isString
8764
8765Checks if the given argument is a string. Only works for string primitives.
8766
8767Use `typeof` to check if a value is classified as a string primitive.
8768
8769```js
8770const isString = val => typeof val === 'string';
8771```
8772
8773<details>
8774<summary>Examples</summary>
8775
8776```js
8777isString('10'); // true
8778```
8779
8780</details>
8781
8782<br>[⬆ Back to top](#contents)
8783
8784### isSymbol
8785
8786Checks if the given argument is a symbol.
8787
8788Use `typeof` to check if a value is classified as a symbol primitive.
8789
8790```js
8791const isSymbol = val => typeof val === 'symbol';
8792```
8793
8794<details>
8795<summary>Examples</summary>
8796
8797```js
8798isSymbol(Symbol('x')); // true
8799```
8800
8801</details>
8802
8803<br>[⬆ Back to top](#contents)
8804
8805### isUndefined
8806
8807Returns `true` if the specified value is `undefined`, `false` otherwise.
8808
8809Use the strict equality operator to check if the value and of `val` are equal to `undefined`.
8810
8811```js
8812const isUndefined = val => val === undefined;
8813```
8814
8815<details>
8816<summary>Examples</summary>
8817
8818```js
8819isUndefined(undefined); // true
8820```
8821
8822</details>
8823
8824<br>[⬆ Back to top](#contents)
8825
8826### isValidJSON
8827
8828Checks if the provided string is a valid JSON.
8829
8830Use `JSON.parse()` and a `try... catch` block to check if the provided string is a valid JSON.
8831
8832```js
8833const isValidJSON = str => {
8834 try {
8835 JSON.parse(str);
8836 return true;
8837 } catch (e) {
8838 return false;
8839 }
8840};
8841```
8842
8843<details>
8844<summary>Examples</summary>
8845
8846```js
8847isValidJSON('{"name":"Adam","age":20}'); // true
8848isValidJSON('{"name":"Adam",age:"20"}'); // false
8849isValidJSON(null); // true
8850```
8851
8852</details>
8853
8854<br>[⬆ Back to top](#contents)
8855
8856
8857---
8858
8859## 🔧 Utility
8860
8861### castArray
8862
8863Casts the provided value as an array if it's not one.
8864
8865Use `Array.prototype.isArray()` to determine if `val` is an array and return it as-is or encapsulated in an array accordingly.
8866
8867```js
8868const castArray = val => (Array.isArray(val) ? val : [val]);
8869```
8870
8871<details>
8872<summary>Examples</summary>
8873
8874```js
8875castArray('foo'); // ['foo']
8876castArray([1]); // [1]
8877```
8878
8879</details>
8880
8881<br>[⬆ Back to top](#contents)
8882
8883### cloneRegExp
8884
8885Clones a regular expression.
8886
8887Use `new RegExp()`, `RegExp.source` and `RegExp.flags` to clone the given regular expression.
8888
8889```js
8890const cloneRegExp = regExp => new RegExp(regExp.source, regExp.flags);
8891```
8892
8893<details>
8894<summary>Examples</summary>
8895
8896```js
8897const regExp = /lorem ipsum/gi;
8898const regExp2 = cloneRegExp(regExp); // /lorem ipsum/gi
8899```
8900
8901</details>
8902
8903<br>[⬆ Back to top](#contents)
8904
8905### coalesce
8906
8907Returns the first non-null/undefined argument.
8908
8909Use `Array.prototype.find()` to return the first non `null`/`undefined` argument.
8910
8911```js
8912const coalesce = (...args) => args.find(_ => ![undefined, null].includes(_));
8913```
8914
8915<details>
8916<summary>Examples</summary>
8917
8918```js
8919coalesce(null, undefined, '', NaN, 'Waldo'); // ""
8920```
8921
8922</details>
8923
8924<br>[⬆ Back to top](#contents)
8925
8926### coalesceFactory
8927
8928Returns a customized coalesce function that returns the first argument that returns `true` from the provided argument validation function.
8929
8930Use `Array.prototype.find()` to return the first argument that returns `true` from the provided argument validation function.
8931
8932```js
8933const coalesceFactory = valid => (...args) => args.find(valid);
8934```
8935
8936<details>
8937<summary>Examples</summary>
8938
8939```js
8940const customCoalesce = coalesceFactory(_ => ![null, undefined, '', NaN].includes(_));
8941customCoalesce(undefined, null, NaN, '', 'Waldo'); // "Waldo"
8942```
8943
8944</details>
8945
8946<br>[⬆ Back to top](#contents)
8947
8948### extendHex
8949
8950Extends a 3-digit color code to a 6-digit color code.
8951
8952Use `Array.prototype.map()`, `String.prototype.split()` and `Array.prototype.join()` to join the mapped array for converting a 3-digit RGB notated hexadecimal color-code to the 6-digit form.
8953`Array.prototype.slice()` is used to remove `#` from string start since it's added once.
8954
8955```js
8956const extendHex = shortHex =>
8957 '#' +
8958 shortHex
8959 .slice(shortHex.startsWith('#') ? 1 : 0)
8960 .split('')
8961 .map(x => x + x)
8962 .join('');
8963```
8964
8965<details>
8966<summary>Examples</summary>
8967
8968```js
8969extendHex('#03f'); // '#0033ff'
8970extendHex('05a'); // '#0055aa'
8971```
8972
8973</details>
8974
8975<br>[⬆ Back to top](#contents)
8976
8977### getURLParameters
8978
8979Returns an object containing the parameters of the current URL.
8980
8981Use `String.match()` with an appropriate regular expression to get all key-value pairs, `Array.prototype.reduce()` to map and combine them into a single object.
8982Pass `location.search` as the argument to apply to the current `url`.
8983
8984```js
8985const getURLParameters = url =>
8986 (url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce(
8987 (a, v) => ((a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a),
8988 {}
8989 );
8990```
8991
8992<details>
8993<summary>Examples</summary>
8994
8995```js
8996getURLParameters('http://url.com/page?name=Adam&surname=Smith'); // {name: 'Adam', surname: 'Smith'}
8997getURLParameters('google.com'); // {}
8998```
8999
9000</details>
9001
9002<br>[⬆ Back to top](#contents)
9003
9004### hexToRGB ![advanced](/advanced.svg)
9005
9006Converts a color code to a `rgb()` or `rgba()` string if alpha value is provided.
9007
9008Use bitwise right-shift operator and mask bits with `&` (and) operator to convert a hexadecimal color code (with or without prefixed with `#`) to a string with the RGB values. If it's 3-digit color code, first convert to 6-digit version. If an alpha value is provided alongside 6-digit hex, give `rgba()` string in return.
9009
9010```js
9011const hexToRGB = hex => {
9012 let alpha = false,
9013 h = hex.slice(hex.startsWith('#') ? 1 : 0);
9014 if (h.length === 3) h = [...h].map(x => x + x).join('');
9015 else if (h.length === 8) alpha = true;
9016 h = parseInt(h, 16);
9017 return (
9018 'rgb' +
9019 (alpha ? 'a' : '') +
9020 '(' +
9021 (h >>> (alpha ? 24 : 16)) +
9022 ', ' +
9023 ((h & (alpha ? 0x00ff0000 : 0x00ff00)) >>> (alpha ? 16 : 8)) +
9024 ', ' +
9025 ((h & (alpha ? 0x0000ff00 : 0x0000ff)) >>> (alpha ? 8 : 0)) +
9026 (alpha ? `, ${h & 0x000000ff}` : '') +
9027 ')'
9028 );
9029};
9030```
9031
9032<details>
9033<summary>Examples</summary>
9034
9035```js
9036hexToRGB('#27ae60ff'); // 'rgba(39, 174, 96, 255)'
9037hexToRGB('27ae60'); // 'rgb(39, 174, 96)'
9038hexToRGB('#fff'); // 'rgb(255, 255, 255)'
9039```
9040
9041</details>
9042
9043<br>[⬆ Back to top](#contents)
9044
9045### httpGet
9046
9047Makes a `GET` request to the passed URL.
9048
9049Use [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest) web api to make a `get` request to the given `url`.
9050Handle the `onload` event, by calling the given `callback` the `responseText`.
9051Handle the `onerror` event, by running the provided `err` function.
9052Omit the third argument, `err`, to log errors to the console's `error` stream by default.
9053
9054```js
9055const httpGet = (url, callback, err = console.error) => {
9056 const request = new XMLHttpRequest();
9057 request.open('GET', url, true);
9058 request.onload = () => callback(request.responseText);
9059 request.onerror = () => err(request);
9060 request.send();
9061};
9062```
9063
9064<details>
9065<summary>Examples</summary>
9066
9067```js
9068httpGet(
9069 'https://jsonplaceholder.typicode.com/posts/1',
9070 console.log
9071); /*
9072Logs: {
9073 "userId": 1,
9074 "id": 1,
9075 "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
9076 "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
9077}
9078*/
9079```
9080
9081</details>
9082
9083<br>[⬆ Back to top](#contents)
9084
9085### httpPost
9086
9087Makes a `POST` request to the passed URL.
9088
9089Use [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest) web api to make a `post` request to the given `url`.
9090Set the value of an `HTTP` request header with `setRequestHeader` method.
9091Handle the `onload` event, by calling the given `callback` the `responseText`.
9092Handle the `onerror` event, by running the provided `err` function.
9093Omit the third argument, `data`, to send no data to the provided `url`.
9094Omit the fourth argument, `err`, to log errors to the console's `error` stream by default.
9095
9096```js
9097const httpPost = (url, data, callback, err = console.error) => {
9098 const request = new XMLHttpRequest();
9099 request.open('POST', url, true);
9100 request.setRequestHeader('Content-type', 'application/json; charset=utf-8');
9101 request.onload = () => callback(request.responseText);
9102 request.onerror = () => err(request);
9103 request.send(data);
9104};
9105```
9106
9107<details>
9108<summary>Examples</summary>
9109
9110```js
9111const newPost = {
9112 userId: 1,
9113 id: 1337,
9114 title: 'Foo',
9115 body: 'bar bar bar'
9116};
9117const data = JSON.stringify(newPost);
9118httpPost(
9119 'https://jsonplaceholder.typicode.com/posts',
9120 data,
9121 console.log
9122); /*
9123Logs: {
9124 "userId": 1,
9125 "id": 1337,
9126 "title": "Foo",
9127 "body": "bar bar bar"
9128}
9129*/
9130httpPost(
9131 'https://jsonplaceholder.typicode.com/posts',
9132 null, // does not send a body
9133 console.log
9134); /*
9135Logs: {
9136 "id": 101
9137}
9138*/
9139```
9140
9141</details>
9142
9143<br>[⬆ Back to top](#contents)
9144
9145### isBrowser
9146
9147Determines if the current runtime environment is a browser so that front-end modules can run on the server (Node) without throwing errors.
9148
9149Use `Array.prototype.includes()` on the `typeof` values of both `window` and `document` (globals usually only available in a browser environment unless they were explicitly defined), which will return `true` if one of them is `undefined`.
9150`typeof` allows globals to be checked for existence without throwing a `ReferenceError`.
9151If both of them are not `undefined`, then the current environment is assumed to be a browser.
9152
9153```js
9154const isBrowser = () => ![typeof window, typeof document].includes('undefined');
9155```
9156
9157<details>
9158<summary>Examples</summary>
9159
9160```js
9161isBrowser(); // true (browser)
9162isBrowser(); // false (Node)
9163```
9164
9165</details>
9166
9167<br>[⬆ Back to top](#contents)
9168
9169### mostPerformant
9170
9171Returns the index of the function in an array of functions which executed the fastest.
9172
9173Use `Array.prototype.map()` to generate an array where each value is the total time taken to execute the function after `iterations` times. Use the difference in `performance.now()` values before and after to get the total time in milliseconds to a high degree of accuracy.
9174Use `Math.min()` to find the minimum execution time, and return the index of that shortest time which corresponds to the index of the most performant function.
9175Omit the second argument, `iterations`, to use a default of 10,000 iterations. The more iterations, the more reliable the result but the longer it will take.
9176
9177```js
9178const mostPerformant = (fns, iterations = 10000) => {
9179 const times = fns.map(fn => {
9180 const before = performance.now();
9181 for (let i = 0; i < iterations; i++) fn();
9182 return performance.now() - before;
9183 });
9184 return times.indexOf(Math.min(...times));
9185};
9186```
9187
9188<details>
9189<summary>Examples</summary>
9190
9191```js
9192mostPerformant([
9193 () => {
9194 // Loops through the entire array before returning `false`
9195 [1, 2, 3, 4, 5, 6, 7, 8, 9, '10'].every(el => typeof el === 'number');
9196 },
9197 () => {
9198 // Only needs to reach index `1` before returning false
9199 [1, '2', 3, 4, 5, 6, 7, 8, 9, 10].every(el => typeof el === 'number');
9200 }
9201]); // 1
9202```
9203
9204</details>
9205
9206<br>[⬆ Back to top](#contents)
9207
9208### nthArg
9209
9210Creates a function that gets the argument at index `n`. If `n` is negative, the nth argument from the end is returned.
9211
9212Use `Array.prototype.slice()` to get the desired argument at index `n`.
9213
9214```js
9215const nthArg = n => (...args) => args.slice(n)[0];
9216```
9217
9218<details>
9219<summary>Examples</summary>
9220
9221```js
9222const third = nthArg(2);
9223third(1, 2, 3); // 3
9224third(1, 2); // undefined
9225const last = nthArg(-1);
9226last(1, 2, 3, 4, 5); // 5
9227```
9228
9229</details>
9230
9231<br>[⬆ Back to top](#contents)
9232
9233### parseCookie
9234
9235Parse an HTTP Cookie header string and return an object of all cookie name-value pairs.
9236
9237Use `String.prototype.split(';')` to separate key-value pairs from each other.
9238Use `Array.prototype.map()` and `String.prototype.split('=')` to separate keys from values in each pair.
9239Use `Array.prototype.reduce()` and `decodeURIComponent()` to create an object with all key-value pairs.
9240
9241```js
9242const parseCookie = str =>
9243 str
9244 .split(';')
9245 .map(v => v.split('='))
9246 .reduce((acc, v) => {
9247 acc[decodeURIComponent(v[0].trim())] = decodeURIComponent(v[1].trim());
9248 return acc;
9249 }, {});
9250```
9251
9252<details>
9253<summary>Examples</summary>
9254
9255```js
9256parseCookie('foo=bar; equation=E%3Dmc%5E2'); // { foo: 'bar', equation: 'E=mc^2' }
9257```
9258
9259</details>
9260
9261<br>[⬆ Back to top](#contents)
9262
9263### prettyBytes ![advanced](/advanced.svg)
9264
9265Converts a number in bytes to a human-readable string.
9266
9267Use an array dictionary of units to be accessed based on the exponent.
9268Use `Number.toPrecision()` to truncate the number to a certain number of digits.
9269Return the prettified string by building it up, taking into account the supplied options and whether it is negative or not.
9270Omit the second argument, `precision`, to use a default precision of `3` digits.
9271Omit the third argument, `addSpace`, to add space between the number and unit by default.
9272
9273```js
9274const prettyBytes = (num, precision = 3, addSpace = true) => {
9275 const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
9276 if (Math.abs(num) < 1) return num + (addSpace ? ' ' : '') + UNITS[0];
9277 const exponent = Math.min(Math.floor(Math.log10(num < 0 ? -num : num) / 3), UNITS.length - 1);
9278 const n = Number(((num < 0 ? -num : num) / 1000 ** exponent).toPrecision(precision));
9279 return (num < 0 ? '-' : '') + n + (addSpace ? ' ' : '') + UNITS[exponent];
9280};
9281```
9282
9283<details>
9284<summary>Examples</summary>
9285
9286```js
9287prettyBytes(1000); // "1 KB"
9288prettyBytes(-27145424323.5821, 5); // "-27.145 GB"
9289prettyBytes(123456789, 3, false); // "123MB"
9290```
9291
9292</details>
9293
9294<br>[⬆ Back to top](#contents)
9295
9296### randomHexColorCode
9297
9298Generates a random hexadecimal color code.
9299
9300Use `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)`.
9301
9302```js
9303const randomHexColorCode = () => {
9304 let n = (Math.random() * 0xfffff * 1000000).toString(16);
9305 return '#' + n.slice(0, 6);
9306};
9307```
9308
9309<details>
9310<summary>Examples</summary>
9311
9312```js
9313randomHexColorCode(); // "#e34155"
9314```
9315
9316</details>
9317
9318<br>[⬆ Back to top](#contents)
9319
9320### RGBToHex
9321
9322Converts the values of RGB components to a color code.
9323
9324Convert given RGB parameters to hexadecimal string using bitwise left-shift operator (`<<`) and `toString(16)`, then `String.padStart(6,'0')` to get a 6-digit hexadecimal value.
9325
9326```js
9327const RGBToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0');
9328```
9329
9330<details>
9331<summary>Examples</summary>
9332
9333```js
9334RGBToHex(255, 165, 1); // 'ffa501'
9335```
9336
9337</details>
9338
9339<br>[⬆ Back to top](#contents)
9340
9341### serializeCookie
9342
9343Serialize a cookie name-value pair into a Set-Cookie header string.
9344
9345Use template literals and `encodeURIComponent()` to create the appropriate string.
9346
9347```js
9348const serializeCookie = (name, val) => `${encodeURIComponent(name)}=${encodeURIComponent(val)}`;
9349```
9350
9351<details>
9352<summary>Examples</summary>
9353
9354```js
9355serializeCookie('foo', 'bar'); // 'foo=bar'
9356```
9357
9358</details>
9359
9360<br>[⬆ Back to top](#contents)
9361
9362### timeTaken
9363
9364Measures the time taken by a function to execute.
9365
9366Use `console.time()` and `console.timeEnd()` to measure the difference between the start and end times to determine how long the callback took to execute.
9367
9368```js
9369const timeTaken = callback => {
9370 console.time('timeTaken');
9371 const r = callback();
9372 console.timeEnd('timeTaken');
9373 return r;
9374};
9375```
9376
9377<details>
9378<summary>Examples</summary>
9379
9380```js
9381timeTaken(() => Math.pow(2, 10)); // 1024, (logged): timeTaken: 0.02099609375ms
9382```
9383
9384</details>
9385
9386<br>[⬆ Back to top](#contents)
9387
9388### toCurrency
9389
9390Take a number and return specified currency formatting.
9391
9392Use `Intl.NumberFormat` to enable country / currency sensitive formatting.
9393
9394```js
9395const toCurrency = (n, curr, LanguageFormat = undefined) =>
9396 Intl.NumberFormat(LanguageFormat, { style: 'currency', currency: curr }).format(n);
9397```
9398
9399<details>
9400<summary>Examples</summary>
9401
9402```js
9403toCurrency(123456.789, 'EUR'); // €123,456.79 | currency: Euro | currencyLangFormat: Local
9404toCurrency(123456.789, 'USD', 'en-us'); // $123,456.79 | currency: US Dollar | currencyLangFormat: English (United States)
9405toCurrency(123456.789, 'USD', 'fa'); // ۱۲۳٬۴۵۶٫۷۹ ؜$ | currency: US Dollar | currencyLangFormat: Farsi
9406toCurrency(322342436423.2435, 'JPY'); // ¥322,342,436,423 | currency: Japanese Yen | currencyLangFormat: Local
9407toCurrency(322342436423.2435, 'JPY', 'fi'); // 322 342 436 423 ¥ | currency: Japanese Yen | currencyLangFormat: Finnish
9408```
9409
9410</details>
9411
9412<br>[⬆ Back to top](#contents)
9413
9414### toDecimalMark
9415
9416Use `toLocaleString()` to convert a float-point arithmetic to the [Decimal mark](https://en.wikipedia.org/wiki/Decimal_mark) form. It makes a comma separated string from a number.
9417
9418 ```js
9419const toDecimalMark = num => num.toLocaleString('en-US');
9420```
9421
9422<details>
9423<summary>Examples</summary>
9424
9425```js
9426toDecimalMark(12305030388.9087); // "12,305,030,388.909"
9427```
9428
9429</details>
9430
9431<br>[⬆ Back to top](#contents)
9432
9433### toOrdinalSuffix
9434
9435Adds an ordinal suffix to a number.
9436
9437Use the modulo operator (`%`) to find values of single and tens digits.
9438Find which ordinal pattern digits match.
9439If digit is found in teens pattern, use teens ordinal.
9440
9441```js
9442const toOrdinalSuffix = num => {
9443 const int = parseInt(num),
9444 digits = [int % 10, int % 100],
9445 ordinals = ['st', 'nd', 'rd', 'th'],
9446 oPattern = [1, 2, 3, 4],
9447 tPattern = [11, 12, 13, 14, 15, 16, 17, 18, 19];
9448 return oPattern.includes(digits[0]) && !tPattern.includes(digits[1])
9449 ? int + ordinals[digits[0] - 1]
9450 : int + ordinals[3];
9451};
9452```
9453
9454<details>
9455<summary>Examples</summary>
9456
9457```js
9458toOrdinalSuffix('123'); // "123rd"
9459```
9460
9461</details>
9462
9463<br>[⬆ Back to top](#contents)
9464
9465### validateNumber
9466
9467Returns `true` if the given value is a number, `false` otherwise.
9468
9469Use `!isNaN()` in combination with `parseFloat()` to check if the argument is a number.
9470Use `isFinite()` to check if the number is finite.
9471Use `Number()` to check if the coercion holds.
9472
9473```js
9474const validateNumber = n => !isNaN(parseFloat(n)) && isFinite(n) && Number(n) == n;
9475```
9476
9477<details>
9478<summary>Examples</summary>
9479
9480```js
9481validateNumber('10'); // true
9482```
9483
9484</details>
9485
9486<br>[⬆ Back to top](#contents)
9487
9488### yesNo
9489
9490Returns `true` if the string is `y`/`yes` or `false` if the string is `n`/`no`.
9491
9492Use `RegExp.test()` to check if the string evaluates to `y/yes` or `n/no`.
9493Omit the second argument, `def` to set the default answer as `no`.
9494
9495```js
9496const yesNo = (val, def = false) =>
9497 /^(y|yes)$/i.test(val) ? true : /^(n|no)$/i.test(val) ? false : def;
9498```
9499
9500<details>
9501<summary>Examples</summary>
9502
9503```js
9504yesNo('Y'); // true
9505yesNo('yes'); // true
9506yesNo('No'); // false
9507yesNo('Foo', true); // true
9508```
9509
9510</details>
9511
9512<br>[⬆ Back to top](#contents)
9513
9514
9515## Collaborators
9516
9517| [<img src="https://github.com/Chalarangelo.png" width="100px;"/>](https://github.com/Chalarangelo)<br/> [<sub>Angelos Chalaris</sub>](https://github.com/Chalarangelo) | [<img src="https://github.com/flxwu.png" width="100px;"/>](https://github.com/flxwu)<br/> [<sub>Felix Wu</sub>](https://github.com/Pl4gue) | [<img src="https://github.com/fejes713.png" width="100px;"/>](https://github.com/fejes713)<br/> [<sub>Stefan Feješ</sub>](https://github.com/fejes713) | [<img src="https://github.com/kingdavidmartins.png" width="100px;"/>](https://github.com/kingdavidmartins)<br/> [<sub>King David Martins</sub>](https://github.com/iamsoorena) | [<img src="https://github.com/iamsoorena.png" width="100px;"/>](https://github.com/iamsoorena)<br/> [<sub>Soorena Soleimani</sub>](https://github.com/iamsoorena) |
9518| --- | --- | --- | --- | --- |
9519| [<img src="https://github.com/elderhsouza.png" width="100px;"/>](https://github.com/elderhsouza)<br/> [<sub>Elder Henrique Souza</sub>](https://github.com/elderhsouza) | [<img src="https://github.com/skatcat31.png" width="100px;"/>](https://github.com/skatcat31)<br/> [<sub>Robert Mennell</sub>](https://github.com/skatcat31) | [<img src="https://github.com/atomiks.png" width="100px;"/>](https://github.com/atomiks)<br/> [<sub>atomiks</sub>](https://github.com/atomiks) |
9520
9521
9522## Credits
9523
9524*Logos made by [Angelos Chalaris](https://github.com/Chalarangelo) are licensed under the [MIT](https://opensource.org/licenses/MIT) license.*
9525*This README is built using [markdown-builder](https://github.com/30-seconds/markdown-builder).*
9526