diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 000000000..629be083a --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,45 @@ +version: "2" # required to adjust maintainability checks +plugins: + eslint: + enabled: true + markdownlint: + enabled: true + # initial: .mdlrc + # config: .mdlrc.style.rb + fixme: + enabled: true +checks: + argument-count: + config: + threshold: 4 + complex-logic: + config: + threshold: 4 + file-lines: + config: + threshold: 250 + method-complexity: + config: + threshold: 5 + method-count: + config: + threshold: 20 + method-lines: + config: + threshold: 25 + nested-control-flow: + config: + threshold: 4 + return-statements: + config: + threshold: 4 + similar-code: + config: + threshold: # language-specific defaults. an override will affect all languages. + identical-code: + config: + threshold: # language-specific defaults. an override will affect all languages +exclude_patterns: + - "locale/" + - "**/test/" + - "dist/" diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 80717b4fd..000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,25 +0,0 @@ - - - - - -## [FEATURE] _REPLACE THIS WITH A BRIEF SUMMARY OF THE SUGGESTED SNIPPET_ -**Category:** -### Description - - - - - -## [BUG] _REPLACE THIS WITH A BRIEF SUMMARY OF YOUR ISSUE_ -### Expected Snippet Behavior - - - -### Current Snippet Behavior - - - -### Possible Solution - - diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 000000000..02b176d69 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,20 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +## Bug description + + +## Steps to reproduce + + +## Expected behavior + + +## Screenshots + + +## Environment + diff --git a/.github/ISSUE_TEMPLATE/discussion.md b/.github/ISSUE_TEMPLATE/discussion.md new file mode 100644 index 000000000..7a2df3f90 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/discussion.md @@ -0,0 +1,8 @@ +--- +name: Discussion +about: Discuss something with us + +--- + +## Description + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..59b6f461d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,9 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +## Description + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c3ed31c2e..dea9a381e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,36 +1,16 @@ - - - + ## Description - -**Resolves** #(issue number) + + + - -**Lodash[ACTION]** #100 -> https://lodash.com/docs/4.17.4#(method) +## PR Type +- [ ] Snippets, Tests & Tags (new snippets, updated snippets, re-tagging of snippets, added/updated tests) +- [ ] Scripts & Website & Meta (anything related to files in the [scripts folder](https://github.com/30-seconds/30-seconds-of-code/tree/master/scripts), how the repository's automated procedures work and the website) +- [ ] Glossary & Secondary Features (anything related to the glossary, such as new or updated terms or other secondary features) +- [ ] General, Typos, Misc. & Meta (everything else, typos, general stuff and meta files in the repository - e.g. the issue template) -## What does your PR belong to? -- [ ] Website -- [ ] Snippets -- [ ] General / Things regarding the repository (like CI Integration) - -## Types of changes -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] Enhancement (non-breaking improvement of a snippet) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to change) - -## [Lodash Backlog](https://github.com/Chalarangelo/30-seconds-of-code/issues/100) - - -- [ ] I added the prefix [UPDATE: `method.md`] or [ADD: `method.md`] -- [ ] I have referenced the `method` to the lodash backlog. - -## Checklist: - -- [ ] My code follows the code style of this project. -- [ ] My change requires a change to the documentation. -- [ ] I have updated the documentation accordingly. -- [ ] I have checked that the changes are working properly -- [ ] I have checked that there isn't any PR doing the same -- [ ] I have read the **CONTRIBUTING** document. +## Guidelines +- [ ] I have read the guidelines in the [CONTRIBUTING](https://github.com/30-seconds/30-seconds-of-code/blob/master/CONTRIBUTING.md) document. +- [ ] My PR doesn't include any `testlog` changes. diff --git a/.gitignore b/.gitignore index 1672226d8..6b3794e52 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,10 @@ currentSnippet\.js *.md.temp.js .idea test.sh -test/ +/*.log + +dist/flavor\.min\.css + +dist/flavor\.css + +test_old/ diff --git a/.mdlrc b/.mdlrc new file mode 100644 index 000000000..1399044eb --- /dev/null +++ b/.mdlrc @@ -0,0 +1,12 @@ +rules "MD003", // header style - atx + "MD004", // ul list style - asterisk + "MD009", // no trailing whitespaces + "MD010", // no hard tabs + "MD011", // no reversed links + "MD018", // no space after hash on atx style header + "MD023", // headers should start on the start of the line + "MD025", // no multiple h1 headers + "MD031", // blanks around fences + "MD038", // no spaces inside code elements + "MD040" // fenced code blocks need to have a language specified +style ".mdlrc.style.rb" diff --git a/.mdlrc.style.rb b/.mdlrc.style.rb new file mode 100644 index 000000000..5118afccf --- /dev/null +++ b/.mdlrc.style.rb @@ -0,0 +1,7 @@ +all +rule 'header-style', :style => "atx" +rule 'ul-style', :style => "asterisk" + +exclude_rule 'first-header-h1' +exclude_rule 'first-line-h1' +exclude_rule 'no-inline-html' diff --git a/.npmignore b/.npmignore new file mode 100644 index 000000000..d01bec1b1 --- /dev/null +++ b/.npmignore @@ -0,0 +1,19 @@ +snippet-template.md +tag_database +travis.log +CONTRIBUTING.md +COLLABORATING.md +CODE_OF_CONDUCT.md +.travis.yml +.mdlrc.style.rb +.mdlrc +.codeclimate.yml +test/* +static-parts/* +snippet_data/* +snippets_archive/* +scripts/* +locale/* +docs/* +.travis/* +.github/* diff --git a/.travis.yml b/.travis.yml index ffb9c3da1..611fd5ae7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,9 +10,13 @@ before_install: script: - npm run tagger - npm run linter -- npm run builder - npm run webber -- npm run module +- npm run builder +- npm run packager +- npm run tester +- npm run extractor +- npm run glossary:keymaker +- npm run glossary:librarian after_success: - chmod +x .travis/push.sh - .travis/push.sh diff --git a/.travis/push.sh b/.travis/push.sh index 174bb5311..3b45d56a5 100755 --- a/.travis/push.sh +++ b/.travis/push.sh @@ -1,3 +1,4 @@ +#!/bin/bash setup_git() { git config --global user.email "30secondsofcode@gmail.com" git config --global user.name "30secondsofcode" @@ -24,7 +25,7 @@ upload_files() { if [ $TRAVIS_EVENT_TYPE != "pull_request" ]; then if [ $TRAVIS_BRANCH == "master" ]; then echo "Pushing to master branch..." - git push --force --quiet "https://${GH_TOKEN}@github.com/Chalarangelo/30-seconds-of-code.git" master > /dev/null 2>&1 + git push --force --quiet "https://${GH_TOKEN}@github.com/30-seconds/30-seconds-of-code.git" master > /dev/null 2>&1 fi fi } diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index df46a145a..53001a0b0 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at chalarangelo@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at 30secondsofcode@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. diff --git a/COLLABORATING.md b/COLLABORATING.md index a38c4d52d..56562d5d4 100644 --- a/COLLABORATING.md +++ b/COLLABORATING.md @@ -8,17 +8,17 @@ As a member of the team that manages **30 seconds of code**, you have the follow - **Be part of the conversation in the issue tracker.** That includes (but is not limited to) helping out new members, discussing new features and explaining decisions to people. - **Review pull requests.** You do not have to read through all of the pull requests and review them, but taking the time each day to review a few can help a great deal. -- **Be civil and polite.** If you are about to lose your temper, take a step back and go do something else. We want our interactions with the community to be polite, so that more people can join the project and contribute in any way they can. Remember to always thank contributors for their help, even if it's minor changes or changes that did not make it into the project. This way we can reward and encourage people to keep being part of the community. +- **Be civil and polite.** If you are about to lose your temper, take a step back and do something else. We want our interactions with the community to be polite so that more people can join the project and contribute in any way they can. Remember to always thank contributors for their help, even if it's minor changes or changes that did not make it into the project. This way we can reward and encourage people to keep being part of the community. - **Contribute when you want, moderate when you can.** If you have a lot on your plate outside of this project, it's alright. It's better to take a break for a few days rather than hastily deal with issues and pull requests that might break things. ## Guidelines for merging pull requests and making changes to the project -- **[Usual guidelines](https://github.com/Chalarangelo/30-seconds-of-code/blob/master/CONTRIBUTING.md) apply.** Make sure to follow them, like everybody else. +- **[Usual guidelines](https://github.com/30-seconds/30-seconds-of-code/blob/master/CONTRIBUTING.md) apply.** Make sure to follow them, like everybody else. - **For a pull request to be considered ready to merge, there should be at least 2 (preferably 3) reviews approving it for merge.** There are, however, certain exceptions: - - **If a pull request only fixes typos**, there is no need to wait for a second reviewer (unless you are not absolutely certain these were not typos in the first place). - - **If a pull request only clarifies a snippet's description or enforces the styleguide for an existng snippet**, you might be able to merge it without getting a second reviewer to review it, but only if you are absolutely certain about it. + - **If a pull request only fixes typos**, there is no need to wait for a second reviewer (unless you are not certain these were not typos in the first place). + - **If a pull request only clarifies a snippet's description or enforces the style guide for an existing snippet**, you might be able to merge it without getting a second reviewer to review it, but only if you are certain about it. - **Make sure pull requests pass the Travis CI build**, otherwise try and find out what's wrong and inform the author of the pull request. - **Changes to build scripts, guidelines and things that might break the processes we have in place need to be reviewed by [@Chalarangelo](https://github.com/Chalarangelo)** (this is temporary, but we need a baseline to make sure we break as few things as possible in the beginning). - **After merging a pull request, make sure to check for untagged snippets and tag them appropriately.** Try to keep all snippets tagged, so that the list and website are up to date. -- **If you make changes or additions to existing snippets or if you want to add you own snippets, you will go through the pull request process that everyone else goes.** Exceptions apply similarly to the ones mentioned above about merging pull requests (i.e. typos, description clarification and the way script and build process changes are handled). Pull requests suggested by collaborators should be reviewed by at least two other collaborators to be considered ready to merge. +- **If you make changes or additions to existing snippets or if you want to add your own snippets, you will go through the pull request process that everyone else goes.** Exceptions apply similarly to the ones mentioned above about merging pull requests (i.e. typos, description clarification and the way script and build process changes are handled). Pull requests suggested by collaborators should be reviewed by at least two other collaborators to be considered ready to merge. - **Pull requests that are inactive for over a week should be closed or put on hold.** diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c274e0e09..7b5b4cdcc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,23 +1,23 @@ -## Contributing +![contribution guidelines](https://i.imgur.com/8Wk9nat.png) **30 seconds of code** is a community effort, so feel free to contribute in any way you can. Every contribution helps! Here's what you can do to help: -- [Open issues](https://github.com/Chalarangelo/30-seconds-of-code/issues/new) for things you want to see added or modified. -- Be part of the discussion by helping out with [existing issues](https://github.com/Chalarangelo/30-seconds-of-code/issues) or talking on our [gitter channel](https://gitter.im/30-seconds-of-code/Lobby). -- Submit [pull requests](https://github.com/Chalarangelo/30-seconds-of-code/pulls) with snippets you have created (see below for guidelines). -- Tag uncategorized snippets by running `npm run tagger` and adding the appropriate tag next to the script name in `tag_database`. +- Submit [pull requests](https://github.com/30-seconds/30-seconds-of-code/pulls) with snippets and tests that you have created (see below for guidelines). +- [Open issues](https://github.com/30-seconds/30-seconds-of-code/issues/new) for things you want to see added or modified. +- Be part of the discussion by helping out with [existing issues](https://github.com/30-seconds/30-seconds-of-code/issues) or talking on our [gitter channel](https://gitter.im/30-seconds-of-code/Lobby). +- Tag uncategorized snippets by running `npm run tagger` and adding the appropriate tags next to the script name in `tag_database`. - Fix typos in existing snippets, improve snippet descriptions and explanations or provide better examples. +- Write tests for existing snippets (see below for guidelines). ### Snippet submission and Pull request guidelines -- **DO NOT MODIFY THE README.md FILE!** Make changes to individual snippet files. You can optionally run `npm run builder` to update the README.md file automatically, based on the changes you have made. -- **DO NOT MODIFY THE index.html FILE!** Make changes to individual snippet files. You can optionally run `npm run webber` to update the index.md file automatically, based on the changes you have made. +- **DO NOT MODIFY THE README.md or index.html FILES!** Make changes to individual snippet files. **Travis CI** will automatically build the `README.md` and `index.html` files when your pull request is merged. - **Snippet filenames** must correspond to the title of the snippet. For example, if your snippet is titled `### awesomeSnippet` the filename should be `awesomeSnippet.md`. - Use `camelCase`, not `kebab-case` or `snake_case`. - Avoid capitalization of words, except if the whole word is capitalized (e.g. `URL` should be capitalized in the filename and the snippet title). -- **Snippet titles** should have be the same as the name of the function that is present in the snippet. +- **Snippet titles** should be the same as the name of the function that is present in the snippet. - All snippet titles must be prefixed with `###` and be at the very first line of your snippet. - Snippet titles must be unique (although if you cannot find a better title, just add some placeholder at the end of the filename and title and we will figure it out). - Follow snippet titles with an empty line. @@ -26,7 +26,7 @@ Here's what you can do to help: - **Snippet code** must be enclosed inside ` ```js ` and ` ``` `. - Remember to start your snippet's code on a new line below the opening backticks. - Use ES6 notation to define your function. For example `const myFunction = ( arg1, arg2 ) => { }`. - - Please use Javacript [Semi-Standard Style](https://github.com/Flet/semistandard). + - Please use Javascript [Semi-Standard Style](https://github.com/Flet/semistandard). - Try to keep your snippets' code short and to the point. Use modern techniques and features. Make sure to test your code before submitting. - All snippets must be followed by one (more if necessary) test case after the code, in a new block enclosed inside ` ```js ` and ` ``` `. The syntax for this is `myFunction('testInput') // 'testOutput'`. Use multiline examples only if necessary. - Try to make your function name unique, so that it does not conflict with existing snippets. @@ -36,11 +36,23 @@ Here's what you can do to help: - Snippets *should* be abstract enough to be applied to different scenarios. - It is not mandatory but highly appreciated if you provide **test cases** and/or performance tests (we recommend using [jsPerf](https://jsperf.com/)). - You can start creating a new snippet, by using the [snippet template](snippet-template.md) to format your snippets. -- Updating the index.html or README.md files should only be done by altering the scripts in the **scripts** folder or altering their relative static parts in the **static-parts** folder. + +### Writing tests +- Before writing any tests run `npm run tester` script. It will update test directory to include new snippets as well as update old ones if needed. +- **DO NOT MODIFY THE snippetName.js files** under test directory. +- We are using [tape](https://github.com/substack/tape) for testing. +- Write tests under `snippetName.test.js` file. If you have trouble doing so, check out tests of other snippets. +- Be sure to run `npm run test`. It is going to run all tests for all snippets. +- Make a new pull request **only if all the tests are passing**. + +#### Browser specific tests +- If your snippet belongs to `browser` category, then you will need to modify the tests to make them work. +- By default, `Node.js` isn't browser environment. That said we have to use an external package to help us simulate the browser for our tests. +- We use [jsdom](https://www.npmjs.com/package/jsdom) for our browser specific tests. You can find their [documentation](https://github.com/jsdom/jsdom) on GitHub as well. ### Additional guidelines and conventions regarding snippets -- When describing snippets, refer to methods, using their full name. For example, use `Array.reduce()`, instead of `reduce()`. +- When describing snippets, refer to methods, using their full name. For example, use `Array.prototype.reduce()`, instead of `reduce()`. - If your snippet contains argument with default parameters, explain what happens if they are omitted when calling the function and what the default case is. - If your snippet uses recursion, explain the base cases. - Always use `const functionName` for function definitions. @@ -49,16 +61,16 @@ Here's what you can do to help: - Try to give meaningful names to variables. For example use `letter`, instead of `lt`. Some exceptions to convention are: - `arr` for arrays (usually as the snippet function's argument). - `str` for strings. - - `n` for a numeric value (usually as the snippet function's argument). + - `num` or `n` for a numeric value (usually as the snippet function's argument). - `el` for DOM elements (usually as the snippet function's argument). - `val` or `v` for value (usually when iterating a list, mapping, sorting etc.). - - `acc` for accumulators in `Array.reduce()`. - - `(a,b)` for the two values compared when using `Array.sort()`. + - `acc` for accumulators in `Array.prototype.reduce()`. + - `(a,b)` for the two values compared when using `Array.prototype.sort()`. - `i` for indexes. - - `func` for function arguments. + - `fn` for function arguments. - `nums` for arrays of numbers. - Use `()` if your function takes no arguments. -- Use `_` if an argument inside some function (e.g. `Array.reduce()`) is not used anywhere in your code. +- Use `_` if an argument inside some function (e.g. `Array.prototype.reduce()`) is not used anywhere in your code. - Specify default parameters for arguments, if necessary. It is preferred to put default parameters last unless you have pretty good reason not to. - If your snippet's function takes variadic arguments, use `...args` (although in certain cases, it might be needed to use a different name). - If your snippet function's body is a single statement, omit the `return` keyword and use an expression instead. @@ -67,7 +79,7 @@ Here's what you can do to help: - Always use single quotes for string literals. Use template literals, instead, if necessary. - If your snippet's code is short enough (around 80 characters), you can make it a single-line function (although not mandatory). Otherwise, use multiple lines. - Prefer using `Array` methods whenever possible. -- Prefer `Array.concat()` instead of `Array.push()` when working with `Array.reduce()`. +- Prefer `Array.prototype.concat()` instead of `Array.prototype.push()` when working with `Array.prototype.reduce()`. - Use strict equality checking (`===` and `!==` instead of `==` and `!=`), unless you specifically have reason not to. - Prefer using the ternary operator (`condition ? trueResult : falseResult`) instead of `if else` statements whenever possible. - Avoid nesting ternary operators (but you can do it if you feel like you should). diff --git a/README.md b/README.md index eb88d68c1..7f59de281 100644 --- a/README.md +++ b/README.md @@ -2,26 +2,40 @@ # 30 seconds of code -[![License](https://img.shields.io/badge/license-CC0--1.0-blue.svg)](https://github.com/Chalarangelo/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) [![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) [![Travis Build](https://travis-ci.org/Chalarangelo/30-seconds-of-code.svg?branch=master)](https://travis-ci.org/Chalarangelo/30-seconds-of-code) [![Insight.io](https://img.shields.io/badge/insight.io-Ready-brightgreen.svg)](https://insight.io/github.com/Chalarangelo/30-seconds-of-code/tree/master/?source=0) [![js-semistandard-style](https://img.shields.io/badge/code%20style-semistandard-brightgreen.svg)](https://github.com/Flet/semistandard) [![ProductHunt](https://img.shields.io/badge/producthunt-vote-orange.svg)](https://www.producthunt.com/posts/30-seconds-of-code) +[![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) [![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) [![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) [![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) [![ProductHunt](https://img.shields.io/badge/producthunt-vote-orange.svg)](https://www.producthunt.com/posts/30-seconds-of-code) > Curated collection of useful JavaScript snippets that you can understand in 30 seconds or less. +[![Sponsored by DigitalOcean](/sponsored_by_DigitalOcean.png)](https://www.digitalocean.com) + - Use Ctrl + F or command + F to search for a snippet. - Contributions welcome, please read the [contribution guide](CONTRIBUTING.md). - Snippets are written in ES6, use the [Babel transpiler](https://babeljs.io/) to ensure backwards-compatibility. - You can import these snippets into your text editor of choice (VSCode, Atom, Sublime) using the files found in [this repo](https://github.com/Rob-Rychs/30-seconds-of-code-texteditorsnippets). - You can import these snippets into Alfred 3, using [this file](https://github.com/lslvxy/30-seconds-of-code-alfredsnippets). +- 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). + +#### Related projects + +- [30 Seconds of CSS](https://30-seconds.github.io/30-seconds-of-css/) +- [30 Seconds of Interviews](https://30secondsofinterviews.org/) +- [30 Seconds of Python](https://github.com/kriadmin/30-seconds-of-python-code) *(unofficial)* +- [30 Seconds of PHP](https://github.com/appzcoder/30-seconds-of-php-code) *(unofficial)* #### Package -⚠️ **WARNING:** Snippets are not production ready. +⚠️ **NOTICE:** A few of our snippets are not yet optimized for production (see disclaimers for individual snippet issues). -You can find a package with all the snippets on [npm](https://www.npmjs.com/package/30-seconds-of-code). +You can find a package with all the snippets on [npm](https://www.npmjs.com/package/30-seconds-of-code). -``` +```bash +# With npm npm install 30-seconds-of-code + +# With yarn +yarn add 30-seconds-of-code ``` CDN links @@ -29,6 +43,7 @@ CDN links - [ES5 Minified (UMD)](https://unpkg.com/30-seconds-of-code/dist/_30s.es5.min.js)
+Details **Browser** @@ -73,12 +88,18 @@ average(1, 2, 3);
View contents +* [`ary`](#ary) * [`call`](#call) * [`collectInto`](#collectinto) * [`flip`](#flip) +* [`over`](#over) +* [`overArgs`](#overargs) +* [`pipeAsyncFunctions`](#pipeasyncfunctions) * [`pipeFunctions`](#pipefunctions) * [`promisify`](#promisify) +* [`rearg`](#rearg) * [`spreadOver`](#spreadover) +* [`unary`](#unary)
@@ -87,53 +108,98 @@ average(1, 2, 3);
View contents +* [`all`](#all) +* [`allEqual`](#allequal) +* [`any`](#any) +* [`arrayToCSV`](#arraytocsv) +* [`bifurcate`](#bifurcate) +* [`bifurcateBy`](#bifurcateby) * [`chunk`](#chunk) * [`compact`](#compact) +* [`countBy`](#countby) * [`countOccurrences`](#countoccurrences) * [`deepFlatten`](#deepflatten) * [`difference`](#difference) +* [`differenceBy`](#differenceby) * [`differenceWith`](#differencewith) -* [`distinctValuesOfArray`](#distinctvaluesofarray) -* [`dropElements`](#dropelements) +* [`drop`](#drop) * [`dropRight`](#dropright) +* [`dropRightWhile`](#droprightwhile) +* [`dropWhile`](#dropwhile) * [`everyNth`](#everynth) * [`filterNonUnique`](#filternonunique) +* [`filterNonUniqueBy`](#filternonuniqueby) +* [`findLast`](#findlast) +* [`findLastIndex`](#findlastindex) * [`flatten`](#flatten) -* [`flattenDepth`](#flattendepth) +* [`forEachRight`](#foreachright) * [`groupBy`](#groupby) * [`head`](#head) +* [`indexOfAll`](#indexofall) * [`initial`](#initial) * [`initialize2DArray`](#initialize2darray) * [`initializeArrayWithRange`](#initializearraywithrange) +* [`initializeArrayWithRangeRight`](#initializearraywithrangeright) * [`initializeArrayWithValues`](#initializearraywithvalues) +* [`initializeNDArray`](#initializendarray) * [`intersection`](#intersection) +* [`intersectionBy`](#intersectionby) +* [`intersectionWith`](#intersectionwith) * [`isSorted`](#issorted) * [`join`](#join) +* [`JSONtoCSV`](#jsontocsv-) * [`last`](#last) -* [`mapObject`](#mapobject) +* [`longestItem`](#longestitem) +* [`mapObject`](#mapobject-) * [`maxN`](#maxn) * [`minN`](#minn) +* [`none`](#none) * [`nthElement`](#nthelement) -* [`pick`](#pick) +* [`offset`](#offset) +* [`partition`](#partition) +* [`permutations`](#permutations-) * [`pull`](#pull) -* [`pullAtIndex`](#pullatindex) -* [`pullAtValue`](#pullatvalue) -* [`quickSort`](#quicksort) +* [`pullAtIndex`](#pullatindex-) +* [`pullAtValue`](#pullatvalue-) +* [`pullBy`](#pullby-) * [`reducedFilter`](#reducedfilter) +* [`reduceSuccessive`](#reducesuccessive) +* [`reduceWhich`](#reducewhich) +* [`reject`](#reject) * [`remove`](#remove) * [`sample`](#sample) * [`sampleSize`](#samplesize) +* [`shank`](#shank) * [`shuffle`](#shuffle) * [`similarity`](#similarity) * [`sortedIndex`](#sortedindex) +* [`sortedIndexBy`](#sortedindexby) +* [`sortedLastIndex`](#sortedlastindex) +* [`sortedLastIndexBy`](#sortedlastindexby) +* [`stableSort`](#stablesort-) * [`symmetricDifference`](#symmetricdifference) +* [`symmetricDifferenceBy`](#symmetricdifferenceby) +* [`symmetricDifferenceWith`](#symmetricdifferencewith) * [`tail`](#tail) * [`take`](#take) * [`takeRight`](#takeright) +* [`takeRightWhile`](#takerightwhile) +* [`takeWhile`](#takewhile) +* [`toHash`](#tohash) * [`union`](#union) +* [`unionBy`](#unionby) +* [`unionWith`](#unionwith) +* [`uniqueElements`](#uniqueelements) +* [`uniqueElementsBy`](#uniqueelementsby) +* [`uniqueElementsByRight`](#uniqueelementsbyright) +* [`uniqueSymmetricDifference`](#uniquesymmetricdifference) +* [`unzip`](#unzip) +* [`unzipWith`](#unzipwith-) * [`without`](#without) +* [`xProd`](#xprod) * [`zip`](#zip) * [`zipObject`](#zipobject) +* [`zipWith`](#zipwith-)
@@ -144,23 +210,39 @@ average(1, 2, 3); * [`arrayToHtmlList`](#arraytohtmllist) * [`bottomVisible`](#bottomvisible) -* [`copyToClipboard`](#copytoclipboard) +* [`copyToClipboard`](#copytoclipboard-) +* [`counter`](#counter-) +* [`createElement`](#createelement) +* [`createEventHub`](#createeventhub-) * [`currentURL`](#currenturl) * [`detectDeviceType`](#detectdevicetype) -* [`elementIsVisibleInViewport`](#elementisvisibleinviewport) +* [`elementContains`](#elementcontains) +* [`elementIsVisibleInViewport`](#elementisvisibleinviewport-) +* [`getImages`](#getimages) * [`getScrollPosition`](#getscrollposition) * [`getStyle`](#getstyle) * [`hasClass`](#hasclass) +* [`hashBrowser`](#hashbrowser-) * [`hide`](#hide) * [`httpsRedirect`](#httpsredirect) -* [`onUserInputChange`](#onuserinputchange) +* [`insertAfter`](#insertafter) +* [`insertBefore`](#insertbefore) +* [`isBrowserTabFocused`](#isbrowsertabfocused) +* [`nodeListToArray`](#nodelisttoarray) +* [`observeMutations`](#observemutations-) +* [`off`](#off) +* [`on`](#on) +* [`onUserInputChange`](#onuserinputchange-) +* [`prefix`](#prefix) +* [`recordAnimationFrames`](#recordanimationframes) * [`redirect`](#redirect) -* [`runAsync`](#runasync) +* [`runAsync`](#runasync-) * [`scrollToTop`](#scrolltotop) * [`setStyle`](#setstyle) * [`show`](#show) -* [`speechSynthesis`](#speechsynthesis) +* [`smoothScroll`](#smoothscroll) * [`toggleClass`](#toggleclass) +* [`triggerEvent`](#triggerevent) * [`UUIDGeneratorBrowser`](#uuidgeneratorbrowser)
@@ -170,10 +252,16 @@ average(1, 2, 3);
View contents +* [`dayOfYear`](#dayofyear) * [`formatDuration`](#formatduration) +* [`getColonTimeFromDate`](#getcolontimefromdate) * [`getDaysDiffBetweenDates`](#getdaysdiffbetweendates) -* [`JSONToDate`](#jsontodate) -* [`toEnglishDate`](#toenglishdate) +* [`getMeridiemSuffixOfInteger`](#getmeridiemsuffixofinteger) +* [`isAfterDate`](#isafterdate) +* [`isBeforeDate`](#isbeforedate) +* [`isSameDate`](#issamedate) +* [`maxDate`](#maxdate) +* [`minDate`](#mindate) * [`tomorrow`](#tomorrow)
@@ -183,24 +271,31 @@ average(1, 2, 3);
View contents +* [`attempt`](#attempt) +* [`bind`](#bind) +* [`bindKey`](#bindkey) * [`chainAsync`](#chainasync) * [`compose`](#compose) +* [`composeRight`](#composeright) +* [`converge`](#converge) * [`curry`](#curry) +* [`debounce`](#debounce) * [`defer`](#defer) +* [`delay`](#delay) * [`functionName`](#functionname) -* [`memoize`](#memoize) +* [`hz`](#hz) +* [`memoize`](#memoize-) +* [`negate`](#negate) * [`once`](#once) +* [`partial`](#partial) +* [`partialRight`](#partialright) * [`runPromisesInSeries`](#runpromisesinseries) * [`sleep`](#sleep) - -
- -### 🔮 Logic - -
-View contents - -* [`negate`](#negate) +* [`throttle`](#throttle-) +* [`times`](#times) +* [`uncurry`](#uncurry) +* [`unfold`](#unfold) +* [`when`](#when)
@@ -209,39 +304,43 @@ average(1, 2, 3);
View contents +* [`approximatelyEqual`](#approximatelyequal) * [`average`](#average) +* [`averageBy`](#averageby) +* [`binomialCoefficient`](#binomialcoefficient) * [`clampNumber`](#clampnumber) -* [`collatz`](#collatz) +* [`degreesToRads`](#degreestorads) * [`digitize`](#digitize) * [`distance`](#distance) -* [`elo`](#elo) +* [`elo`](#elo-) * [`factorial`](#factorial) -* [`factors`](#factors) * [`fibonacci`](#fibonacci) -* [`fibonacciCountUntilNum`](#fibonaccicountuntilnum) -* [`fibonacciUntilNum`](#fibonacciuntilnum) * [`gcd`](#gcd) * [`geometricProgression`](#geometricprogression) * [`hammingDistance`](#hammingdistance) -* [`howManyTimes`](#howmanytimes) * [`inRange`](#inrange) -* [`isArmstrongNumber`](#isarmstrongnumber) * [`isDivisible`](#isdivisible) * [`isEven`](#iseven) * [`isPrime`](#isprime) * [`lcm`](#lcm) -* [`luhnCheck`](#luhncheck) +* [`luhnCheck`](#luhncheck-) +* [`maxBy`](#maxby) * [`median`](#median) +* [`minBy`](#minby) * [`percentile`](#percentile) * [`powerset`](#powerset) * [`primes`](#primes) +* [`radsToDegrees`](#radstodegrees) +* [`randomIntArrayInRange`](#randomintarrayinrange) * [`randomIntegerInRange`](#randomintegerinrange) * [`randomNumberInRange`](#randomnumberinrange) * [`round`](#round) -* [`solveRPN`](#solverpn) +* [`sdbm`](#sdbm) * [`standardDeviation`](#standarddeviation) * [`sum`](#sum) +* [`sumBy`](#sumby) * [`sumPower`](#sumpower) +* [`toSafeInteger`](#tosafeinteger)
@@ -250,8 +349,16 @@ average(1, 2, 3);
View contents +* [`atob`](#atob) +* [`btoa`](#btoa) +* [`colorize`](#colorize) * [`hasFlags`](#hasflags) +* [`hashNode`](#hashnode) +* [`isDuplexStream`](#isduplexstream) +* [`isReadableStream`](#isreadablestream) +* [`isStream`](#isstream) * [`isTravisCI`](#istravisci) +* [`isWritableStream`](#iswritablestream) * [`JSONToFile`](#jsontofile) * [`readFileLines`](#readfilelines) * [`untildify`](#untildify) @@ -264,16 +371,40 @@ average(1, 2, 3);
View contents -* [`cleanObj`](#cleanobj) +* [`bindAll`](#bindall) +* [`deepClone`](#deepclone) +* [`deepFreeze`](#deepfreeze) +* [`defaults`](#defaults) +* [`dig`](#dig) +* [`equals`](#equals-) +* [`findKey`](#findkey) +* [`findLastKey`](#findlastkey) +* [`flattenObject`](#flattenobject) +* [`forOwn`](#forown) +* [`forOwnRight`](#forownright) +* [`functions`](#functions) +* [`get`](#get) * [`invertKeyValues`](#invertkeyvalues) * [`lowercaseKeys`](#lowercasekeys) +* [`mapKeys`](#mapkeys) +* [`mapValues`](#mapvalues) +* [`matches`](#matches) +* [`matchesWith`](#matcheswith) +* [`merge`](#merge) +* [`nest`](#nest) * [`objectFromPairs`](#objectfrompairs) * [`objectToPairs`](#objecttopairs) +* [`omit`](#omit) +* [`omitBy`](#omitby) * [`orderBy`](#orderby) -* [`select`](#select) +* [`pick`](#pick) +* [`pickBy`](#pickby) +* [`renameKeys`](#renamekeys) * [`shallowClone`](#shallowclone) * [`size`](#size) +* [`transform`](#transform) * [`truthCheckCollection`](#truthcheckcollection) +* [`unflattenObject`](#unflattenobject-)
@@ -282,59 +413,91 @@ average(1, 2, 3);
View contents -* [`anagrams`](#anagrams) * [`byteSize`](#bytesize) * [`capitalize`](#capitalize) * [`capitalizeEveryWord`](#capitalizeeveryword) -* [`countVowels`](#countvowels) +* [`CSVToArray`](#csvtoarray) +* [`CSVToJSON`](#csvtojson-) +* [`decapitalize`](#decapitalize) * [`escapeHTML`](#escapehtml) * [`escapeRegExp`](#escaperegexp) * [`fromCamelCase`](#fromcamelcase) +* [`indentString`](#indentstring) * [`isAbsoluteURL`](#isabsoluteurl) +* [`isAnagram`](#isanagram) +* [`isLowerCase`](#islowercase) +* [`isUpperCase`](#isuppercase) +* [`mapString`](#mapstring) * [`mask`](#mask) +* [`pad`](#pad) * [`palindrome`](#palindrome) * [`pluralize`](#pluralize) -* [`repeatString`](#repeatstring) +* [`removeNonASCII`](#removenonascii) * [`reverseString`](#reversestring) * [`sortCharactersInString`](#sortcharactersinstring) * [`splitLines`](#splitlines) +* [`stringPermutations`](#stringpermutations-) +* [`stripHTMLTags`](#striphtmltags) * [`toCamelCase`](#tocamelcase) * [`toKebabCase`](#tokebabcase) * [`toSnakeCase`](#tosnakecase) * [`truncateString`](#truncatestring) * [`unescapeHTML`](#unescapehtml) +* [`URLJoin`](#urljoin-) * [`words`](#words)
+### 📃 Type + +
+View contents + +* [`getType`](#gettype) +* [`is`](#is) +* [`isArrayLike`](#isarraylike) +* [`isBoolean`](#isboolean) +* [`isEmpty`](#isempty) +* [`isFunction`](#isfunction) +* [`isNil`](#isnil) +* [`isNull`](#isnull) +* [`isNumber`](#isnumber) +* [`isObject`](#isobject) +* [`isObjectLike`](#isobjectlike) +* [`isPlainObject`](#isplainobject) +* [`isPrimitive`](#isprimitive) +* [`isPromiseLike`](#ispromiselike) +* [`isString`](#isstring) +* [`isSymbol`](#issymbol) +* [`isUndefined`](#isundefined) +* [`isValidJSON`](#isvalidjson) + +
+ ### 🔧 Utility
View contents +* [`castArray`](#castarray) * [`cloneRegExp`](#cloneregexp) * [`coalesce`](#coalesce) * [`coalesceFactory`](#coalescefactory) * [`extendHex`](#extendhex) -* [`getType`](#gettype) * [`getURLParameters`](#geturlparameters) -* [`hexToRGB`](#hextorgb) -* [`isArray`](#isarray) -* [`isArrayLike`](#isarraylike) -* [`isBoolean`](#isboolean) -* [`isFunction`](#isfunction) -* [`isNull`](#isnull) -* [`isNumber`](#isnumber) -* [`isPrimitive`](#isprimitive) -* [`isPromiseLike`](#ispromiselike) -* [`isString`](#isstring) -* [`isSymbol`](#issymbol) -* [`isValidJSON`](#isvalidjson) -* [`prettyBytes`](#prettybytes) +* [`hexToRGB`](#hextorgb-) +* [`httpGet`](#httpget) +* [`httpPost`](#httppost) +* [`isBrowser`](#isbrowser) +* [`mostPerformant`](#mostperformant) +* [`nthArg`](#ntharg) +* [`parseCookie`](#parsecookie) +* [`prettyBytes`](#prettybytes-) * [`randomHexColorCode`](#randomhexcolorcode) * [`RGBToHex`](#rgbtohex) -* [`sdbm`](#sdbm) +* [`serializeCookie`](#serializecookie) * [`timeTaken`](#timetaken) +* [`toCurrency`](#tocurrency) * [`toDecimalMark`](#todecimalmark) * [`toOrdinalSuffix`](#toordinalsuffix) * [`validateNumber`](#validatenumber) @@ -342,8 +505,32 @@ average(1, 2, 3);
+ --- - ## 🔌 Adapter + +## 🔌 Adapter + +### ary + +Creates a function that accepts up to `n` arguments, ignoring any additional arguments. + +Call the provided function, `fn`, with up to `n` arguments, using `Array.prototype.slice(0,n)` and the spread operator (`...`). + +```js +const ary = (fn, n) => (...args) => fn(...args.slice(0, n)); +``` + +
+Examples + +```js +const firstTwoMax = ary(Math.max, 2); +[[2, 6, 'a'], [8, 4, 6], [10]].map(x => firstTwoMax(...x)); // [6, 8, 10] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### call @@ -353,8 +540,8 @@ Use a closure to call a stored key with stored arguments. ```js const call = (key, ...args) => context => context[key](...args); -``` - +``` +
Examples @@ -372,7 +559,6 @@ Promise.resolve([1, 2, 3])
[⬆ Back to top](#table-of-contents) - ### collectInto Changes a function that accepts an array into a variadic function. @@ -381,8 +567,8 @@ Given a function, return a closure that collects all inputs into an array-accept ```js const collectInto = fn => (...args) => fn(args); -``` - +``` +
Examples @@ -391,24 +577,23 @@ const Pall = collectInto(Promise.all.bind(Promise)); let p1 = Promise.resolve(1); let p2 = Promise.resolve(2); let p3 = new Promise(resolve => setTimeout(resolve, 2000, 3)); -Pall(p1, p2, p3).then(console.log); +Pall(p1, p2, p3).then(console.log); // [1, 2, 3] (after about 2 seconds) ```

[⬆ Back to top](#table-of-contents) - ### flip -Flip takes a function as an argument, then makes the first argument the last +Flip takes a function as an argument, then makes the first argument the last. Return a closure that takes variadic inputs, and splices the last argument to make it the first argument before applying the rest. ```js -const flip = fn => (...args) => fn(args.pop(), ...args); -``` - +const flip = fn => (first, ...rest) => fn(...rest, first); +``` +
Examples @@ -426,12 +611,88 @@ Object.assign(b, a); // == b
[⬆ Back to top](#table-of-contents) +### over + +Creates a function that invokes each provided function with the arguments it receives and returns the results. + +Use `Array.prototype.map()` and `Function.prototype.apply()` to apply each function to the given arguments. + +```js +const over = (...fns) => (...args) => fns.map(fn => fn.apply(null, args)); +``` + +
+Examples + +```js +const minMax = over(Math.min, Math.max); +minMax(1, 2, 3, 4, 5); // [1,5] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### overArgs + +Creates a function that invokes the provided function with its arguments transformed. + +Use `Array.prototype.map()` to apply `transforms` to `args` in combination with the spread operator (`...`) to pass the transformed arguments to `fn`. + +```js +const overArgs = (fn, transforms) => (...args) => fn(...args.map((val, i) => transforms[i](val))); +``` + +
+Examples + +```js +const square = n => n * n; +const double = n => n * 2; +const fn = overArgs((x, y) => [x, y], [square, double]); +fn(9, 3); // [81, 6] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### pipeAsyncFunctions + +Performs left-to-right function composition for asynchronous functions. + +Use `Array.prototype.reduce()` with the spread operator (`...`) to perform left-to-right function composition using `Promise.then()`. +The functions can return a combination of: simple values, `Promise`'s, or they can be defined as `async` ones returning through `await`. +All functions must be unary. + +```js +const pipeAsyncFunctions = (...fns) => arg => fns.reduce((p, f) => p.then(f), Promise.resolve(arg)); +``` + +
+Examples + +```js +const sum = pipeAsyncFunctions( + x => x + 1, + x => new Promise(resolve => setTimeout(() => resolve(x + 2), 1000)), + x => x + 3, + async x => (await x) + 4 +); +(async () => { + console.log(await sum(5)); // 15 (after one second) +})(); +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### pipeFunctions Performs left-to-right function composition. -Use `Array.reduce()` with the spread operator (`...`) to perform left-to-right function composition. +Use `Array.prototype.reduce()` with the spread operator (`...`) to perform left-to-right function composition. The first (leftmost) function can accept one or more arguments; the remaining functions must be unary. ```js @@ -452,7 +713,6 @@ multiplyAndAdd5(5, 2); // 15
[⬆ Back to top](#table-of-contents) - ### promisify Converts an asynchronous function to return a promise. @@ -481,6 +741,32 @@ delay(2000).then(() => console.log('Hi!')); // // Promise resolves after 2s
[⬆ Back to top](#table-of-contents) +### rearg + +Creates a function that invokes the provided function with its arguments arranged according to the specified indexes. + +Use `Array.prototype.map()` to reorder arguments based on `indexes` in combination with the spread operator (`...`) to pass the transformed arguments to `fn`. + +```js +const rearg = (fn, indexes) => (...args) => fn(...indexes.map(i => args[i])); +``` + +
+Examples + +```js +var rearged = rearg( + function(a, b, c) { + return [a, b, c]; + }, + [2, 0, 1] +); +rearged('b', 'c', 'a'); // ['a', 'b', 'c'] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### spreadOver @@ -490,8 +776,8 @@ Use closures and the spread operator (`...`) to map the array of arguments to th ```js const spreadOver = fn => argsArr => fn(...argsArr); -``` - +``` +
Examples @@ -504,15 +790,175 @@ arrayMax([1, 2, 3]); // 3
[⬆ Back to top](#table-of-contents) +### unary + +Creates a function that accepts up to one argument, ignoring any additional arguments. + +Call the provided function, `fn`, with just the first argument given. + +```js +const unary = fn => val => fn(val); +``` + +
+Examples + +```js +['6', '8', '10'].map(unary(parseInt)); // [6, 8, 10] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + + --- - ## 📚 Array + +## 📚 Array + +### all + +Returns `true` if the provided predicate function returns `true` for all elements in a collection, `false` otherwise. + +Use `Array.prototype.every()` to test if all elements in the collection return `true` based on `fn`. +Omit the second argument, `fn`, to use `Boolean` as a default. + +```js +const all = (arr, fn = Boolean) => arr.every(fn); +``` + +
+Examples + +```js +all([4, 2, 3], x => x > 1); // true +all([1, 2, 3]); // true +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### allEqual + +Check if all elements in an array are equal. + +Use `Array.prototype.every()` to check if all the elements of the array are the same as the first one. + +```js +const allEqual = arr => arr.every(val => val === arr[0]); +``` + +
+Examples + +```js +allEqual([1, 2, 3, 4, 5, 6]); // false +allEqual([1, 1, 1, 1]); // true +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### any + +Returns `true` if the provided predicate function returns `true` for at least one element in a collection, `false` otherwise. + +Use `Array.prototype.some()` to test if any elements in the collection return `true` based on `fn`. +Omit the second argument, `fn`, to use `Boolean` as a default. + +```js +const any = (arr, fn = Boolean) => arr.some(fn); +``` + +
+Examples + +```js +any([0, 1, 2, 0], x => x >= 2); // true +any([0, 0, 1, 0]); // true +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### arrayToCSV + +Converts a 2D array to a comma-separated values (CSV) string. + +Use `Array.prototype.map()` and `Array.prototype.join(delimiter)` to combine individual 1D arrays (rows) into strings. +Use `Array.prototype.join('\n')` to combine all rows into a CSV string, separating each row with a newline. +Omit the second argument, `delimiter`, to use a default delimiter of `,`. + +```js +const arrayToCSV = (arr, delimiter = ',') => + arr.map(v => v.map(x => `"${x}"`).join(delimiter)).join('\n'); +``` + +
+Examples + +```js +arrayToCSV([['a', 'b'], ['c', 'd']]); // '"a","b"\n"c","d"' +arrayToCSV([['a', 'b'], ['c', 'd']], ';'); // '"a";"b"\n"c";"d"' +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### bifurcate + +Splits 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. + +Use `Array.prototype.reduce()` and `Array.prototype.push()` to add elements to groups, based on `filter`. + +```js +const bifurcate = (arr, filter) => + arr.reduce((acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc), [[], []]); +``` + +
+Examples + +```js +bifurcate(['beep', 'boop', 'foo', 'bar'], [true, true, false, true]); // [ ['beep', 'boop', 'bar'], ['foo'] ] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### bifurcateBy + +Splits 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. + +Use `Array.prototype.reduce()` and `Array.prototype.push()` to add elements to groups, based on the value returned by `fn` for each element. + +```js +const bifurcateBy = (arr, fn) => + arr.reduce((acc, val, i) => (acc[fn(val, i) ? 0 : 1].push(val), acc), [[], []]); +``` + +
+Examples + +```js +bifurcateBy(['beep', 'boop', 'foo', 'bar'], x => x[0] === 'b'); // [ ['beep', 'boop', 'bar'], ['foo'] ] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### chunk Chunks an array into smaller arrays of a specified size. Use `Array.from()` to create a new array, that fits the number of chunks that will be produced. -Use `Array.slice()` to map each element of the new array to a chunk the length of `size`. +Use `Array.prototype.slice()` to map each element of the new array to a chunk the length of `size`. If the original array can't be split evenly, the final chunk will contain the remaining elements. ```js @@ -533,12 +979,11 @@ chunk([1, 2, 3, 4, 5], 2); // [[1,2],[3,4],[5]]
[⬆ Back to top](#table-of-contents) - ### compact Removes falsey values from an array. -Use `Array.filter()` to filter out falsey values (`false`, `null`, `0`, `""`, `undefined`, and `NaN`). +Use `Array.prototype.filter()` to filter out falsey values (`false`, `null`, `0`, `""`, `undefined`, and `NaN`). ```js const compact = arr => arr.filter(Boolean); @@ -555,15 +1000,41 @@ compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34]); // [ 1, 2, 3, 'a'
[⬆ Back to top](#table-of-contents) +### countBy + +Groups the elements of an array based on the given function and returns the count of elements in each group. + +Use `Array.prototype.map()` to map the values of an array to a function or property name. +Use `Array.prototype.reduce()` to create an object, where the keys are produced from the mapped results. + +```js +const countBy = (arr, fn) => + arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => { + acc[val] = (acc[val] || 0) + 1; + return acc; + }, {}); +``` + +
+Examples + +```js +countBy([6.1, 4.2, 6.3], Math.floor); // {4: 1, 6: 2} +countBy(['one', 'two', 'three'], 'length'); // {3: 2, 5: 1} +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### countOccurrences Counts the occurrences of a value in an array. -Use `Array.reduce()` to increment a counter each time you encounter the specific value inside the array. +Use `Array.prototype.reduce()` to increment a counter each time you encounter the specific value inside the array. ```js -const countOccurrences = (arr, value) => arr.reduce((a, v) => (v === value ? a + 1 : a + 0), 0); +const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0); ```
@@ -577,13 +1048,12 @@ countOccurrences([1, 1, 2, 1, 2, 3], 1); // 3
[⬆ Back to top](#table-of-contents) - ### deepFlatten Deep flattens an array. Use recursion. -Use `Array.concat()` with an empty array (`[]`) and the spread operator (`...`) to flatten an array. +Use `Array.prototype.concat()` with an empty array (`[]`) and the spread operator (`...`) to flatten an array. Recursively flatten each element that is an array. ```js @@ -601,12 +1071,11 @@ deepFlatten([1, [2], [[3], 4], 5]); // [1,2,3,4,5]
[⬆ Back to top](#table-of-contents) - ### difference Returns the difference between two arrays. -Create a `Set` from `b`, then use `Array.filter()` on `a` to only keep values not contained in `b`. +Create a `Set` from `b`, then use `Array.prototype.filter()` on `a` to only keep values not contained in `b`. ```js const difference = (a, b) => { @@ -626,12 +1095,36 @@ difference([1, 2, 3], [1, 2, 4]); // [3]
[⬆ Back to top](#table-of-contents) +### differenceBy + +Returns the difference between two arrays, after applying the provided function to each array element of both. + +Create 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. + +```js +const differenceBy = (a, b, fn) => { + const s = new Set(b.map(v => fn(v))); + return a.filter(x => !s.has(fn(x))); +}; +``` + +
+Examples + +```js +differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [1.2] +differenceBy([{ x: 2 }, { x: 1 }], [{ x: 1 }], v => v.x); // [ { x: 2 } ] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### differenceWith Filters out all values from an array for which the comparator function does not return `true`. -Use `Array.filter()` and `Array.findIndex()` to find the appropriate values. +Use `Array.prototype.filter()` and `Array.prototype.findIndex()` to find the appropriate values. ```js const differenceWith = (arr, val, comp) => arr.filter(a => val.findIndex(b => comp(a, b)) === -1); @@ -648,60 +1141,34 @@ differenceWith([1, 1.2, 1.5, 3, 0], [1.9, 3, 0], (a, b) => Math.round(a) === Mat
[⬆ Back to top](#table-of-contents) +### drop -### distinctValuesOfArray +Returns a new array with `n` elements removed from the left. -Returns all the distinct values of an array. - -Use ES6 `Set` and the `...rest` operator to discard all duplicated values. +Use `Array.prototype.slice()` to slice the remove the specified number of elements from the left. ```js -const distinctValuesOfArray = arr => [...new Set(arr)]; +const drop = (arr, n = 1) => arr.slice(n); ```
Examples ```js -distinctValuesOfArray([1, 2, 2, 3, 4, 4, 5]); // [1,2,3,4,5] +drop([1, 2, 3]); // [2,3] +drop([1, 2, 3], 2); // [3] +drop([1, 2, 3], 42); // [] ```

[⬆ Back to top](#table-of-contents) - -### dropElements - -Removes elements in an array until the passed function returns `true`. Returns the remaining elements in the array. - -Loop through the array, using `Array.slice()` to drop the first element of the array until the returned value from the function is `true`. -Returns the remaining elements. - -```js -const dropElements = (arr, func) => { - while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1); - return arr; -}; -``` - -
-Examples - -```js -dropElements([1, 2, 3, 4], n => n >= 3); // [3,4] -``` - -
- -
[⬆ Back to top](#table-of-contents) - - ### dropRight Returns a new array with `n` elements removed from the right. -Use `Array.slice()` to slice the remove the specified number of elements from the right. +Use `Array.prototype.slice()` to slice the remove the specified number of elements from the right. ```js const dropRight = (arr, n = 1) => arr.slice(0, -n); @@ -720,12 +1187,61 @@ dropRight([1, 2, 3], 42); // []
[⬆ Back to top](#table-of-contents) +### dropRightWhile + +Removes elements from the end of an array until the passed function returns `true`. Returns the remaining elements in the array. + +Loop through the array, using `Array.prototype.slice()` to drop the last element of the array until the returned value from the function is `true`. +Returns the remaining elements. + +```js +const dropRightWhile = (arr, func) => { + while (arr.length > 0 && !func(arr[arr.length - 1])) arr = arr.slice(0, -1); + return arr; +}; +``` + +
+Examples + +```js +dropRightWhile([1, 2, 3, 4], n => n < 3); // [1, 2] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### dropWhile + +Removes elements in an array until the passed function returns `true`. Returns the remaining elements in the array. + +Loop through the array, using `Array.prototype.slice()` to drop the first element of the array until the returned value from the function is `true`. +Returns the remaining elements. + +```js +const dropWhile = (arr, func) => { + while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1); + return arr; +}; +``` + +
+Examples + +```js +dropWhile([1, 2, 3, 4], n => n >= 3); // [3,4] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### everyNth Returns every nth element in an array. -Use `Array.filter()` to create a new array that contains every nth element of a given array. +Use `Array.prototype.filter()` to create a new array that contains every nth element of a given array. ```js const everyNth = (arr, nth) => arr.filter((e, i) => i % nth === nth - 1); @@ -742,12 +1258,11 @@ everyNth([1, 2, 3, 4, 5, 6], 2); // [ 2, 4, 6 ]
[⬆ Back to top](#table-of-contents) - ### filterNonUnique Filters out the non-unique values in an array. -Use `Array.filter()` for an array containing only the unique values. +Use `Array.prototype.filter()` for an array containing only the unique values. ```js const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i)); @@ -757,74 +1272,153 @@ const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexO Examples ```js -filterNonUnique([1, 2, 2, 3, 4, 4, 5]); // [1,3,5] +filterNonUnique([1, 2, 2, 3, 4, 4, 5]); // [1, 3, 5] ```

[⬆ Back to top](#table-of-contents) +### filterNonUniqueBy -### flatten +Filters out the non-unique values in an array, based on a provided comparator function. -Flattens an array. - -Use a new array and concatenate it with the spread input array causing a shallow denesting of any contained arrays. +Use `Array.prototype.filter()` and `Array.prototype.every()` for an array containing only the unique values, based on the comparator function, `fn`. +The comparator function takes four arguments: the values of the two elements being compared and their indexes. ```js -const flatten = arr => [].concat(...arr); +const filterNonUniqueBy = (arr, fn) => + arr.filter((v, i) => arr.every((x, j) => (i === j) === fn(v, x, i, j))); ```
Examples ```js -flatten([1, [2], 3, 4]); // [1,2,3,4] +filterNonUniqueBy( + [ + { id: 0, value: 'a' }, + { id: 1, value: 'b' }, + { id: 2, value: 'c' }, + { id: 1, value: 'd' }, + { id: 0, value: 'e' } + ], + (a, b) => a.id == b.id +); // [ { id: 2, value: 'c' } ] ```

[⬆ Back to top](#table-of-contents) +### findLast -### flattenDepth +Returns the last element for which the provided function returns a truthy value. + +Use `Array.prototype.filter()` to remove elements for which `fn` returns falsey values, `Array.prototype.pop()` to get the last one. + +```js +const findLast = (arr, fn) => arr.filter(fn).pop(); +``` + +
+Examples + +```js +findLast([1, 2, 3, 4], n => n % 2 === 1); // 3 +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### findLastIndex + +Returns the index of the last element for which the provided function returns a truthy value. + +Use `Array.prototype.map()` to map each element to an array with its index and value. +Use `Array.prototype.filter()` to remove elements for which `fn` returns falsey values, `Array.prototype.pop()` to get the last one. + +```js +const findLastIndex = (arr, fn) => + arr + .map((val, i) => [i, val]) + .filter(([i, val]) => fn(val, i, arr)) + .pop()[0]; +``` + +
+Examples + +```js +findLastIndex([1, 2, 3, 4], n => n % 2 === 1); // 2 (index of the value 3) +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### flatten Flattens an array up to the specified depth. Use recursion, decrementing `depth` by 1 for each level of depth. -Use `Array.reduce()` and `Array.concat()` to merge elements or arrays. +Use `Array.prototype.reduce()` and `Array.prototype.concat()` to merge elements or arrays. Base case, for `depth` equal to `1` stops recursion. -Omit the second element, `depth` to flatten only to a depth of `1` (single flatten). +Omit the second argument, `depth` to flatten only to a depth of `1` (single flatten). ```js -const flattenDepth = (arr, depth = 1) => - depth != 1 - ? arr.reduce((a, v) => a.concat(Array.isArray(v) ? flattenDepth(v, depth - 1) : v), []) - : arr.reduce((a, v) => a.concat(v), []); +const flatten = (arr, depth = 1) => + arr.reduce((a, v) => a.concat(depth > 1 && Array.isArray(v) ? flatten(v, depth - 1) : v), []); ```
Examples ```js -flattenDepth([1, [2], 3, 4]); // [1,2,3,4] +flatten([1, [2], 3, 4]); // [1, 2, 3, 4] +flatten([1, [2, [3, [4, 5], 6], 7], 8], 2); // [1, 2, 3, [4, 5], 6, 7, 8] ```

[⬆ Back to top](#table-of-contents) +### forEachRight + +Executes a provided function once for each array element, starting from the array's last element. + +Use `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. + +```js +const forEachRight = (arr, callback) => + arr + .slice(0) + .reverse() + .forEach(callback); +``` + +
+Examples + +```js +forEachRight([1, 2, 3, 4], val => console.log(val)); // '4', '3', '2', '1' +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### groupBy Groups the elements of an array based on the given function. -Use `Array.map()` to map the values of an array to a function or property name. -Use `Array.reduce()` to create an object, where the keys are produced from the mapped results. +Use `Array.prototype.map()` to map the values of an array to a function or property name. +Use `Array.prototype.reduce()` to create an object, where the keys are produced from the mapped results. ```js -const groupBy = (arr, func) => - arr.map(typeof func === 'function' ? func : val => val[func]).reduce((acc, val, i) => { +const groupBy = (arr, fn) => + arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => { acc[val] = (acc[val] || []).concat(arr[i]); return acc; }, {}); @@ -842,7 +1436,6 @@ groupBy(['one', 'two', 'three'], 'length'); // {3: ['one', 'two'], 5: ['three']}
[⬆ Back to top](#table-of-contents) - ### head Returns the head of a list. @@ -864,6 +1457,29 @@ head([1, 2, 3]); // 1
[⬆ Back to top](#table-of-contents) +### indexOfAll + +Returns all indices of `val` in an array. +If `val` never occurs, returns `[]`. + +Use `Array.prototype.reduce()` to loop over elements and store indices for matching elements. +Return the array of indices. + +```js +const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []); +``` + +
+Examples + +```js +indexOfAll([1, 2, 3, 1, 2, 3], 1); // [0,3] +indexOfAll([1, 2, 3], 4); // [] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### initial @@ -886,18 +1502,15 @@ initial([1, 2, 3]); // [1,2]
[⬆ Back to top](#table-of-contents) - ### initialize2DArray Initializes a 2D array of given width and height and value. -Use `Array.map()` to generate h rows where each is a new array of size w initialize with value. If the value is not provided, default to `null`. +Use `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`. ```js const initialize2DArray = (w, h, val = null) => - Array(h) - .fill() - .map(() => Array(w).fill(val)); + Array.from({ length: h }).map(() => Array.from({ length: w }).fill(val)); ```
@@ -911,18 +1524,17 @@ initialize2DArray(2, 2, 0); // [[0,0], [0,0]]
[⬆ Back to top](#table-of-contents) - ### initializeArrayWithRange -Initializes an array containing the numbers in the specified range where `start` and `end` are inclusive with there common difference `step`. +Initializes an array containing the numbers in the specified range where `start` and `end` are inclusive with their common difference `step`. -Use `Array(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.map()` to fill with the desired values in a range. +Use `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. You can omit `start` to use a default value of `0`. You can omit `step` to use a default value of `1`. ```js const initializeArrayWithRange = (end, start = 0, step = 1) => - Array.from({ length: Math.ceil((end + 1 - start) / step) }).map((v, i) => i * step + start); + Array.from({ length: Math.ceil((end - start + 1) / step) }, (v, i) => i * step + start); ```
@@ -938,35 +1550,87 @@ initializeArrayWithRange(9, 0, 2); // [0,2,4,6,8]
[⬆ Back to top](#table-of-contents) +### initializeArrayWithRangeRight -### initializeArrayWithValues +Initializes an array containing the numbers in the specified range (in reverse) where `start` and `end` are inclusive with their common difference `step`. -Initializes and fills an array with the specified values. - -Use `Array(n)` to create an array of the desired length, `fill(v)` to fill it with the desired values. -You can omit `value` to use a default value of `0`. +Use `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. +You can omit `start` to use a default value of `0`. +You can omit `step` to use a default value of `1`. ```js -const initializeArrayWithValues = (n, value = 0) => Array(n).fill(value); +const initializeArrayWithRangeRight = (end, start = 0, step = 1) => + Array.from({ length: Math.ceil((end + 1 - start) / step) }).map( + (v, i, arr) => (arr.length - i - 1) * step + start + ); ```
Examples ```js -initializeArrayWithValues(5, 2); // [2,2,2,2,2] +initializeArrayWithRangeRight(5); // [5,4,3,2,1,0] +initializeArrayWithRangeRight(7, 3); // [7,6,5,4,3] +initializeArrayWithRangeRight(9, 0, 2); // [8,6,4,2,0] ```

[⬆ Back to top](#table-of-contents) +### initializeArrayWithValues + +Initializes and fills an array with the specified values. + +Use `Array(n)` to create an array of the desired length, `fill(v)` to fill it with the desired values. +You can omit `val` to use a default value of `0`. + +```js +const initializeArrayWithValues = (n, val = 0) => Array(n).fill(val); +``` + +
+Examples + +```js +initializeArrayWithValues(5, 2); // [2, 2, 2, 2, 2] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### initializeNDArray + +Create a n-dimensional array with given value. + +Use recursion. +Use `Array.prototype.map()` to generate rows where each is a new array initialized using `initializeNDArray`. + +```js +const initializeNDArray = (val, ...args) => + args.length === 0 + ? val + : Array.from({ length: args[0] }).map(() => initializeNDArray(val, ...args.slice(1))); +``` + +
+Examples + +```js +initializeNDArray(1, 3); // [1,1,1] +initializeNDArray(5, 2, 2, 2); // [[[5,5],[5,5]],[[5,5],[5,5]]] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### intersection Returns a list of elements that exist in both arrays. -Create a `Set` from `b`, then use `Array.filter()` on `a` to only keep values contained in `b`. +Create a `Set` from `b`, then use `Array.prototype.filter()` on `a` to only keep values contained in `b`. ```js const intersection = (a, b) => { @@ -979,13 +1643,57 @@ const intersection = (a, b) => { Examples ```js -intersection([1, 2, 3], [4, 3, 2]); // [2,3] +intersection([1, 2, 3], [4, 3, 2]); // [2, 3] ```

[⬆ Back to top](#table-of-contents) +### intersectionBy + +Returns a list of elements that exist in both arrays, after applying the provided function to each array element of both. + +Create 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. + +```js +const intersectionBy = (a, b, fn) => { + const s = new Set(b.map(fn)); + return a.filter(x => s.has(fn(x))); +}; +``` + +
+Examples + +```js +intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [2.1] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### intersectionWith + +Returns a list of elements that exist in both arrays, using a provided comparator function. + +Use `Array.prototype.filter()` and `Array.prototype.findIndex()` in combination with the provided comparator to determine intersecting values. + +```js +const intersectionWith = (a, b, comp) => a.filter(x => b.findIndex(y => comp(x, y)) !== -1); +``` + +
+Examples + +```js +intersectionWith([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] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### isSorted @@ -997,10 +1705,12 @@ Return `0` if the `direction` changes or the `direction` if the last element is ```js const isSorted = arr => { - const direction = arr[0] > arr[1] ? -1 : 1; - for (let [i, val] of arr.entries()) - if (i === arr.length - 1) return direction; + let direction = -(arr[0] - arr[1]); + for (let [i, val] of arr.entries()) { + direction = !direction ? -(arr[i - 1] - arr[i]) : direction; + if (i === arr.length - 1) return !direction ? 0 : direction; else if ((val - arr[i + 1]) * direction > 0) return 0; + } }; ``` @@ -1017,12 +1727,12 @@ isSorted([4, 3, 5]); // 0
[⬆ Back to top](#table-of-contents) - ### join -Joins all elements of an array into a string and returns this string. Uses a separator and an end separator. +Joins all elements of an array into a string and returns this string. +Uses a separator and an end separator. -Use `Array.reduce()` to combine elements into a string. +Use `Array.prototype.reduce()` to combine elements into a string. Omit the second argument, `separator`, to use a default separator of `','`. Omit the third argument, `end`, to use the same value as `separator` by default. @@ -1030,9 +1740,11 @@ Omit the third argument, `end`, to use the same value as `separator` by default. const join = (arr, separator = ',', end = separator) => arr.reduce( (acc, val, i) => - i == arr.length - 2 + i === arr.length - 2 ? acc + val + end - : i == arr.length - 1 ? acc + val : acc + val + separator, + : i === arr.length - 1 + ? acc + val + : acc + val + separator, '' ); ``` @@ -1050,6 +1762,39 @@ join(['pen', 'pineapple', 'apple', 'pen']); // "pen,pineapple,apple,pen"
[⬆ Back to top](#table-of-contents) +### JSONtoCSV ![advanced](/advanced.svg) + +Converts an array of objects to a comma-separated values (CSV) string that contains only the `columns` specified. + +Use `Array.prototype.join(demiliter)` to combine all the names in `columns` to create the first row. +Use `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`. +Use `Array.prototype.join('\n')` to combine all rows into a string. +Omit the third argument, `delimiter`, to use a default delimiter of `,`. + +```js +const JSONtoCSV = (arr, columns, delimiter = ',') => + [ + columns.join(delimiter), + ...arr.map(obj => + columns.reduce( + (acc, key) => `${acc}${!acc.length ? '' : delimiter}"${!obj[key] ? '' : obj[key]}"`, + '' + ) + ) + ].join('\n'); +``` + +
+Examples + +```js +JSONtoCSV([{ 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"' +JSONtoCSV([{ 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"' +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### last @@ -1072,8 +1817,35 @@ last([1, 2, 3]); // 3
[⬆ Back to top](#table-of-contents) +### longestItem -### mapObject +Takes any number of iterable objects or objects with a `length` property and returns the longest one. +If multiple objects have the same length, the first one will be returned. +Returns `undefined` if no arguments are provided. + +Use `Array.prototype.reduce()`, comparing the `length` of objects to find the longest one. + +```js +const longestItem = (val, ...vals) => + [val, ...vals].reduce((a, x) => (x.length > a.length ? x : a)); +``` + +
+Examples + +```js +longestItem('this', 'is', 'a', 'testcase'); // 'testcase' +longestItem(...['a', 'ab', 'abc']); // 'abc' +longestItem(...['a', 'ab', 'abc'], 'abcd'); // 'abcd' +longestItem([1, 2, 3], [1, 2], [1, 2, 3, 4, 5]); // [1, 2, 3, 4, 5] +longestItem([1, 2, 3], 'foobar'); // 'foobar' +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### mapObject ![advanced](/advanced.svg) Maps 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. @@ -1098,13 +1870,13 @@ squareIt([1, 2, 3]); // { 1: 1, 2: 4, 3: 9 }
[⬆ Back to top](#table-of-contents) - ### maxN -Returns the `n` maximum elements from the provided array. If `n` is greater than or equal to the provided array's length, then return the original array(sorted in descending order). +Returns the `n` maximum elements from the provided array. +If `n` is greater than or equal to the provided array's length, then return the original array (sorted in descending order). -Use `Array.sort()` combined with the spread operator (`...`) to create a shallow clone of the array and sort it in descending order. -Use `Array.slice()` to get the specified number of elements. +Use `Array.prototype.sort()` combined with the spread operator (`...`) to create a shallow clone of the array and sort it in descending order. +Use `Array.prototype.slice()` to get the specified number of elements. Omit the second argument, `n`, to get a one-element array. ```js @@ -1123,18 +1895,19 @@ maxN([1, 2, 3], 2); // [3,2]
[⬆ Back to top](#table-of-contents) - ### minN -Returns the `n` minimum elements from the provided array. If `n` is greater than or equal to the provided array's length, then return the original array(sorted in ascending order). +Returns the `n` minimum elements from the provided array. +If `n` is greater than or equal to the provided array's length, then return the original array (sorted in ascending order). -Use `Array.sort()` combined with the spread operator (`...`) to create a shallow clone of the array and sort it in ascending order. -Use `Array.slice()` to get the specified number of elements. +Use `Array.prototype.sort()` combined with the spread operator (`...`) to create a shallow clone of the array and sort it in ascending order. +Use `Array.prototype.slice()` to get the specified number of elements. Omit the second argument, `n`, to get a one-element array. ```js const minN = (arr, n = 1) => [...arr].sort((a, b) => a - b).slice(0, n); ``` +
Examples @@ -1147,17 +1920,39 @@ minN([1, 2, 3], 2); // [1,2]
[⬆ Back to top](#table-of-contents) +### none + +Returns `true` if the provided predicate function returns `false` for all elements in a collection, `false` otherwise. + +Use `Array.prototype.some()` to test if any elements in the collection return `true` based on `fn`. +Omit the second argument, `fn`, to use `Boolean` as a default. + +```js +const none = (arr, fn = Boolean) => !arr.some(fn); +``` + +
+Examples + +```js +none([0, 1, 3, 0], x => x == 2); // true +none([0, 0, 0]); // true +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### nthElement Returns the nth element of an array. -Use `Array.slice()` to get an array containing the nth element at the first place. -If the index is out of bounds, return `[]`. +Use `Array.prototype.slice()` to get an array containing the nth element at the first place. +If the index is out of bounds, return `undefined`. Omit the second argument, `n`, to get the first element of the array. ```js -const nthElement = (arr, n = 0) => (n > 0 ? arr.slice(n, n + 1) : arr.slice(n))[0]; +const nthElement = (arr, n = 0) => (n === -1 ? arr.slice(n) : arr.slice(n, n + 1))[0]; ```
@@ -1172,36 +1967,101 @@ nthElement(['a', 'b', 'b'], -3); // 'a'
[⬆ Back to top](#table-of-contents) +### offset -### pick +Moves the specified amount of elements to the end of the array. -Picks the key-value pairs corresponding to the given keys from an object. - -Use `Array.reduce()` to convert the filtered/picked keys back to an object with the corresponding key-value pair if the key exists in the obj. +Use `Array.prototype.slice()` twice to get the elements after the specified index and the elements before that. +Use the spread operator(`...`) to combine the two into one array. +If `offset` is negative, the elements will be moved from end to start. ```js -const pick = (obj, arr) => - arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[curr]), acc), {}); +const offset = (arr, offset) => [...arr.slice(offset), ...arr.slice(0, offset)]; ```
Examples ```js -pick({ a: 1, b: '2', c: 3 }, ['a', 'c']); // { 'a': 1, 'c': 3 } +offset([1, 2, 3, 4, 5], 2); // [3, 4, 5, 1, 2] +offset([1, 2, 3, 4, 5], -2); // [4, 5, 1, 2, 3] ```

[⬆ Back to top](#table-of-contents) +### partition + +Groups the elements into two arrays, depending on the provided function's truthiness for each element. + +Use `Array.prototype.reduce()` to create an array of two arrays. +Use `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. + +```js +const partition = (arr, fn) => + arr.reduce( + (acc, val, i, arr) => { + acc[fn(val, i, arr) ? 0 : 1].push(val); + return acc; + }, + [[], []] + ); +``` + +
+Examples + +```js +const users = [{ user: 'barney', age: 36, active: false }, { user: 'fred', age: 40, active: true }]; +partition(users, o => o.active); // [[{ 'user': 'fred', 'age': 40, 'active': true }],[{ 'user': 'barney', 'age': 36, 'active': false }]] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### permutations ![advanced](/advanced.svg) + +⚠️ **WARNING**: This function's execution time increases exponentially with each array element. Anything more than 8 to 10 entries will cause your browser to hang as it tries to solve all the different combinations. + +Generates all permutations of an array's elements (contains duplicates). + +Use recursion. +For each element in the given array, create all the partial permutations for the rest of its elements. +Use `Array.prototype.map()` to combine the element with each partial permutation, then `Array.prototype.reduce()` to combine all permutations in one array. +Base cases are for array `length` equal to `2` or `1`. + +```js +const permutations = arr => { + if (arr.length <= 2) return arr.length === 2 ? [arr, [arr[1], arr[0]]] : arr; + return arr.reduce( + (acc, item, i) => + acc.concat( + permutations([...arr.slice(0, i), ...arr.slice(i + 1)]).map(val => [item, ...val]) + ), + [] + ); +}; +``` + +
+Examples + +```js +permutations([1, 33, 5]); // [ [ 1, 33, 5 ], [ 1, 5, 33 ], [ 33, 1, 5 ], [ 33, 5, 1 ], [ 5, 1, 33 ], [ 5, 33, 1 ] ] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### pull Mutates the original array to filter out the values specified. -Use `Array.filter()` and `Array.includes()` to pull out the values that are not needed. -Use `Array.length = 0` to mutate the passed in an array by resetting it's length to zero and `Array.push()` to re-populate it with only the pulled values. +Use `Array.prototype.filter()` and `Array.prototype.includes()` to pull out the values that are not needed. +Use `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. _(For a snippet that does not mutate the original array see [`without`](#without))_ @@ -1226,14 +2086,13 @@ pull(myArray, 'a', 'c'); // myArray = [ 'b', 'b' ]
[⬆ Back to top](#table-of-contents) - -### pullAtIndex +### pullAtIndex ![advanced](/advanced.svg) Mutates the original array to filter out the values at the specified indexes. -Use `Array.filter()` and `Array.includes()` to pull out the values that are not needed. -Use `Array.length = 0` to mutate the passed in an array by resetting it's length to zero and `Array.push()` to re-populate it with only the pulled values. -Use `Array.push()` to keep track of pulled values +Use `Array.prototype.filter()` and `Array.prototype.includes()` to pull out the values that are not needed. +Use `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. +Use `Array.prototype.push()` to keep track of pulled values ```js const pullAtIndex = (arr, pullArr) => { @@ -1259,14 +2118,13 @@ let pulled = pullAtIndex(myArray, [1, 3]); // myArray = [ 'a', 'c' ] , pulled =
[⬆ Back to top](#table-of-contents) - -### pullAtValue +### pullAtValue ![advanced](/advanced.svg) Mutates the original array to filter out the values specified. Returns the removed elements. -Use `Array.filter()` and `Array.includes()` to pull out the values that are not needed. -Use `Array.length = 0` to mutate the passed in an array by resetting it's length to zero and `Array.push()` to re-populate it with only the pulled values. -Use `Array.push()` to keep track of pulled values +Use `Array.prototype.filter()` and `Array.prototype.includes()` to pull out the values that are not needed. +Use `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. +Use `Array.prototype.push()` to keep track of pulled values ```js const pullAtValue = (arr, pullArr) => { @@ -1291,45 +2149,45 @@ let pulled = pullAtValue(myArray, ['b', 'd']); // myArray = [ 'a', 'c' ] , pulle
[⬆ Back to top](#table-of-contents) +### pullBy ![advanced](/advanced.svg) -### quickSort +Mutates the original array to filter out the values specified, based on a given iterator function. -QuickSort an Array (ascending sort by default). - -Use recursion. -Use `Array.filter` and spread operator (`...`) to create an array that all elements with values less than the pivot come before the pivot, and all elements with values greater than the pivot come after it. -If the parameter `desc` is truthy, return array sorts in descending order. +Check if the last argument provided in a function. +Use `Array.prototype.map()` to apply the iterator function `fn` to all array elements. +Use `Array.prototype.filter()` and `Array.prototype.includes()` to pull out the values that are not needed. +Use `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. ```js -const quickSort = ([n, ...nums], desc) => - isNaN(n) - ? [] - : [ - ...quickSort(nums.filter(v => (desc ? v > n : v <= n)), desc), - n, - ...quickSort(nums.filter(v => (!desc ? v > n : v <= n)), desc) - ]; +const pullBy = (arr, ...args) => { + const length = args.length; + let fn = length > 1 ? args[length - 1] : undefined; + fn = typeof fn == 'function' ? (args.pop(), fn) : undefined; + let argState = (Array.isArray(args[0]) ? args[0] : args).map(val => fn(val)); + let pulled = arr.filter((v, i) => !argState.includes(fn(v))); + arr.length = 0; + pulled.forEach(v => arr.push(v)); +}; ```
Examples ```js -quickSort([4, 1, 3, 2]); // [1,2,3,4] -quickSort([4, 1, 3, 2], true); // [4,3,2,1] +var myArray = [{ x: 1 }, { x: 2 }, { x: 3 }, { x: 1 }]; +pullBy(myArray, [{ x: 1 }, { x: 3 }], o => o.x); // myArray = [{ x: 2 }] ```

[⬆ Back to top](#table-of-contents) - ### reducedFilter Filter an array of objects based on a condition while also filtering out unspecified keys. -Use `Array.filter()` to filter the array based on the predicate `fn` so that it returns the objects for which the condition returned a truthy value. -On the filtered array, use `Array.map()` to return the new object using `Array.reduce()` to filter out the keys which were not supplied as the `keys` argument. +Use `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. +On 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. ```js const reducedFilter = (data, keys, fn) => @@ -1365,12 +2223,81 @@ reducedFilter(data, ['id', 'name'], item => item.age > 24); // [{ id: 2, name: '
[⬆ Back to top](#table-of-contents) +### reduceSuccessive + +Applies a function against an accumulator and each element in the array (from left to right), returning an array of successively reduced values. + +Use `Array.prototype.reduce()` to apply the given function to the given array, storing each new result. + +```js +const reduceSuccessive = (arr, fn, acc) => + arr.reduce((res, val, i, arr) => (res.push(fn(res.slice(-1)[0], val, i, arr)), res), [acc]); +``` + +
+Examples + +```js +reduceSuccessive([1, 2, 3, 4, 5, 6], (acc, val) => acc + val, 0); // [0, 1, 3, 6, 10, 15, 21] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### reduceWhich + +Returns the minimum/maximum value of an array, after applying the provided function to set comparing rule. + +Use `Array.prototype.reduce()` in combination with the `comparator` function to get the appropriate element in the array. +You can omit the second parameter, `comparator`, to use the default one that returns the minimum element in the array. + +```js +const reduceWhich = (arr, comparator = (a, b) => a - b) => + arr.reduce((a, b) => (comparator(a, b) >= 0 ? b : a)); +``` + +
+Examples + +```js +reduceWhich([1, 3, 2]); // 1 +reduceWhich([1, 3, 2], (a, b) => b - a); // 3 +reduceWhich( + [{ name: 'Tom', age: 12 }, { name: 'Jack', age: 18 }, { name: 'Lucy', age: 9 }], + (a, b) => a.age - b.age +); // {name: "Lucy", age: 9} +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### reject + +Takes a predicate and array, like `Array.prototype.filter()`, but only keeps `x` if `pred(x) === false`. + +```js +const reject = (pred, array) => array.filter((...args) => !pred(...args)); +``` + +
+Examples + +```js +reject(x => x % 2 === 0, [1, 2, 3, 4, 5]); // [1, 3, 5] +reject(word => word.length > 4, ['Apple', 'Pear', 'Kiwi', 'Banana']); // ['Pear', 'Kiwi'] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### remove Removes elements from an array for which the given function returns `false`. -Use `Array.filter()` to find array elements that return truthy values and `Array.reduce()` to remove elements using `Array.splice()`. +Use `Array.prototype.filter()` to find array elements that return truthy values and `Array.prototype.reduce()` to remove elements using `Array.prototype.splice()`. The `func` is invoked with three arguments (`value, index, array`). ```js @@ -1387,14 +2314,13 @@ const remove = (arr, func) => Examples ```js -remove([1, 2, 3, 4], n => n % 2 == 0); // [2, 4] +remove([1, 2, 3, 4], n => n % 2 === 0); // [2, 4] ```

[⬆ Back to top](#table-of-contents) - ### sample Returns a random element from an array. @@ -1417,13 +2343,12 @@ sample([3, 7, 9, 11]); // 9
[⬆ Back to top](#table-of-contents) - ### sampleSize Gets `n` random elements at unique keys from `array` up to the size of `array`. -Shuffle the array using the [Fisher-Yates algorithm](https://github.com/chalarangelo/30-seconds-of-code#shuffle). -Use `Array.slice()` to get the first `n` elements. +Shuffle the array using the [Fisher-Yates algorithm](https://github.com/30-seconds/30-seconds-of-code#shuffle). +Use `Array.prototype.slice()` to get the first `n` elements. Omit the second argument, `n` to get only one element at random from the array. ```js @@ -1449,12 +2374,43 @@ sampleSize([1, 2, 3], 4); // [2,3,1]
[⬆ Back to top](#table-of-contents) +### shank + +Has the same functionality as [`Array.prototype.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. + +Use `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. +Omit the second argument, `index`, to start at `0`. +Omit the third argument, `delCount`, to remove `0` elements. +Omit the fourth argument, `elements`, in order to not add any new elements. + +```js +const shank = (arr, index = 0, delCount = 0, ...elements) => + arr + .slice(0, index) + .concat(elements) + .concat(arr.slice(index + delCount)); +``` + +
+Examples + +```js +const names = ['alpha', 'bravo', 'charlie']; +const namesAndDelta = shank(names, 1, 0, 'delta'); // [ 'alpha', 'delta', 'bravo', 'charlie' ] +const namesNoBravo = shank(names, 1, 1); // [ 'alpha', 'charlie' ] +console.log(names); // ['alpha', 'bravo', 'charlie'] +``` + + +
+ +
[⬆ Back to top](#table-of-contents) ### shuffle Randomizes the order of the values of an array, returning a new array. -Uses the Fisher-Yates algorithm to reorder the elements of the array, based on the [Lodash implementation](https://github.com/lodash/lodash/blob/b2ea6b1cd251796dcb5f9700c4911a7b6223920b/shuffle.js), but as a pure function. +Uses the [Fisher-Yates algorithm](https://github.com/30-seconds/30-seconds-of-code#shuffle) to reorder the elements of the array. ```js const shuffle = ([...arr]) => { @@ -1472,19 +2428,18 @@ const shuffle = ([...arr]) => { ```js const foo = [1, 2, 3]; -shuffle(foo); // [2,3,1], foo = [1,2,3] +shuffle(foo); // [2, 3, 1], foo = [1, 2, 3] ```

[⬆ Back to top](#table-of-contents) - ### similarity Returns an array of elements that appear in both arrays. -Use `filter()` to remove values that are not part of `values`, determined using `includes()`. +Use `Array.prototype.filter()` to remove values that are not part of `values`, determined using `Array.prototype.includes()`. ```js const similarity = (arr, values) => arr.filter(v => values.includes(v)); @@ -1494,20 +2449,19 @@ const similarity = (arr, values) => arr.filter(v => values.includes(v)); Examples ```js -similarity([1, 2, 3], [1, 2, 4]); // [1,2] +similarity([1, 2, 3], [1, 2, 4]); // [1, 2] ```

[⬆ Back to top](#table-of-contents) - ### sortedIndex Returns the lowest index at which value should be inserted into array in order to maintain its sort order. Check if the array is sorted in descending order (loosely). -Use `Array.findIndex()` to find the appropriate index where the element should be inserted. +Use `Array.prototype.findIndex()` to find the appropriate index where the element should be inserted. ```js const sortedIndex = (arr, n) => { @@ -1529,12 +2483,124 @@ sortedIndex([30, 50], 40); // 1
[⬆ Back to top](#table-of-contents) +### sortedIndexBy + +Returns the lowest index at which value should be inserted into array in order to maintain its sort order, based on a provided iterator function. + +Check if the array is sorted in descending order (loosely). +Use `Array.prototype.findIndex()` to find the appropriate index where the element should be inserted, based on the iterator function `fn`. + +```js +const sortedIndexBy = (arr, n, fn) => { + const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]); + const val = fn(n); + const index = arr.findIndex(el => (isDescending ? val >= fn(el) : val <= fn(el))); + return index === -1 ? arr.length : index; +}; +``` + +
+Examples + +```js +sortedIndexBy([{ x: 4 }, { x: 5 }], { x: 4 }, o => o.x); // 0 +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### sortedLastIndex + +Returns the highest index at which value should be inserted into array in order to maintain its sort order. + +Check if the array is sorted in descending order (loosely). +Use `Array.prototype.reverse()` and `Array.prototype.findIndex()` to find the appropriate last index where the element should be inserted. + +```js +const sortedLastIndex = (arr, n) => { + const isDescending = arr[0] > arr[arr.length - 1]; + const index = arr.reverse().findIndex(el => (isDescending ? n <= el : n >= el)); + return index === -1 ? 0 : arr.length - index; +}; +``` + +
+Examples + +```js +sortedLastIndex([10, 20, 30, 30, 40], 30); // 4 +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### sortedLastIndexBy + +Returns the highest index at which value should be inserted into array in order to maintain its sort order, based on a provided iterator function. + +Check if the array is sorted in descending order (loosely). +Use `Array.prototype.map()` to apply the iterator function to all elements of the array. +Use `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. + +```js +const sortedLastIndexBy = (arr, n, fn) => { + const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]); + const val = fn(n); + const index = arr + .map(fn) + .reverse() + .findIndex(el => (isDescending ? val <= el : val >= el)); + return index === -1 ? 0 : arr.length - index; +}; +``` + +
+Examples + +```js +sortedLastIndexBy([{ x: 4 }, { x: 5 }], { x: 4 }, o => o.x); // 1 +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### stableSort ![advanced](/advanced.svg) + +Performs stable sorting of an array, preserving the initial indexes of items when their values are the same. +Does not mutate the original array, but returns a new array instead. + +Use `Array.prototype.map()` to pair each element of the input array with its corresponding index. +Use `Array.prototype.sort()` and a `compare` function to sort the list, preserving their initial order if the items compared are equal. +Use `Array.prototype.map()` to convert back to the initial array items. + +```js +const stableSort = (arr, compare) => + arr + .map((item, index) => ({ item, index })) + .sort((a, b) => compare(a.item, b.item) || a.index - b.index) + .map(({ item }) => item); +``` + +
+Examples + +```js +const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; +const stable = stableSort(arr, () => 0); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### symmetricDifference -Returns the symmetric difference between two arrays. +Returns the symmetric difference between two arrays, without filtering out duplicate values. -Create a `Set` from each array, then use `Array.filter()` on each of them to only keep values not contained in the other. +Create a `Set` from each array, then use `Array.prototype.filter()` on each of them to only keep values not contained in the other. ```js const symmetricDifference = (a, b) => { @@ -1548,19 +2614,72 @@ const symmetricDifference = (a, b) => { Examples ```js -symmetricDifference([1, 2, 3], [1, 2, 4]); // [3,4] +symmetricDifference([1, 2, 3], [1, 2, 4]); // [3, 4] +symmetricDifference([1, 2, 2], [1, 3, 1]); // [2, 2, 3] ```

[⬆ Back to top](#table-of-contents) +### symmetricDifferenceBy + +Returns the symmetric difference between two arrays, after applying the provided function to each array element of both. + +Create 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. + +```js +const symmetricDifferenceBy = (a, b, fn) => { + const sA = new Set(a.map(v => fn(v))), + sB = new Set(b.map(v => fn(v))); + return [...a.filter(x => !sB.has(fn(x))), ...b.filter(x => !sA.has(fn(x)))]; +}; +``` + +
+Examples + +```js +symmetricDifferenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); // [ 1.2, 3.4 ] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### symmetricDifferenceWith + +Returns the symmetric difference between two arrays, using a provided function as a comparator. + +Use `Array.prototype.filter()` and `Array.prototype.findIndex()` to find the appropriate values. + +```js +const symmetricDifferenceWith = (arr, val, comp) => [ + ...arr.filter(a => val.findIndex(b => comp(a, b)) === -1), + ...val.filter(a => arr.findIndex(b => comp(a, b)) === -1) +]; +``` + +
+Examples + +```js +symmetricDifferenceWith( + [1, 1.2, 1.5, 3, 0], + [1.9, 3, 0, 3.9], + (a, b) => Math.round(a) === Math.round(b) +); // [1, 1.2, 3.9] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### tail Returns all elements in an array except for the first one. -Return `arr.slice(1)` if the array's `length` is more than `1`, otherwise, return the whole array. +Return `Array.prototype.slice(1)` if the array's `length` is more than `1`, otherwise, return the whole array. ```js const tail = arr => (arr.length > 1 ? arr.slice(1) : arr); @@ -1578,12 +2697,11 @@ tail([1]); // [1]
[⬆ Back to top](#table-of-contents) - ### take Returns an array with n elements removed from the beginning. -Use `Array.slice()` to create a slice of the array with `n` elements taken from the beginning. +Use `Array.prototype.slice()` to create a slice of the array with `n` elements taken from the beginning. ```js const take = (arr, n = 1) => arr.slice(0, n); @@ -1601,12 +2719,11 @@ take([1, 2, 3], 0); // []
[⬆ Back to top](#table-of-contents) - ### takeRight Returns an array with n elements removed from the end. -Use `Array.slice()` to create a slice of the array with `n` elements taken from the end. +Use `Array.prototype.slice()` to create a slice of the array with `n` elements taken from the end. ```js const takeRight = (arr, n = 1) => arr.slice(arr.length - n, arr.length); @@ -1624,6 +2741,90 @@ takeRight([1, 2, 3]); // [3]
[⬆ Back to top](#table-of-contents) +### takeRightWhile + +Removes elements from the end of an array until the passed function returns `true`. Returns the removed elements. + +Loop through the array, using a `Array.prototype.reduceRight()` and accumulating elements while the function returns falsy value. + +```js +const takeRightWhile = (arr, func) => + arr.reduceRight((acc, el) => (func(el) ? acc : [el, ...acc]), []); +``` + +
+Examples + +```js +takeRightWhile([1, 2, 3, 4], n => n < 3); // [3, 4] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### takeWhile + +Removes elements in an array until the passed function returns `true`. Returns the removed elements. + +Loop through the array, using a `for...of` loop over `Array.prototype.entries()` until the returned value from the function is `true`. +Return the removed elements, using `Array.prototype.slice()`. + +```js +const takeWhile = (arr, func) => { + for (const [i, val] of arr.entries()) if (func(val)) return arr.slice(0, i); + return arr; +}; +``` + +
+Examples + +```js +takeWhile([1, 2, 3, 4], n => n >= 3); // [1, 2] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### toHash + +Reduces a given Array-like into a value hash (keyed data store). + +Given an Iterable or Array-like structure, call `Array.prototype.prototype.reduce.call()` on the provided object to step over it and return an Object, keyed by the reference value. + +```js +const toHash = (object, key) => + Array.prototype.reduce.call( + object, + (acc, data, index) => ((acc[!key ? index : data[key]] = data), acc), + {} + ); +``` + +
+Examples + +```js +toHash([4, 3, 2, 1]); // { 0: 4, 1: 3, 2: 2, 1: 1 } +toHash([{ a: 'label' }], 'a'); // { label: { a: 'label' } } +// A more in depth example: +let users = [{ id: 1, first: 'Jon' }, { id: 2, first: 'Joe' }, { id: 3, first: 'Moe' }]; +let managers = [{ manager: 1, employees: [2, 3] }]; +// We use function here because we want a bindable reference, but a closure referencing the hash would work, too. +managers.forEach( + manager => + (manager.employees = manager.employees.map(function(id) { + return this[id]; + }, toHash(users, 'id'))) +); +managers; // [ { manager:1, employees: [ { id: 2, first: "Joe" }, { id: 3, first: "Moe" } ] } ] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### union @@ -1646,12 +2847,234 @@ union([1, 2, 3], [4, 3, 2]); // [1,2,3,4]
[⬆ Back to top](#table-of-contents) +### unionBy + +Returns every element that exists in any of the two arrays once, after applying the provided function to each array element of both. + +Create a `Set` by applying all `fn` to all values of `a`. +Create a `Set` from `a` and all elements in `b` whose value, after applying `fn` does not match a value in the previously created set. +Return the last set converted to an array. + +```js +const unionBy = (a, b, fn) => { + const s = new Set(a.map(v => fn(v))); + return Array.from(new Set([...a, ...b.filter(x => !s.has(fn(x)))])); +}; +``` + +
+Examples + +```js +unionBy([2.1], [1.2, 2.3], Math.floor); // [2.1, 1.2] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### unionWith + +Returns every element that exists in any of the two arrays once, using a provided comparator function. + +Create a `Set` with all values of `a` and values in `b` for which the comparator finds no matches in `a`, using `Array.prototype.findIndex()`. + +```js +const unionWith = (a, b, comp) => + Array.from(new Set([...a, ...b.filter(x => a.findIndex(y => comp(x, y)) === -1)])); +``` + +
+Examples + +```js +unionWith([1, 1.2, 1.5, 3, 0], [1.9, 3, 0, 3.9], (a, b) => Math.round(a) === Math.round(b)); // [1, 1.2, 1.5, 3, 0, 3.9] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### uniqueElements + +Returns all unique values of an array. + +Use ES6 `Set` and the `...rest` operator to discard all duplicated values. + +```js +const uniqueElements = arr => [...new Set(arr)]; +``` + +
+Examples + +```js +uniqueElements([1, 2, 2, 3, 4, 4, 5]); // [1, 2, 3, 4, 5] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### uniqueElementsBy + +Returns all unique values of an array, based on a provided comparator function. + +Use `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`. +The comparator function takes two arguments: the values of the two elements being compared. + +```js +const uniqueElementsBy = (arr, fn) => + arr.reduce((acc, v) => { + if (!acc.some(x => fn(v, x))) acc.push(v); + return acc; + }, []); +``` + +
+Examples + +```js +uniqueElementsBy( + [ + { id: 0, value: 'a' }, + { id: 1, value: 'b' }, + { id: 2, value: 'c' }, + { id: 1, value: 'd' }, + { id: 0, value: 'e' } + ], + (a, b) => a.id == b.id +); // [ { id: 0, value: 'a' }, { id: 1, value: 'b' }, { id: 2, value: 'c' } ] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### uniqueElementsByRight + +Returns all unique values of an array, based on a provided comparator function. + +Use `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`. +The comparator function takes two arguments: the values of the two elements being compared. + +```js +const uniqueElementsByRight = (arr, fn) => + arr.reduceRight((acc, v) => { + if (!acc.some(x => fn(v, x))) acc.push(v); + return acc; + }, []); +``` + +
+Examples + +```js +uniqueElementsByRight( + [ + { id: 0, value: 'a' }, + { id: 1, value: 'b' }, + { id: 2, value: 'c' }, + { id: 1, value: 'd' }, + { id: 0, value: 'e' } + ], + (a, b) => a.id == b.id +); // [ { id: 0, value: 'e' }, { id: 1, value: 'd' }, { id: 2, value: 'c' } ] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### uniqueSymmetricDifference + +Returns the unique symmetric difference between two arrays, not containing duplicate values from either array. + +Use `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. + +```js +const uniqueSymmetricDifference = (a, b) => [ + ...new Set([...a.filter(v => !b.includes(v)), ...b.filter(v => !a.includes(v))]) +]; +``` + +
+Examples + +```js +uniqueSymmetricDifference([1, 2, 3], [1, 2, 4]); // [3, 4] +uniqueSymmetricDifference([1, 2, 2], [1, 3, 1]); // [2, 3] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### unzip + +Creates an array of arrays, ungrouping the elements in an array produced by [zip](#zip). + +Use `Math.max.apply()` to get the longest subarray in the array, `Array.prototype.map()` to make each element an array. +Use `Array.prototype.reduce()` and `Array.prototype.forEach()` to map grouped values to individual arrays. + +```js +const unzip = arr => + arr.reduce( + (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc), + Array.from({ + length: Math.max(...arr.map(x => x.length)) + }).map(x => []) + ); +``` + +
+Examples + +```js +unzip([['a', 1, true], ['b', 2, false]]); //[['a', 'b'], [1, 2], [true, false]] +unzip([['a', 1, true], ['b', 2]]); //[['a', 'b'], [1, 2], [true]] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + +### unzipWith ![advanced](/advanced.svg) + +Creates an array of elements, ungrouping the elements in an array produced by [zip](#zip) and applying the provided function. + +Use `Math.max.apply()` to get the longest subarray in the array, `Array.prototype.map()` to make each element an array. +Use `Array.prototype.reduce()` and `Array.prototype.forEach()` to map grouped values to individual arrays. +Use `Array.prototype.map()` and the spread operator (`...`) to apply `fn` to each individual group of elements. + +```js +const unzipWith = (arr, fn) => + arr + .reduce( + (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc), + Array.from({ + length: Math.max(...arr.map(x => x.length)) + }).map(x => []) + ) + .map(val => fn(...val)); +``` + +
+Examples + +```js +unzipWith([[1, 10, 100], [2, 20, 200]], (...args) => args.reduce((acc, v) => acc + v, 0)); // [3, 30, 300] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### without Filters out the elements of an array, that have one of the specified values. -Use `Array.filter()` to create an array excluding(using `!Array.includes()`) all given values. +Use `Array.prototype.filter()` to create an array excluding(using `!Array.includes()`) all given values. _(For a snippet that mutates the original array see [`pull`](#pull))_ @@ -1670,6 +3093,26 @@ without([2, 1, 2, 3], 1, 2); // [3]
[⬆ Back to top](#table-of-contents) +### xProd + +Creates a new array out of the two supplied by creating each possible pair from the arrays. + +Use `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. + +```js +const xProd = (a, b) => a.reduce((acc, x) => acc.concat(b.map(y => [x, y])), []); +``` + +
+Examples + +```js +xProd([1, 2], ['a', 'b']); // [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']] +``` + +
+ +
[⬆ Back to top](#table-of-contents) ### zip @@ -1700,12 +3143,11 @@ zip(['a'], [1, 2], [true, false]); // [['a', 1, true], [undefined, 2, false]]
[⬆ Back to top](#table-of-contents) - ### zipObject Given an array of valid property identifiers and an array of values, return an object associating the properties to the values. -Since 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.reduce()`. +Since 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()`. ```js const zipObject = (props, values) => @@ -1724,18 +3166,60 @@ zipObject(['a', 'b'], [1, 2, 3]); // {a: 1, b: 2}
[⬆ Back to top](#table-of-contents) +### zipWith ![advanced](/advanced.svg) + +Creates 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. + +Check if the last argument provided is a function. +Use `Math.max()` to get the longest array in the arguments. +Creates an array with that length as return value and use `Array.from()` with a map-function to create an array of grouped elements. +If lengths of the argument-arrays vary, `undefined` is used where no value could be found. +The function is invoked with the elements of each group `(...group)`. + +```js +const zipWith = (...array) => { + const fn = typeof array[array.length - 1] === 'function' ? array.pop() : undefined; + return Array.from( + { length: Math.max(...array.map(a => a.length)) }, + (_, i) => (fn ? fn(...array.map(a => a[i])) : array.map(a => a[i])) + ); +}; +``` + +
+Examples + +```js +zipWith([1, 2], [10, 20], [100, 200], (a, b, c) => a + b + c); // [111,222] +zipWith( + [1, 2, 3], + [10, 20], + [100, 200], + (a, b, c) => (a != null ? a : 'a') + (b != null ? b : 'b') + (c != null ? c : 'c') +); // [111, 222, '3bc'] +``` + +
+ +
[⬆ Back to top](#table-of-contents) + + --- - ## 🌐 Browser + +## 🌐 Browser ### arrayToHtmlList Converts the given array elements into `
  • ` tags and appends them to the list of the given id. -Use `Array.map()` and `document.querySelector()` to create a list of html tags. +Use `Array.prototype.map()`, `document.querySelector()`, and an anonymous inner closure to create a list of html tags. ```js const arrayToHtmlList = (arr, listID) => - arr.map(item => (document.querySelector('#' + listID).innerHTML += `
  • ${item}
  • `)); + (el => ( + (el = document.querySelector('#' + listID)), + (el.innerHTML += arr.map(item => `
  • ${item}
  • `).join('')) + ))(); ```
    @@ -1749,7 +3233,6 @@ arrayToHtmlList(['item 1', 'item 2'], 'myListID');
    [⬆ Back to top](#table-of-contents) - ### bottomVisible Returns `true` if the bottom of the page is visible, `false` otherwise. @@ -1773,10 +3256,12 @@ bottomVisible(); // true
    [⬆ Back to top](#table-of-contents) +### copyToClipboard ![advanced](/advanced.svg) -### copyToClipboard +⚠️ **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). -Copy a string to the clipboard. Only works as a result of user action (i.e. inside a `click` event listener). +Copy a string to the clipboard. +Only works as a result of user action (i.e. inside a `click` event listener). Create a new `