Merge branch 'master' into master

This commit is contained in:
Angelos Chalaris
2017-12-15 13:51:58 +02:00
committed by GitHub
50 changed files with 2561 additions and 752 deletions

2
.gitignore vendored
View File

@ -1,3 +1,3 @@
node_modules/
currentSnippet\.js
*.md.temp.js

View File

@ -7,14 +7,15 @@ 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).
- Fix typos in existing snippets or run `npm run lint "snippet-name.md"` on unlinted snippets (yes, this is something we actually want help with).
- Tag untagged snippets by running `npm run tagger` and adding the appropriate tag next to the script name in `tag_database`.
- Fix typos in existing snippets or run `npm run linter` to lint unlinted snippets (yes, this is something we actually want help with, as this can take quite a while to run).
### Snippet submission and Pull request guidelines
- **DO NOT MODIFY THE README.md FILE!** Make changes to individual snippet files. You can optionally run `npm run build-list` to update the README.md file automatically, based on the changes you have made.
- **Snippet filenames** must correspond to the title of the snippet. For example if your snippet is titled `### Awesome snippet` the filename should be `awesome-snippet.md`.
- **Snippet filenames** must correspond to the title of the snippet. For example, if your snippet is titled `### Awesome snippet` the filename should be `awesome-snippet.md`.
- Use `kebab-case`, not `snake_case`.
- Avoid capitalization of words, except if the whole word is capitalized (e.g `URL` should be capitalized in the filename and the snippet title).
- Avoid capitalization of words, except if the whole word is capitalized (e.g. `URL` should be capitalized in the filename and the snippet title).
- If there are parentheses in the title, add them to the filename (e.g. `awesome-snippet-(extra-awesome).md` if your snippet's title is `Awesome snippet (extra awesome)`).
- **Snippet titles** should have only the first letter of the first word capitalized. Certain words can be in capitals (e.g. `URL`, `RGB`), but this is on a per-snippet basis.
- All snippet titles must be prefixed with `###` and be at the very first line of your snippet.
@ -28,23 +29,25 @@ Here's what you can do to help:
- Try to keep your snippets' code short and to the point. Use modern techniques and features. Make sure to test your code before submitting.
- All snippets must be followed by one (more if necessary) test case after the code, on a new line, in the form of a comment, along with the expected output. The syntax for this is `myFunction('testInput') -> 'testOutput'`. Use multiline comments only if necessary.
- Try to make your function name unique, so that it does not conflict with existing snippets.
- Snippets should be short (usually below 10 lines). If your snippet is longer than that, you can still submit it and we can help you shorten it or figure out ways to improve it.
- Snippets should be short (usually below 10 lines). If your snippet is longer than that, you can still submit it, and we can help you shorten it or figure out ways to improve it.
- Snippets *should* solve real-world problems, no matter how simple.
- Snippets *should* be abstract enough to be applied to different scenarios.
- It is not mandatory, but highly appreciated if you provide **test cases** and/or performance tests (we recommend using [jsPerf](https://jsperf.com/)).
- It is not mandatory but highly appreciated if you provide **test cases** and/or performance tests (we recommend using [jsPerf](https://jsperf.com/)).
- You can start creating a new snippet, by using the [snippet template](snippet-template.md) to format your snippets.
### Additional guidelines and conventions regarding snippets
- When describing snippets, refer to methods, using their full name. For example, use `Array.reduce()`, instead of `reduce()`.
- If your snippet contains argument with default parameters, explain what happens if they are ommited when calling the function and what the default case is.
- If your snippet 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.
- Use variables only when necessary. Prefer `const` when the values are not altered after assignment, otherwise use `let`. Avoid using `var`.
- Use variables only when necessary. Prefer `const` when the values are not altered after assignment, otherwise, use `let`. Avoid using `var`.
- Use `camelCase` for function and variable names if they consist of more than one word.
- Try to give meaningful names to variables. For example use `letter`, instead of `lt`. Some exceptions by convention are:
- 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).
- `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()`.
@ -52,7 +55,7 @@ Here's what you can do to help:
- `func` for function arguments.
- `nums` for arrays of numbers.
- Use `_` if your function takes no arguments or if an argument inside some function (e.g. `Array.reduce()`) is not used anywhere in your code.
- Specify default parameters for arguments, if necessary. It is preferred to put default parameters last, unless you have pretty good reason not to.
- Specify default parameters for arguments, if necessary. It is preferred to put default parameters last unless you have pretty good reason not to.
- If your snippet's function takes variadic arguments, use `..args` (although in certain cases, it might be needed to use a different name).
- If your snippet function's body is a single statement, omit the `return` keyword and use an expression instead.
- Always use soft tabs (2 spaces), never hard tabs.
@ -70,5 +73,5 @@ Here's what you can do to help:
- Use arrow functions as much as possible, except when you can't.
- Use semicolons whenever necessary. If your snippet function's body is a single statement, return an expression and add a semicolon at the end.
- Leave a single space after a comma (`,`) character.
- Try to strike a balance between readability, brevity and performance.
- Try to strike a balance between readability, brevity, and performance.
- Never use `eval()`. Your snippet will be disqualified immediately.

1613
README.md

File diff suppressed because it is too large Load Diff

65
package-lock.json generated
View File

@ -65,16 +65,6 @@
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
"integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4="
},
"ansi-regex": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz",
"integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk="
},
"ansi-styles": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz",
"integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94="
},
"anymatch": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
@ -333,21 +323,39 @@
"integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0="
},
"chalk": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz",
"integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=",
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz",
"integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==",
"dev": true,
"requires": {
"ansi-styles": "1.1.0",
"ansi-styles": "3.2.0",
"escape-string-regexp": "1.0.5",
"has-ansi": "0.1.0",
"strip-ansi": "0.3.0",
"supports-color": "0.2.0"
"supports-color": "4.5.0"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
"dev": true,
"requires": {
"color-convert": "1.9.1"
}
},
"has-flag": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
"integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
"dev": true
},
"supports-color": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz",
"integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo="
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
"integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
"dev": true,
"requires": {
"has-flag": "2.0.0"
}
}
}
},
@ -442,7 +450,6 @@
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.5.1.tgz",
"integrity": "sha512-689HrwGw8Rbk1xtV9C4dY6TPJAvIYZbRbnKSAtfJ7tHqICFGoZ0PCWYjxfmerRyxBG0o3sbG3pe7N8vqPwIHuQ==",
"requires": {
"chalk": "0.5.1",
"commander": "2.6.0",
"date-fns": "1.29.0",
"lodash": "4.17.4",
@ -1346,14 +1353,6 @@
"function-bind": "1.1.1"
}
},
"has-ansi": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz",
"integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=",
"requires": {
"ansi-regex": "0.2.1"
}
},
"has-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
@ -2855,14 +2854,6 @@
"safe-buffer": "5.1.1"
}
},
"strip-ansi": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz",
"integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=",
"requires": {
"ansi-regex": "0.2.1"
}
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",

View File

@ -11,10 +11,13 @@
"description": "A collection of useful Javascript snippets.",
"version": "1.0.0",
"main": "index.js",
"devDependencies": {},
"devDependencies": {
"chalk": "^2.3.0"
},
"scripts": {
"build-list": "node ./scripts/builder.js",
"lint": "node ./scripts/lintSnippet.js",
"builder": "node ./scripts/build-script.js",
"linter": "node ./scripts/lint-script.js",
"tagger": "node ./scripts/tag-script.js",
"start": "concurrently --kill-others \"nodemon -e js,md -i README.md -x \\\"npm run build-list\\\"\" \"live-server ./build\""
},
"repository": {

79
scripts/build-script.js Normal file
View File

@ -0,0 +1,79 @@
/*
This is the builder script that generates the README file.
Run using `npm run builder`.
*/
// Load modules
const fs = require('fs-extra'), path = require('path'), chalk = require('chalk');
// Set variables for paths
const snippetsPath = './snippets', staticPartsPath = './static-parts';
// Set variables for script
let snippets = {}, startPart = '', endPart = '', output = '', tagDbData = {};
// Load helper functions (these are from existing snippets in 30 seconds of code!)
const objectFromPairs = arr => arr.reduce((a, v) => (a[v[0]] = v[1], a), {});
const capitalize = (str, lowerRest = false) => str.slice(0, 1).toUpperCase() + (lowerRest ? str.slice(1).toLowerCase() : str.slice(1));
// Start the timer of the script
console.time('Builder');
// Synchronously read all snippets and sort them as necessary (case-insensitive)
try {
let snippetFilenames = fs.readdirSync(snippetsPath);
snippetFilenames.sort((a, b) => {
a = a.toLowerCase();
b = b.toLowerCase();
if (a < b) return -1;
if (a > b) return 1;
return 0;
});
// Store the data read from each snippet in the appropriate object
for(let snippet of snippetFilenames) snippets[snippet] = fs.readFileSync(path.join(snippetsPath,snippet),'utf8');
}
catch (err){ // Handle errors (hopefully not!)
console.log(`${chalk.red('ERROR!')} During snippet loading: ${err}`);
process.exit(1);
}
// Load static parts for the README file
try {
startPart = fs.readFileSync(path.join(staticPartsPath,'README-start.md'),'utf8');
endPart = fs.readFileSync(path.join(staticPartsPath,'README-end.md'),'utf8');
}
catch (err){ // Handle errors (hopefully not!)
console.log(`${chalk.red('ERROR!')} During static part loading: ${err}`);
process.exit(1);
}
// Load tag data from the database
try {
tagDbData = objectFromPairs(fs.readFileSync('tag_database','utf8').split('\n').slice(0,-1).map(v => v.split(':').slice(0,2)));
}
catch (err){ // Handle errors (hopefully not!)
console.log(`${chalk.red('ERROR!')} During tag database loading: ${err}`);
process.exit(1);
}
// Create the output for the README file
try {
// Add the start static part
output += `${startPart+'\n'}`;
// Loop over tags and snippets to create the table of contents
for(let tag of [...new Set(Object.entries(tagDbData).map(t => t[1]))].filter(v => v).sort((a,b) => a.localeCompare(b))){
output +=`### ${capitalize(tag, true)}\n`;
for(let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
output += `* [${taggedSnippet[0][0].toUpperCase() + taggedSnippet[0].replace(/-/g,' ').slice(1)}](#${taggedSnippet[0].replace(/\(/g,'').replace(/\)/g,'').toLowerCase()})\n`
output += '\n';
}
// Loop over tags and snippets to create the list of snippets
for(let tag of [...new Set(Object.entries(tagDbData).map(t => t[1]))].filter(v => v).sort((a,b) => a.localeCompare(b))){
output +=`## ${capitalize(tag, true)}\n`;
for(let taggedSnippet of Object.entries(tagDbData).filter(v => v[1] === tag))
output += `\n${snippets[taggedSnippet[0]+'.md']+'\n[⬆ back to top](#table-of-contents)\n'}`;
}
// Add the ending static part
output += `\n${endPart+'\n'}`;
// Write to the README file
fs.writeFileSync('README.md', output);
}
catch (err){ // Handle errors (hopefully not!)
console.log(`${chalk.red('ERROR!')} During README generation: ${err}`);
process.exit(1);
}
// Log a success message
console.log(`${chalk.green('SUCCESS!')} README file generated!`);
// Log the time taken
console.timeEnd('Builder');

View File

@ -1,57 +0,0 @@
var fs = require('fs-extra');
var path = require('path');
var snippetsPath = './snippets';
var staticPartsPath = './static-parts';
var snippets = {}, startPart = '', endPart = '', output = '';
console.time('Builder');
try {
var snippetFilenames = fs.readdirSync(snippetsPath);
snippetFilenames.sort((a, b) => {
a = a.toLowerCase();
b = b.toLowerCase();
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
});
for(var snippet of snippetFilenames){
snippets[snippet] = fs.readFileSync(path.join(snippetsPath,snippet),'utf8');
}
}
catch (err){
console.log('Error during snippet loading: '+err);
process.exit(1);
}
try {
startPart = fs.readFileSync(path.join(staticPartsPath,'README-start.md'),'utf8');
endPart = fs.readFileSync(path.join(staticPartsPath,'README-end.md'),'utf8');
}
catch (err){
console.log('Error during static part loading: '+err);
process.exit(1);
}
try {
output += `${startPart+'\n'}`;
for(var snippet of Object.entries(snippets))
output += `* [${snippet[0][0].toUpperCase() + snippet[0].replace(/-/g,' ').slice(1,snippet[0].length-3)}](#${snippet[0].slice(0,snippet[0].length-3).replace(/\(/g,'').replace(/\)/g,'').toLowerCase()})\n`
output += '\n';
for(var snippet of Object.entries(snippets))
output += `${snippet[1]+'\n'}`;
output += `${endPart+'\n'}`;
fs.writeFileSync('README.md', output);
}
catch (err){
console.log('Error during README generation: '+err);
process.exit(1);
}
console.timeEnd('Builder');

44
scripts/lint-script.js Normal file
View File

@ -0,0 +1,44 @@
/*
This is the linter script that lints snippets.
Run using `npm run linter`.
*/
// Load modules
const fs = require('fs-extra'), cp = require('child_process'), path = require('path'), chalk = require('chalk');
// Set variables for paths
var snippetsPath = './snippets';
// Read files, lint each one individually and update
try {
let snippetFilenames = fs.readdirSync(snippetsPath);
snippetFilenames.sort((a, b) => {
a = a.toLowerCase();
b = b.toLowerCase();
if (a < b) return -1;
if (a > b) return 1;
return 0;
});
// Read each file, get its code, write it to a temporary file, pass it through
// semistandard, get the output from the file, update the original file.
for(let snippet of snippetFilenames){
// Start a timer for the file
console.time(`Linter (${snippet})`);
// Synchronously read data from the snippet, get the code, write it to a temporary file
let snippetData = fs.readFileSync(path.join(snippetsPath,snippet),'utf8');
let originalCode = snippetData.slice(snippetData.indexOf('```js')+5,snippetData.lastIndexOf('```'));
fs.writeFileSync(`${snippet}.temp.js`,`${originalCode}`);
// Run semistandard asynchronously (only way this manages to run), get linted code
// and write back to the original snippet file. Remove temporary file
cp.exec(`semistandard "${snippet}.temp.js" --fix`,{},(error, stdOut, stdErr) => {
let lintedCode = fs.readFileSync(`${snippet}.temp.js`,'utf8');
fs.writeFile(path.join(snippetsPath,snippet), `${snippetData.slice(0, snippetData.indexOf('```js')+5)+lintedCode+'```\n'}`);
fs.unlink(`${snippet}.temp.js`);
// Log a success message
console.log(`${chalk.green('SUCCESS!')} Linted snippet: ${snippet}`);
// Log the time taken for the file
console.timeEnd(`Linter (${snippet})`);
});
}
}
catch (err){ // Handle errors (hopefully not!)
console.log(`${chalk.red('ERROR!')} During linting: ${err}`);
process.exit(1);
}

View File

@ -1,31 +0,0 @@
var fs = require('fs-extra');
var cp = require('child_process');
var path = require('path');
var snippetsPath = './snippets';
var snippetFilename = '';
console.time('Linter');
if(process.argv.length < 3){
console.log('Please specify the filename of a snippet to be linted.');
console.log('Example usage: npm run lint "snippet-file.md"');
process.exit(0);
}
else {
snippetFilename = process.argv[2];
let snippetData = fs.readFileSync(path.join(snippetsPath,snippetFilename),'utf8');
try {
let originalCode = snippetData.slice(snippetData.indexOf('```js')+5,snippetData.lastIndexOf('```'));
fs.writeFileSync('currentSnippet.js',`${originalCode}`);
cp.exec('semistandard "currentSnippet.js" --fix',{},(error, stdOut, stdErr) => {
let lintedCode = fs.readFileSync('currentSnippet.js','utf8');
fs.writeFile(path.join(snippetsPath,snippetFilename), `${snippetData.slice(0, snippetData.indexOf('```js')+5)+lintedCode+'```\n'}`);
console.timeEnd('Linter');
});
}
catch (err){
console.log('Error during snippet loading: '+err);
process.exit(1);
}
}

67
scripts/tag-script.js Normal file
View File

@ -0,0 +1,67 @@
/*
This is the tagger script that updates the tag_databse file and logs stats for snippet tags.
Run using `npm run tagger`.
*/
// Load modules
const fs = require('fs-extra'), path = require('path'), chalk = require('chalk');
// Set variables for paths
const snippetsPath = './snippets';
// Set variables for script
let snippets = {}, output = '', tagDbData = {}, missingTags = 0, tagDbStats = {};
// Load helper functions (these are from existing snippets in 30 seconds of code!)
const objectFromPairs = arr => arr.reduce((a, v) => (a[v[0]] = v[1], a), {});
const countOccurrences = (arr, value) => arr.reduce((a, v) => v === value ? a + 1 : a + 0, 0);
// Start the timer of the script
console.time('Tagger');
// Synchronously read all snippets and sort them as necessary (case-insensitive)
try {
let snippetFilenames = fs.readdirSync(snippetsPath);
snippetFilenames.sort((a, b) => {
a = a.toLowerCase();
b = b.toLowerCase();
if (a < b) return -1;
if (a > b) return 1;
return 0;
});
// Store the data read from each snippet in the appropriate object
for(let snippet of snippetFilenames) snippets[snippet] = fs.readFileSync(path.join(snippetsPath,snippet),'utf8');
}
catch (err){ // Handle errors (hopefully not!)
console.log(`${chalk.red('ERROR!')} During snippet loading: ${err}`);
process.exit(1);
}
// Load tag data from the database
try {
tagDbData = objectFromPairs(fs.readFileSync('tag_database','utf8').split('\n').slice(0,-1).map(v => v.split(':').slice(0,2)));
tagDbStats = Object.entries(tagDbData).sort((a,b) => a[1].localeCompare(b[1])).reduce((acc, val) => {acc.hasOwnProperty(val[1]) ? acc[val[1]]++ : acc[val[1]] = 1; return acc;}, {});
}
catch (err){ // Handle errors (hopefully not!)
console.log(`${chalk.red('ERROR!')} During tag database loading: ${err}`);
process.exit(1);
}
// Update the listing of snippets in tag_database and log the statistics, along with missing scripts
try {
for(let snippet of Object.entries(snippets))
if(tagDbData.hasOwnProperty(snippet[0].slice(0,-3)) && tagDbData[snippet[0].slice(0,-3)].trim())
output += `${snippet[0].slice(0,-3)}:${tagDbData[snippet[0].slice(0,-3)].trim()}\n`;
else {
output += `${snippet[0].slice(0,-3)}:\n`;
missingTags++;
console.log(`${chalk.yellow('Tag missing:')} ${snippet[0].slice(0,-3)}`);
}
// Write to tag_database
fs.writeFileSync('tag_database', output);
}
catch (err){ // Handle errors (hopefully not!)
console.log(`${chalk.red('ERROR!')} During tag_database generation: ${err}`);
process.exit(1);
}
// Log statistics for the tag_database file
console.log(`\n${chalk.bgWhite(chalk.black('=== TAG STATS ==='))}`)
for(let tagData of Object.entries(tagDbStats).filter(v => v[0] !== 'undefined'))
console.log(`${chalk.green(tagData[0])}: ${tagData[1]} snippets`);
console.log(`${chalk.blue('Untagged snippets:')} ${missingTags}\n`);
// Log a success message
console.log(`${chalk.green('SUCCESS!')} tag_database file updated!`);
// Log the time taken
console.timeEnd('Tagger');

View File

@ -3,6 +3,6 @@
Create a `Set` from `b`, then use `Array.filter()` on `a` to only keep values not contained in `b`.
```js
const difference = (a, b) => { const s = new Set(b); return a.filter(x => !s.has(x)); }
const difference = (a, b) => { const s = new Set(b); return a.filter(x => !s.has(x)); };
// difference([1,2,3], [1,2]) -> [3]
```

View File

@ -0,0 +1,10 @@
### Array includes
Use `slice()` to offset the array/string and `indexOf()` to check if the value is included.
Omit the last argument, `fromIndex`, to check the whole array/string.
```js
const includes = (collection, val, fromIndex=0) => collection.slice(fromIndex).indexOf(val) != -1;
// includes("30-seconds-of-code", "code") -> true
// includes([1, 2, 3, 4], [1, 2], 1) -> false
```

View File

@ -3,6 +3,6 @@
Create a `Set` from `b`, then use `Array.filter()` on `a` to only keep values contained in `b`.
```js
const intersection = (a, b) => { const s = new Set(b); return a.filter(x => s.has(x)); }
const intersection = (a, b) => { const s = new Set(b); return a.filter(x => s.has(x)); };
// intersection([1,2,3], [4,3,2]) -> [2,3]
```

9
snippets/array-sample.md Normal file
View File

@ -0,0 +1,9 @@
### Array sample
Use `Math.random()` to generate a random number, multiply it with `length` and round it of to the nearest whole number using `Math.floor()`.
This method also works with strings.
```js
const sample = arr => arr[Math.floor(Math.random() * arr.length)];
// sample([3, 7, 9, 11]) -> 9
```

View File

@ -3,6 +3,6 @@
Create a `Set` with all values of `a` and `b` and convert to an array.
```js
const union = (a, b) => Array.from(new Set([...a, ...b]))
const union = (a, b) => Array.from(new Set([...a, ...b]));
// union([1,2,3], [4,3,2]) -> [1,2,3,4]
```

View File

@ -0,0 +1,9 @@
### Array without
Use `Array.filter()` to create an array excluding all given values.
```js
const without = (arr, ...args) => arr.filter(v => args.indexOf(v) === -1);
// without[2, 1, 2, 3], 1, 2) -> [3]
// without([2, 1, 2, 3, 4, 5, 5, 5, 3, 2, 7, 7], 3, 1, 5, 2) -> [ 4, 7, 7 ]
```

16
snippets/array-zip.md Normal file
View File

@ -0,0 +1,16 @@
### Array zip
Use `Math.max.apply()` 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.
```js
const zip = (...arrays) => {
const maxLength = Math.max.apply(null, arrays.map(a => a.length));
return Array.from({length: maxLength}).map((_, i) => {
return Array.from({length: arrays.length}, (_, k) => arrays[k][i]);
})
}
//zip(['a', 'b'], [1, 2], [true, false]); -> [['a', 1, true], ['b', 2, false]]
//zip(['a'], [1, 2], [true, false]); -> [['a', 1, true], [undefined, 2, false]]
```

View File

@ -1,10 +1,11 @@
### Capitalize first letter
Use `slice(0,1)` and `toUpperCase()` to capitalize first letter, `slice(1)` to get the rest of the string.
Use destructuring and `toUpperCase()` to capitalize first letter, `...rest` to get array of characters after first letter and then `Array.join('')` to make it a string again.
Omit the `lowerRest` parameter to keep the rest of the string intact, or set it to `true` to convert to lower case.
```js
const capitalize = (str, lowerRest = false) =>
str.slice(0, 1).toUpperCase() + (lowerRest ? str.slice(1).toLowerCase() : str.slice(1));
const capitalize = ([first,...rest], lowerRest = false) =>
first.toUpperCase() + (lowerRest ? rest.join('').toLowerCase() : rest.join(''));
// capitalize('myName') -> 'MyName'
// capitalize('myName', true) -> 'Myname'
```

View File

@ -7,5 +7,5 @@ If the original array can't be split evenly, the final chunk will contain the re
```js
const chunk = (arr, size) =>
Array.from({length: Math.ceil(arr.length / size)}, (v, i) => arr.slice(i * size, i * size + size));
// chunk([1,2,3,4,5], 2) -> [[1,2],[3,4],5]
// chunk([1,2,3,4,5], 2) -> [[1,2],[3,4],[5]]
```

View File

@ -0,0 +1,9 @@
### Collatz algorithm
If `n` is even, return `n/2`. Otherwise return `3n+1`.
```js
const collatz = n => (n % 2 == 0) ? (n / 2) : (3 * n + 1);
// collatz(8) --> 4
// collatz(5) --> 16
```

View File

@ -6,13 +6,10 @@ Otherwise return a curried function `f` that expects the rest of the arguments.
If you want to curry a function that accepts a variable number of arguments (a variadic function, e.g. `Math.min()`), you can optionally pass the number of arguments to the second parameter `arity`.
```js
const curry = (f, arity = f.length, next) =>
(next = prevArgs =>
nextArg => {
const args = [ ...prevArgs, nextArg ];
return args.length >= arity ? f(...args) : next(args);
}
)([]);
const curry = (fn, arity = fn.length, ...args) =>
arity <= args.length
? fn(...args)
: curry.bind(null, fn, arity, ...args);
// curry(Math.pow)(2)(10) -> 1024
// curry(Math.min, 3)(10)(50)(2) -> 2
```

View File

@ -1,10 +1,10 @@
### Deep flatten array
Use recursion.
Use `Array.reduce()` to get all elements that are not arrays, flatten each element that is an array.
Use `Array.concat()` with an empty array (`[]`) and the spread operator (`...`) to flatten an array.
Rrecursively flatten each element that is an array.
```js
const deepFlatten = arr =>
arr.reduce((a, v) => a.concat(Array.isArray(v) ? deepFlatten(v) : v), []);
const deepFlatten = arr => [].concat(...arr.map(v => Array.isArray(v) ? deepFlatten(v) : v));
// deepFlatten([1,[2],[[3],4],5]) -> [1,2,3,4,5]
```

View File

@ -0,0 +1,12 @@
### Drop elements in array
Loop through the array, using `Array.shift()` 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.shift();
return arr;
};
// dropElements([1, 2, 3, 4], n => n >= 3) -> [3,4]
```

View File

@ -0,0 +1,19 @@
### Element is visible in viewport
Use `Element.getBoundingClientRect()` and the `window.inner(Width|Height)` values
to determine if a given element is visible in the viewport.
Omit the second argument to determine if the element is entirely visible, or specify `true` to determine if
it is partially visible.
```js
const elementIsVisibleInViewport = (el, partiallyVisible = false) => {
const { top, left, bottom, right } = el.getBoundingClientRect();
return partiallyVisible
? ((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) &&
((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))
: top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;
};
// e.g. 100x100 viewport and a 10x10px element at position {top: -1, left: 0, bottom: 9, right: 10}
// elementIsVisibleInViewport(el) -> false (not fully visible)
// elementIsVisibleInViewport(el, true) -> true (partially visible)
```

10
snippets/fill-array.md Normal file
View File

@ -0,0 +1,10 @@
### Fill array
Use `Array.map()` to map values between `start` (inclusive) and `end` (exclusive) to `value`.
Omit `start` to start at the first element and/or `end` to finish at the last.
```js
const fillArray = (arr, value, start = 0, end = arr.length) =>
arr.map((v, i) => i >= start && i < end ? value : v);
// fillArray([1,2,3,4],'8',1,3) -> [1,'8','8',4]
```

View File

@ -0,0 +1,13 @@
### Flatten array up to depth
Use recursion, decrementing `depth` by 1 for each level of depth.
Use `Array.reduce()` and `Array.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).
```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), []);
// flattenDepth([1,[2],[[[3],4],5]], 2) -> [1,2,[3],4,5]
```

View File

@ -0,0 +1,8 @@
### Get days difference between dates
Calculate the difference (in days) between to `Date` objects.
```js
const getDaysDiffBetweenDates = (dateInitial, dateFinal) => (dateFinal - dateInitial) / (1000 * 3600 * 24);
// getDaysDiffBetweenDates(new Date("2017-12-13"), new Date("2017-12-22")) -> 9
```

View File

@ -5,10 +5,8 @@ Use `Array.reduce()` to create an object, where the keys are produced from the m
```js
const groupBy = (arr, func) =>
(typeof func === 'function' ? arr.map(func) : arr.map(val => val[func]))
.reduce((acc, val, i) => {
acc[val] = acc[val] === undefined ? [arr[i]] : acc[val].concat(arr[i]); return acc;
}, {});
arr.map(typeof func === 'function' ? func : val => val[func])
.reduce((acc, val, i) => { acc[val] = (acc[val] || []).concat(arr[i]); return acc; }, {});
// groupBy([6.1, 4.2, 6.3], Math.floor) -> {4: [4.2], 6: [6.1, 6.3]}
// groupBy(['one', 'two', 'three'], 'length') -> {3: ['one', 'two'], 5: ['three']}
```

9
snippets/is-array.md Normal file
View File

@ -0,0 +1,9 @@
### Is array
Use `Array.isArray()` to check if a value is classified as an array.
```js
const isArray = val => !!val && Array.isArray(val);
// isArray(null) -> false
// isArray([1]) -> true
```

9
snippets/is-boolean.md Normal file
View File

@ -0,0 +1,9 @@
### Is boolean
Use `typeof` to check if a value is classified as a boolean primitive.
```js
const isBoolean = val => typeof val === 'boolean';
// isBoolean(null) -> false
// isBoolean(false) -> true
```

9
snippets/is-function.md Normal file
View File

@ -0,0 +1,9 @@
### Is function
Use `typeof` to check if a value is classified as a function primitive.
```js
const isFunction = val => val && typeof val === 'function';
// isFunction('x') -> false
// isFunction(x => x) -> true
```

9
snippets/is-number.md Normal file
View File

@ -0,0 +1,9 @@
### Is number
Use `typeof` to check if a value is classified as a number primitive.
```js
const isNumber = val => typeof val === 'number';
// isNumber('1') -> false
// isNumber(1) -> true
```

9
snippets/is-string.md Normal file
View File

@ -0,0 +1,9 @@
### Is string
Use `typeof` to check if a value is classified as a string primitive.
```js
const isString = val => typeof val === 'string';
// isString(10) -> false
// isString('10') -> true
```

9
snippets/is-symbol.md Normal file
View File

@ -0,0 +1,9 @@
### Is symbol
Use `typeof` to check if a value is classified as a symbol primitive.
```js
const isSymbol = val => typeof val === 'symbol';
// isSymbol('x') -> false
// isSymbol(Symbol('x')) -> true
```

View File

@ -1,13 +1,14 @@
### Measure time taken by function
Use `performance.now()` to get start and end time for the function, `console.log()` the time taken.
Pass a callback function as the argument.
Use `console.time()` and `console.timeEnd()` to measure the difference between the start and end times to determine how long the callback took to execute.
```js
const timeTaken = callback => {
const t0 = performance.now(), r = callback();
console.log(performance.now() - t0);
console.time('timeTaken');
const r = callback();
console.timeEnd('timeTaken');
return r;
};
// timeTaken(() => Math.pow(2, 10)) -> 1024 (0.010000000009313226 logged in console)
// timeTaken(() => Math.pow(2, 10)) -> 1024
// (logged): timeTaken: 0.02099609375ms
```

View File

@ -0,0 +1,11 @@
### Nth element of array
Use `Array.slice()` to get an array containing the nth element at the first place.
If the index is out of bounds, return `[]`.
Omit the second argument, `n`, to get the first element of the array.
```js
const nth = (arr, n=0) => (n>0? arr.slice(n,n+1) : arr.slice(n))[0];
// nth(['a','b','c'],1) -> 'b'
// nth(['a','b','b']-2) -> 'a'
```

View File

@ -0,0 +1,9 @@
### Number to array of digits
Convert the number to a string, use `split()` to convert build an array.
Use `Array.map()` and `parseInt()` to transform each value to an integer.
```js
const digitize = n => (''+n).split('').map(i => parseInt(i));
// digitize(2334) -> [2, 3, 3, 4]
```

View File

@ -7,9 +7,9 @@ If digit is found in teens pattern, use teens ordinal.
```js
const toOrdinalSuffix = num => {
const int = parseInt(num), digits = [(int % 10), (int % 100)],
ordinals = ["st", "nd", "rd", "th"], oPattern = [1,2,3,4],
tPattern = [11, 12, 13, 14, 15, 16, 17, 18, 19]
return oPattern.includes(digits[0]) && !tPattern.includes(digits[1]) ? int + ordinals[digits[0]-1] : int + ordinals[3];
}
ordinals = ['st', 'nd', 'rd', 'th'], oPattern = [1, 2, 3, 4],
tPattern = [11, 12, 13, 14, 15, 16, 17, 18, 19];
return oPattern.includes(digits[0]) && !tPattern.includes(digits[1]) ? int + ordinals[digits[0] - 1] : int + ordinals[3];
};
// toOrdinalSuffix("123") -> "123rd"
```

View File

@ -1,8 +1,14 @@
### Pipe
Use `Array.reduce()` to pass value through functions.
Use `Array.reduce()` to perform left-to-right function composition.
The first (leftmost) function can accept one or more arguments; the remaining functions must be unary.
```js
const pipe = (...funcs) => arg => funcs.reduce((acc, func) => func(acc), arg);
// pipe(btoa, x => x.toUpperCase())("Test") -> "VGVZDA=="
const pipe = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)));
/*
const add5 = x => x + 5
const multiply = (x, y) => x * y
const multiplyAndAdd5 = pipe(multiply, add5)
multiplyAndAdd5(5, 2) -> 15
*/
```

View File

@ -0,0 +1,9 @@
### Round number to n digits
Use `Math.round()` and template literals to round the number to the specified number of digits.
Omit the second argument, `decimals` to round to an integer.
```js
const round = (n, decimals=0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`);
// round(1.005, 2) -> 1.01
```

View File

@ -0,0 +1,12 @@
### Shallow clone object
Use `Object.assign()` and an empty object (`{}`) to create a shallo clone of the original.
```js
const shallowClone = obj => Object.assign({}, obj);
/*
const a = { x: true, y: 1 };
const b = shallowClone(a);
a === b -> false
*/
```

View File

@ -0,0 +1,15 @@
### Speech synthesis (experimental)
Use `SpeechSynthesisUtterance.voice` and `indow.speechSynthesis.getVoices()` to convert a message to speech.
Use `window.speechSynthesis.speak()` to play the message.
Learn more about the [SpeechSynthesisUtterance interface of the Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance).
```js
const speak = message => {
const msg = new SpeechSynthesisUtterance(message);
msg.voice = window.speechSynthesis.getVoices()[0];
window.speechSynthesis.speak(msg);
};
// speak('Hello, World') -> plays the message
```

View File

@ -11,7 +11,7 @@ const standardDeviation = (arr, usePopulation = false) => {
arr.reduce((acc, val) => acc.concat(Math.pow(val - mean, 2)), [])
.reduce((acc, val) => acc + val, 0) / (arr.length - (usePopulation ? 0 : 1))
);
}
};
// standardDeviation([10,2,38,23,38,23,21]) -> 13.284434142114991 (sample)
// standardDeviation([10,2,38,23,38,23,21], true) -> 12.29899614287479 (population)
```

9
snippets/take-right.md Normal file
View File

@ -0,0 +1,9 @@
### Take right
Use `Array.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);
// takeRight([1, 2, 3], 2) -> [ 2, 3 ]
// takeRight([1, 2, 3]) -> [3]
```

View File

@ -4,7 +4,6 @@ Use `Array.slice()` to create a slice of the array with `n` elements taken from
```js
const take = (arr, n = 1) => arr.slice(0, n);
// take([1, 2, 3], 5) -> [1, 2, 3]
// take([1, 2, 3], 0) -> []
```

View File

@ -0,0 +1,9 @@
### Write a JSON to a file
Use `fs.writeFile()`, template literals and `JSON.stringify()` to write a `json` object to a `.json` file.
```js
const fs = require('fs');
const jsonToFile = (obj, filename) => fs.writeFile(`${filename}.json`, JSON.stringify(obj, null, 2))
// jsonToFile({test: "is passed"}, 'testJsonFile') -> writes the object to 'testJsonFile.json'
```

View File

@ -7,4 +7,4 @@
- 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.
## Contents
## Table of Contents

93
tag_database Normal file
View File

@ -0,0 +1,93 @@
anagrams-of-string-(with-duplicates):string
array-concatenation:array
array-difference:array
array-includes:array
array-intersection:array
array-sample:array
array-union:array
array-without:array
array-zip:array
average-of-array-of-numbers:array
bottom-visible:browser
capitalize-first-letter-of-every-word:string
capitalize-first-letter:string
chain-asynchronous-functions:function
check-for-palindrome:string
chunk-array:array
collatz-algorithm:math
compact:array
count-occurrences-of-a-value-in-array:array
current-URL:browser
curry:function
deep-flatten-array:array
distance-between-two-points:math
divisible-by-number:math
drop-elements-in-array:array
element-is-visible-in-viewport:browser
escape-regular-expression:utility
even-or-odd-number:math
factorial:math
fibonacci-array-generator:math
fill-array:array
filter-out-non-unique-values-in-an-array:array
flatten-array-up-to-depth:array
flatten-array:array
get-days-difference-between-dates:date
get-max-value-from-array:array
get-min-value-from-array:array
get-native-type-of-value:utility
get-scroll-position:browser
greatest-common-divisor-(GCD):math
group-by:array
hamming-distance:math
head-of-list:array
initial-of-list:array
initialize-array-with-range:array
initialize-array-with-values:array
is-array:utility
is-boolean:utility
is-function:utility
is-number:utility
is-string:utility
is-symbol:utility
last-of-list:array
measure-time-taken-by-function:utility
median-of-array-of-numbers:array
nth-element-of-array:array
number-to-array-of-digits:utility
object-from-key-value-pairs:object
object-to-key-value-pairs:object
ordinal-suffix-of-number:utility
percentile:math
pick:array
pipe:function
powerset:math
promisify:function
random-integer-in-range:utility
random-number-in-range:utility
redirect-to-URL:browser
reverse-a-string:string
RGB-to-hexadecimal:utility
round-number-to-n-digits:math
run-promises-in-series:function
scroll-to-top:browser
shallow-clone-object:object
shuffle-array:array
similarity-between-arrays:array
sleep:function
sort-characters-in-string-(alphabetical):string
speech-synthesis-(experimental):media
standard-deviation:math
sum-of-array-of-numbers:array
swap-values-of-two-variables:utility
tail-of-list:array
take-right:array
take:array
truncate-a-string:string
unique-values-of-array:array
URL-parameters:utility
UUID-generator:utility
validate-email:utility
validate-number:utility
value-or-default:utility
write-json-to-file:node

913
yarn.lock

File diff suppressed because it is too large Load Diff