├── .eslintrc
├── .gitignore
├── LICENSE
├── README.md
├── arrays
├── empty-an-array
│ └── index.js
└── merge-array-alternating-values
│ └── index.js
├── axios
├── download-files-with-progress
│ ├── images
│ │ └── .gitignore
│ └── index.js
└── download-files
│ ├── images
│ └── .gitignore
│ └── index.js
├── code-styling
├── 01-callbacks.js
├── 02-promises.js
├── 03-async-await.js
├── async-or-not-async
│ └── async-or-not-async.js
├── await-sync-function
│ └── await-sync-function.js
└── combine-callbacks-and-promises
│ ├── base-command.js
│ └── deploy-command.js
├── collections
├── array-compact
│ └── array-compact.js
├── async-array-every
│ └── async-array-every.js
├── async-array-filter
│ └── async-array-filter.js
├── async-array-find
│ └── async-array-find.js
├── async-array-forEach
│ └── async-array-forEach.js
├── async-array-map
│ └── async-array-map.js
├── async-array-mapSeries
│ └── async-array-mapSeries.js
└── async-array-some
│ └── async-array-some.js
├── data-structures
├── implementations
│ ├── linked-list
│ │ ├── linked-list.js
│ │ └── node.js
│ ├── priority-queue
│ │ └── priority-queue.js
│ ├── queue
│ │ └── queue.js
│ ├── set
│ │ └── set.js
│ └── stack
│ │ └── stack.js
└── starter-files
│ ├── linked-list.js
│ ├── priority-queue.js
│ ├── queue.js
│ ├── set.js
│ └── stack.js
├── date
├── increase-date-by-one-week.js
└── tomorrows-date.js
├── error-handling
└── unhandled-rejection.js
├── events
└── async-event-listener
│ ├── event.js
│ ├── index.js
│ └── listener.js
├── filesystem
├── create-an-empty-file
│ ├── .gitignore
│ └── index.js
├── file-created-date
│ ├── content.txt
│ └── index.js
├── file-last-updated-date
│ ├── content.txt
│ └── index.js
└── write-json-object-to-file
│ ├── content.txt
│ ├── index.js
│ └── promisified.js
├── flow-control
├── async-constructors
│ ├── index.js
│ └── sample-cache-driver.js
├── promise-and-callback
│ ├── index.js
│ └── promise-and-callback.js
├── promises-in-parallel
│ ├── index.js
│ └── with-errors.js
├── promises-in-sequence
│ ├── index.js
│ └── with-errors.js
└── promises-waterfall
│ ├── .gitignore
│ └── index.js
├── loops
└── for-of-vs-for-in
│ └── for-of-vs-for-in.js
├── misc
├── custom-error
│ ├── error.js
│ └── index.js
└── increased-memory-limit
│ └── show-memory-limit.js
├── objects
├── deep-merge.js
└── merge-objects.js
├── package.json
├── streaming
└── async-readline
│ ├── async-stringify-transform.js
│ └── input.js
├── strings
├── json-stringify-with-spaces-and-line-breaks
│ └── index.js
└── string-replace-all-appearances
│ └── index.js
└── test
└── data-structures
├── linked-list.js
├── priority-queue.js
├── queue.js
├── set.js
└── stack.js
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true,
4 | "node": true
5 | },
6 | "extends": "standard",
7 | "parserOptions": {
8 | "ecmaVersion": 2018
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 | package-lock.json
33 |
34 | # Optional npm cache directory
35 | .npm
36 |
37 | # Optional REPL history
38 | .node_repl_history
39 |
40 | # IDEA (Webstorm)
41 | .idea
42 |
43 | # Visual Studio Code
44 | .vscode
45 |
46 | # bcrypt windows
47 | build
48 | src
49 | binding.gyp
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016-2018 Future Studio
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Node.js tutorials
2 | This repository includes examples for the [Node.js tutorials published on Future Studio](https://futurestud.io/tutorials/tag/nodejs). Enjoy toying with all the individual examples and feel free to apply the practical approaches in your apps.
3 |
4 |
5 |
6 | ------
7 |
8 |
The Node.js tutorials are supported by Future Studio University 🚀
9 |
10 | Join the Future Studio University and Skyrocket in Node.js
11 |
12 |
13 | ------
14 |
15 |
16 | ## YouTube Playlist
17 | Even though there aren’t that many tutorials, find the [Node.js playlist on YouTube](https://www.youtube.com/watch?v=s6TNwLnhppk&list=PLpUMhvC6l7AMwyuEqLPvfEtKQbdD4BJ5o).
18 |
19 |
20 | ---
21 |
22 |
23 | ## Tutorials
24 |
25 |
26 | #### Axios
27 | - [Axios — Download Files in Node.js](https://futurestud.io/tutorials/download-files-images-with-axios-in-node-js)
28 |
29 |
30 | #### Mocha
31 | - [Mocha — Global Setup and Teardown (before/after)](https://futurestud.io/tutorials/mocha-global-setup-and-teardown-before-after)
32 |
33 |
34 | #### Flow Control
35 | - [Node.js — Run Async Functions/Promises in Sequence](https://futurestud.io/tutorials/node-js-run-async-functions-promises-in-sequence)
36 |
37 |
38 | #### Classes
39 | - [Node.js — Extend Multiple Classes (Multi Inheritance)](https://futurestud.io/tutorials/node-js-extend-multiple-classes-multi-inheritance)
40 |
41 |
42 | #### Collections
43 | - [Node.js — Run an Asynchronous Function in Array.map()](https://futurestud.io/tutorials/node-js-how-to-run-an-asynchronous-function-in-array-map)
44 |
45 |
46 | #### Filesystem
47 | - [Node.js — Get a File’s Last Modified/Updated Date](https://futurestud.io/tutorials/node-js-get-a-files-last-modified-updated-date)
48 | - [Node.js — Get a File’s Created Date](https://futurestud.io/tutorials/node-js-get-a-files-created-date)
49 | - [Node.js — Write a JSON Object to a File](https://futurestud.io/tutorials/node-js-write-a-json-object-to-a-file)
50 |
51 |
52 | #### Streaming
53 | - [Node.js — Filter Data in Streams](https://futurestud.io/tutorials/node-js-filter-data-in-streams)
54 |
55 |
56 | #### Code Styling
57 | - [Callback and Promise Support in your Node.js Modules](https://futurestud.io/tutorials/callback-and-promise-support-in-your-node-js-modules)
58 |
59 |
60 | #### Error Handling
61 | - [Node.js — Create Your Own Custom Error](https://futurestud.io/tutorials/node-js-create-your-custom-error)
62 |
63 |
64 | #### Strings
65 | - [Node.js — Human-Readable JSON.stringify() With Spaces and Line Breaks](https://futurestud.io/tutorials/node-js-human-readable-json-stringify-with-spaces-and-line-breaks)
66 | - [Node.js — String Replace All Appearances](https://futurestud.io/tutorials/node-js-string-replace-all-appearances)
67 |
68 |
69 | #### Misc
70 | - [Increase the Memory Limit for Your Node.js Process](https://futurestud.io/tutorials/node-js-increase-the-memory-limit-for-your-process)
71 | - [Get Number of Seconds Since Epoch in JavaScript](https://futurestud.io/tutorials/get-number-of-seconds-since-epoch-in-javascript)
72 |
--------------------------------------------------------------------------------
/arrays/empty-an-array/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const array = [1, 2, 3, 4, 5]
4 | array.length = 0
5 | console.log(array.length)
6 |
7 | let array2 = [1, 2, 3, 4, 5]
8 | array2 = []
9 | console.log(array2.length)
10 |
--------------------------------------------------------------------------------
/arrays/merge-array-alternating-values/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const _ = require('lodash')
4 |
5 | const first = [1, 2, 3, 4]
6 | const second = ['A', 'B', 'C', 'D']
7 |
8 | const vanilla1 = Array.prototype.concat(
9 | ...first.map((item, index) => {
10 | return [item, second[index]]
11 | })
12 | )
13 | console.log(vanilla1)
14 |
15 | const vanilla2 =
16 | first
17 | .map((item, index) => {
18 | return [item, second[index]]
19 | })
20 | .reduce((result, pair) => {
21 | return result.concat(pair)
22 | })
23 | console.log(vanilla2)
24 |
25 | const lodash1 = _.flatten(_.zip(first, second))
26 | console.log(lodash1)
27 |
28 | const lodash2 =
29 | _
30 | .zip(first, second)
31 | .reduce((result, pair) => {
32 | return result.concat(pair)
33 | })
34 | console.log(lodash2)
35 |
--------------------------------------------------------------------------------
/axios/download-files-with-progress/images/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/axios/download-files-with-progress/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Fs = require('fs')
4 | const Path = require('path')
5 | const Axios = require('axios')
6 | const ProgressBar = require('progress')
7 |
8 | class DownloadWithProgressBar {
9 | constructor () {
10 | this.source = 'https://unsplash.com/photos/P6uF0I_okfk/download?force=true'
11 | this.destination = Path.resolve(`${__dirname}/images/hills.jpg`)
12 | }
13 |
14 | async start () {
15 | console.log('Connecting …')
16 | const { data, headers } = await this.startDownload()
17 |
18 | console.log('Starting download')
19 | const progressBar = this.initializeProgressBar({ totalLength: headers['content-length'] })
20 |
21 | return new Promise((resolve, reject) => {
22 | const file = this.toFile()
23 | file.on('error', reject)
24 | file.on('finish', resolve)
25 |
26 | data.pipe(file)
27 | data.on('data', (chunk) => progressBar.tick(chunk.length))
28 | })
29 | }
30 |
31 | async startDownload () {
32 | return Axios({
33 | method: 'GET',
34 | url: this.source,
35 | responseType: 'stream'
36 | })
37 | }
38 |
39 | initializeProgressBar ({ totalLength }) {
40 | return new ProgressBar('-> downloading [:bar] :percent :etas', {
41 | width: 40,
42 | complete: '=',
43 | incomplete: ' ',
44 | renderThrottle: 1,
45 | total: parseInt(totalLength)
46 | })
47 | }
48 |
49 | toFile () {
50 | return Fs.createWriteStream(this.destination)
51 | }
52 | }
53 |
54 | const download = new DownloadWithProgressBar()
55 | download.start()
56 |
--------------------------------------------------------------------------------
/axios/download-files/images/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/axios/download-files/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Fs = require('fs')
4 | const Path = require('path')
5 | const Listr = require('listr')
6 | const Axios = require('axios')
7 |
8 | /**
9 | * Start tasks to prepare or destroy data in MongoDB
10 | *
11 | * @param {Listr} tasks Listr instance with tasks
12 | * @return {void}
13 | */
14 | function kickoff (tasks) {
15 | tasks
16 | .run()
17 | .then(process.exit)
18 | .catch(process.exit)
19 | }
20 |
21 | /**
22 | * Entry point for the NPM "pumpitup" and "cleanup" scripts
23 | * Imports movie and TV show sample data to MongoDB
24 | */
25 | if (process.argv) {
26 | const tasks = [
27 | {
28 | title: 'Downloading images with axios',
29 | task: async () => {
30 | const url = 'https://unsplash.com/photos/AaEQmoufHLk/download?force=true'
31 | const path = Path.resolve(__dirname, 'images', 'code.jpg')
32 | const writer = Fs.createWriteStream(path)
33 |
34 | const response = await Axios({
35 | url,
36 | method: 'GET',
37 | responseType: 'stream'
38 | })
39 |
40 | response.data.pipe(writer)
41 |
42 | return new Promise((resolve, reject) => {
43 | writer.on('finish', resolve)
44 | writer.on('error', reject)
45 | })
46 | }
47 | }
48 | ]
49 |
50 | kickoff(new Listr(tasks))
51 | }
52 |
--------------------------------------------------------------------------------
/code-styling/01-callbacks.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | function grindCoffee (callback) {
4 | console.log('Beans grinded.')
5 | callback()
6 | }
7 |
8 | function brewCoffee (callback) {
9 | console.log('Coffee brewed.')
10 | callback()
11 | }
12 |
13 | function drinkCoffee (callback) {
14 | console.log('Work hard. Brew hard.')
15 | callback()
16 | }
17 |
18 | function breakfast () {
19 | grindCoffee(() => {
20 | brewCoffee(() => {
21 | drinkCoffee(() => {
22 | console.log('Fiery for the day!')
23 | })
24 | })
25 | })
26 | }
27 |
28 | breakfast()
29 |
--------------------------------------------------------------------------------
/code-styling/02-promises.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | function grindCoffee () {
4 | console.log('Beans grinded.')
5 | return Promise.resolve()
6 | }
7 |
8 | function brewCoffee () {
9 | console.log('Coffee brewed.')
10 | return Promise.resolve()
11 | }
12 |
13 | function drinkCoffee () {
14 | console.log('Work hard. Brew hard.')
15 | return Promise.resolve()
16 | }
17 |
18 | function breakfast () {
19 | grindCoffee()
20 | .then(() => brewCoffee())
21 | .then(() => drinkCoffee())
22 | .then(() => console.log('Fiery for the day!'))
23 | }
24 |
25 | breakfast()
26 |
--------------------------------------------------------------------------------
/code-styling/03-async-await.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | async function grindCoffee () {
4 | console.log('Beans grinded.')
5 | }
6 |
7 | async function brewCoffee () {
8 | console.log('Coffee brewed.')
9 | }
10 |
11 | async function drinkCoffee () {
12 | console.log('Work hard. Brew hard.')
13 | }
14 |
15 | async function breakfast () {
16 | // everything is top-level when using async/await
17 | // no nested callbacks to retrieve the results
18 |
19 | await grindCoffee()
20 | await brewCoffee()
21 | await drinkCoffee()
22 | console.log('Fiery for the day!')
23 | }
24 |
25 | breakfast()
26 |
--------------------------------------------------------------------------------
/code-styling/async-or-not-async/async-or-not-async.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class PostController {
4 | async create () {
5 | // even though Promise.resolve() returns a Promise,
6 | // the `async` keyword clearly tells you the return value (=Promise)
7 | console.log('Creating new post.')
8 |
9 | await new Promise(resolve => {
10 | setTimeout(() => {
11 | console.log('New post created.')
12 | resolve()
13 | }, 1000)
14 | })
15 |
16 | return Promise.resolve()
17 | }
18 | }
19 |
20 | const controller = new PostController()
21 | controller.create()
22 |
--------------------------------------------------------------------------------
/code-styling/await-sync-function/await-sync-function.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class PostController {
4 | async find () {
5 | const result = await this._filter()
6 | console.log(result)
7 | }
8 |
9 | _filter () {
10 | return [ 1, 2, 3, 4, 5 ].filter(id => id % 2 === 0)
11 | }
12 | }
13 |
14 | const controller = new PostController()
15 | controller.find()
16 |
--------------------------------------------------------------------------------
/code-styling/combine-callbacks-and-promises/base-command.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class Command {
4 | async run (callback) {
5 | try {
6 | await this.ensureInProjectRoot()
7 | await callback()
8 | } catch (error) {
9 | this.prettyPrintError(error)
10 | process.exit(1)
11 | }
12 | }
13 |
14 | async ensureInProjectRoot () {
15 | // ensure that the command execution was
16 | // initiated from the project’s root
17 | }
18 |
19 | async prettyPrintError (error) {
20 | // create a nice-looking error output
21 | console.error(error)
22 | }
23 | }
24 |
25 | module.exports = Command
26 |
--------------------------------------------------------------------------------
/code-styling/combine-callbacks-and-promises/deploy-command.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const BaseCommand = require('./base-command')
4 |
5 | class Deploy extends BaseCommand {
6 | async handle () {
7 | /**
8 | * Combine async/await with callbacks: the base command
9 | * implements a `run` method that accepts an async
10 | * callback as a parameter.
11 | *
12 | * This async callback method will then be processed
13 | * within `run`. Additionally, `run` implements
14 | * basic handling, like pretty error logging.
15 | */
16 | await this.run(async () => {
17 | await this._deploy()
18 | })
19 | }
20 |
21 | async _deploy () {
22 | // do the heavy lifting here
23 | }
24 | }
25 |
26 | module.exports = Deploy
27 |
--------------------------------------------------------------------------------
/collections/array-compact/array-compact.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Removes all falsey values from the given `array`.
5 | * Falsey values are `null`, `undefined`, `''`,
6 | * `false`, `0`, `NaN`.
7 | *
8 | * @param {Array} array
9 | *
10 | * @returns {Array}
11 | */
12 | function compact (array) {
13 | array = Array.isArray(array) ? array : [ array ]
14 |
15 | return array.filter(item => !!item)
16 | }
17 |
18 | function run () {
19 | const values = [ 0, null, undefined, 1, false, 2, '', 3, NaN ]
20 |
21 | console.log(
22 | compact(values) // output: [ 1, 2, 3 ]
23 | )
24 | }
25 |
26 | run()
27 |
--------------------------------------------------------------------------------
/collections/async-array-every/async-array-every.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Asynchronous version of `Array#every()` testing
5 | * whether all elements in the `array` pass the
6 | * test implemented by the `callback` function.
7 | *
8 | * @param {Array} array
9 | * @param {Function} callback
10 | *
11 | * @returns {Boolean}
12 | */
13 | async function every (array, callback) {
14 | array = Array.isArray(array) ? array : [ array ]
15 |
16 | const values = await Promise.all(array.map(callback))
17 |
18 | return values.every((value) => !!value)
19 | }
20 |
21 | /**
22 | * Helper function that waits for the given number of `ms`.
23 | *
24 | * @param {Integer} ms
25 | *
26 | * @returns {Integer}
27 | */
28 | async function wait (ms) {
29 | await new Promise(resolve => setTimeout(resolve, ms))
30 |
31 | return ms
32 | }
33 |
34 | async function run () {
35 | const timeouts = [10, 600, 200, 775, 125, 990]
36 |
37 | console.log('Is every waiting time greater than 500? ->')
38 | console.log(
39 | await every(timeouts, async timeout => {
40 | return await wait(timeout) > 500
41 | })
42 |
43 | )
44 |
45 | console.log('\nIs every waiting time greater than 5? ->')
46 | console.log(
47 | await every(timeouts, async timeout => {
48 | return await wait(timeout) > 5
49 | })
50 | )
51 | }
52 |
53 | run()
54 |
--------------------------------------------------------------------------------
/collections/async-array-filter/async-array-filter.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Asynchronously filter the given `array` using the provided `callback`.
5 | * At first, this function uses an async version of Array.map to run the
6 | * `callback` on every item. This returns an array of boolean values,
7 | * like `[ true, false, true ]`. The final filter results will be
8 | * calculated based on the boolean results and only those items
9 | * having a `true` result in the boolean array will survive.
10 | *
11 | *
12 | * @param {Array} array
13 | * @param {Function} callback
14 | *
15 | * @returns {Array}
16 | */
17 | async function filter (array, callback) {
18 | array = Array.isArray(array) ? array : [ array ]
19 |
20 | const values = await Promise.all(array.map(callback))
21 |
22 | return array.filter((_, index) => values[index])
23 | }
24 |
25 | /**
26 | * Helper function that waits for the given number of `ms`.
27 | *
28 | * @param {Integer} ms
29 | *
30 | * @returns {Integer}
31 | */
32 | async function wait (ms) {
33 | await new Promise(resolve => setTimeout(resolve, ms))
34 |
35 | return ms
36 | }
37 |
38 | async function run () {
39 | const timeouts = [10, 600, 200, 775, 125, 990]
40 |
41 | console.log(
42 | await filter(timeouts, async timeout => {
43 | return await wait(timeout) > 500
44 | })
45 | )
46 | }
47 |
48 | run()
49 |
--------------------------------------------------------------------------------
/collections/async-array-find/async-array-find.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Asynchronous version of Array#find(). Returns the first
5 | * item in the `array` that satisfies the `callback`
6 | * testing function, `undefined` otherwise.
7 | *
8 | * @param {Array} array
9 | * @param {Function} callback
10 | *
11 | * @returns {*} the found value
12 | */
13 | async function find (array, callback) {
14 | const mapped = await Promise.all(array.map(callback))
15 |
16 | return array.find((_, i) => mapped[i])
17 | }
18 |
19 | /**
20 | * Helper function that waits for the given number of `ms`.
21 | *
22 | * @param {Integer} ms
23 | *
24 | * @returns {Integer}
25 | */
26 | async function wait (ms) {
27 | await new Promise(resolve => setTimeout(resolve, ms))
28 |
29 | return ms
30 | }
31 |
32 | async function run () {
33 | const timeouts = [10, 600, 200, 775, 125, 990]
34 |
35 | console.log(
36 | await find(timeouts, async timeout => {
37 | return await wait(timeout) > 700
38 | })
39 | )
40 | }
41 |
42 | run()
43 |
--------------------------------------------------------------------------------
/collections/async-array-forEach/async-array-forEach.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Asynchrounous version of Array#forEach(). It runs the given
5 | * `callback` function on each `array` item. The callback
6 | * receives the current array item as a parameter.
7 | *
8 | * @param {Array} array
9 | * @param {Function} callback
10 | */
11 | async function forEach (array, callback) {
12 | array = Array.isArray(array) ? array : [ array ]
13 |
14 | await Promise.all(array.map(callback))
15 | }
16 |
17 | /**
18 | * Helper function that waits for the given number of `ms`.
19 | *
20 | * @param {Integer} ms
21 | *
22 | * @returns {Integer}
23 | */
24 | async function wait (ms) {
25 | await new Promise(resolve => setTimeout(resolve, ms))
26 |
27 | return ms
28 | }
29 |
30 | async function run () {
31 | const timeouts = [10, 600, 200, 775, 125, 990]
32 |
33 | console.log('Fetching data ->')
34 | await forEach(timeouts, async timeout => {
35 | console.log(
36 | `took ${await wait(timeout)} ms`
37 | )
38 | })
39 | }
40 |
41 | run()
42 |
--------------------------------------------------------------------------------
/collections/async-array-map/async-array-map.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Axios = require('axios')
4 |
5 | async function githubDetailsFor (repo) {
6 | const response = await Axios({
7 | method: 'GET',
8 | url: repo.url,
9 | headers: { Accept: 'application/vnd.github.v3+json' }
10 | })
11 |
12 | return Object.assign(repo, {
13 | name: response.data.full_name,
14 | description: response.data.description
15 | })
16 | }
17 |
18 | async function run () {
19 | const repos = [
20 | { url: 'https://api.github.com/repos/futurestudio/futureflix-starter-kit' },
21 | { url: 'https://api.github.com/repos/futurestudio/android-tutorials-glide' }
22 | ]
23 |
24 | const promises = repos.map(async (repo) => {
25 | return githubDetailsFor(repo)
26 | })
27 |
28 | console.log(
29 | await Promise.all(promises)
30 | )
31 | }
32 |
33 | run()
34 |
--------------------------------------------------------------------------------
/collections/async-array-mapSeries/async-array-mapSeries.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Axios = require('axios')
4 |
5 | /**
6 | * Asynchronous version of Array#map(), running all transformations
7 | * in sequence. It runs the given `callback` on each item of
8 | * the `array` and returns an array of transformed items.
9 | *
10 | * @param {Array} array
11 | * @param {Function} callback
12 | *
13 | * @returns {Array} resulting array of transformed items
14 | */
15 | async function mapSeries (array, callback) {
16 | const results = []
17 |
18 | for (const index in array) {
19 | results.push(
20 | await callback(array[index], index, array)
21 | )
22 | }
23 |
24 | return results
25 | }
26 |
27 | async function githubDetailsFor (repo) {
28 | const response = await Axios({
29 | method: 'GET',
30 | url: repo.url,
31 | headers: { Accept: 'application/vnd.github.v3+json' }
32 | })
33 |
34 | return Object.assign(repo, {
35 | name: response.data.full_name,
36 | description: response.data.description
37 | })
38 | }
39 |
40 | async function run () {
41 | const repos = [
42 | { url: 'https://api.github.com/repos/superchargejs/supercharge' },
43 | { url: 'https://api.github.com/repos/superchargejs/collections' },
44 | { url: 'https://api.github.com/repos/futurestudio/nodejs-tutorials' },
45 | { url: 'https://api.github.com/repos/futurestudio/futureflix-starter-kit' }
46 | ]
47 |
48 | console.log(
49 | await mapSeries(repos, async (repo, index, array) => {
50 | return githubDetailsFor(repo)
51 | })
52 | )
53 | }
54 |
55 | run()
56 |
--------------------------------------------------------------------------------
/collections/async-array-some/async-array-some.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Asynchronous version of `Array#some()`. The function
5 | * tests whether at least one element in the `array`
6 | * passes the check implemented by the `callback`.
7 | *
8 | * @param {Array} array
9 | * @param {Function} callback
10 | *
11 | * @returns {Boolean}
12 | */
13 | async function some (array, callback) {
14 | array = Array.isArray(array) ? array : [ array ]
15 |
16 | const values = await Promise.all(array.map(callback))
17 |
18 | return values.some(value => !!value)
19 | }
20 |
21 | /**
22 | * Helper function that waits for the given number of `ms`.
23 | *
24 | * @param {Integer} ms
25 | *
26 | * @returns {Integer}
27 | */
28 | async function wait (ms) {
29 | await new Promise(resolve => setTimeout(resolve, ms))
30 |
31 | return ms
32 | }
33 |
34 | async function run () {
35 | const timeouts = [10, 600, 200, 775, 125, 990]
36 |
37 | console.log('Are some waiting times greater than 500? ->')
38 | console.log(
39 | await some(timeouts, async timeout => {
40 | return await wait(timeout) > 500
41 | })
42 |
43 | )
44 |
45 | console.log('\nAre some waiting times lower then 10? ->')
46 | console.log(
47 | await some(timeouts, async timeout => {
48 | return await wait(timeout) < 10
49 | })
50 | )
51 | }
52 |
53 | run()
54 |
--------------------------------------------------------------------------------
/data-structures/implementations/linked-list/linked-list.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Node = require('./node')
4 |
5 | class LinkedList {
6 | constructor () {
7 | this._head = null
8 | }
9 |
10 | addToHead (value) {
11 | if (this.isEmpty()) {
12 | this._head = new Node(value)
13 | return this
14 | }
15 |
16 | this._head = new Node(value, this._head)
17 |
18 | return this
19 | }
20 |
21 | addBefore (value, newValue) {
22 | if (!this.has(value)) {
23 | throw new Error(`Cannot insert ${newValue}: cannot find ${value}`)
24 | }
25 |
26 | let prevNode = null
27 | let current = this._head
28 |
29 | while (current) {
30 | if (current.getValue() === value) {
31 | if (prevNode) {
32 | prevNode.setNext(new Node(newValue, current))
33 | } else {
34 | this.addToHead(newValue)
35 | }
36 |
37 | break
38 | }
39 |
40 | prevNode = current
41 | current = current.getNext()
42 | }
43 |
44 | return this
45 | }
46 |
47 | addAfter (value, newValue) {
48 | if (!this.has(value)) {
49 | throw new Error(`Cannot insert ${newValue}: cannot find ${value}`)
50 | }
51 |
52 | let current = this._head
53 |
54 | while (current) {
55 | if (current.getValue() === value) {
56 | current.setNext(new Node(newValue, current.getNext()))
57 | break
58 | }
59 |
60 | current = current.getNext()
61 | }
62 |
63 | return this
64 | }
65 |
66 | addToTail (value) {
67 | if (this.isEmpty()) {
68 | this._head = new Node(value)
69 | return this
70 | }
71 |
72 | let current = this._head
73 |
74 | while (current.hasNext()) {
75 | current = current.getNext()
76 | }
77 |
78 | current.setNext(new Node(value))
79 |
80 | return this
81 | }
82 |
83 | removeHead () {
84 | if (this.isEmpty()) {
85 | return this
86 | }
87 |
88 | if (this._head.hasNext()) {
89 | this._head = this._head.getNext()
90 |
91 | return this
92 | }
93 |
94 | this._head = null
95 |
96 | return this
97 | }
98 |
99 | removeTail () {
100 | if (this.isEmpty()) {
101 | return this
102 | }
103 |
104 | let preLast = null
105 | let current = this._head
106 |
107 | while (current.hasNext()) {
108 | preLast = current
109 | current = current.getNext()
110 | }
111 |
112 | if (preLast) {
113 | preLast.setNext(null)
114 | return this
115 | }
116 |
117 | this.removeHead()
118 |
119 | return this
120 | }
121 |
122 | remove (value) {
123 | if (this.isEmpty()) {
124 | return this
125 | }
126 |
127 | if (!this.has(value)) {
128 | throw new Error(`Cannot find ${value} and therefore not remove it`)
129 | }
130 |
131 | let prevNode = null
132 | let current = this._head
133 |
134 | while (current) {
135 | if (current.getValue() === value) {
136 | break
137 | }
138 |
139 | prevNode = current
140 | current = current.getNext()
141 | }
142 |
143 | if (prevNode) {
144 | prevNode.setNext(current.getNext())
145 | return this
146 | }
147 |
148 | this.removeHead()
149 |
150 | return this
151 | }
152 |
153 | has (value) {
154 | let current = this._head
155 |
156 | while (current) {
157 | if (current.getValue() === value) {
158 | return true
159 | }
160 |
161 | current = current.getNext()
162 | }
163 |
164 | return false
165 | }
166 |
167 | traverse (callback) {
168 | let current = this._head
169 |
170 | while (current) {
171 | callback(current.getValue())
172 | current = current.getNext()
173 | }
174 | }
175 |
176 | toArray () {
177 | let result = []
178 | this.traverse(value => result.push(value))
179 |
180 | return result
181 | }
182 |
183 | size () {
184 | let size = 0
185 |
186 | this.traverse(() => {
187 | size += 1
188 | })
189 |
190 | return size
191 | }
192 |
193 | isEmpty () {
194 | return this.size() === 0
195 | }
196 |
197 | clear () {
198 | this._head = null
199 | }
200 | }
201 |
202 | module.exports = LinkedList
203 |
--------------------------------------------------------------------------------
/data-structures/implementations/linked-list/node.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class Node {
4 | constructor (value, next) {
5 | this.value = value
6 | this.next = next
7 | }
8 |
9 | getValue () {
10 | return this.value
11 | }
12 |
13 | getNext () {
14 | return this.next
15 | }
16 |
17 | setNext (next) {
18 | this.next = next
19 | }
20 |
21 | hasNext () {
22 | return !!this.next
23 | }
24 | }
25 |
26 | module.exports = Node
27 |
--------------------------------------------------------------------------------
/data-structures/implementations/priority-queue/priority-queue.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class PriorityQueue {
4 | /**
5 | * Creates a new PriorityQueue instance.
6 | *
7 | * @param {Mixed} items
8 | */
9 | constructor () {
10 | this._queue = []
11 | }
12 |
13 | /**
14 | * Pushes a new `item` with the related
15 | * `priority` into the queue.
16 | *
17 | * @param {Mixed} items
18 | */
19 | enqueue (item, priority) {
20 | if (Number.isNaN(+priority) || priority < 1) {
21 | throw new Error('Failed to enqueue item: the priority must be a positive number')
22 | }
23 |
24 | this._queue.push({ priority, item })
25 |
26 | this._queue.sort((a, b) => {
27 | return a.priority - b.priority
28 | })
29 |
30 | return this
31 | }
32 |
33 | /**
34 | * Removes and returns the item which is up
35 | * for processing. Returns `undefined`
36 | * if the queue is empty.
37 | *
38 | * @returns {Mixed}
39 | */
40 | dequeue () {
41 | if (this.isEmpty()) {
42 | return
43 | }
44 |
45 | const { item } = this._queue.shift()
46 |
47 | return item
48 | }
49 |
50 | /**
51 | * Returns the front item without removing it
52 | * from the queue. Returns `undefined` if
53 | * the queue is empty.
54 | *
55 | * @returns {Mixed}
56 | */
57 | peek () {
58 | return this._queue[0] && this._queue[0].item
59 | }
60 |
61 | /**
62 | * Returns the number of items in the queue.
63 | *
64 | * @returns {Integer}
65 | */
66 | size () {
67 | return this._queue.length
68 | }
69 |
70 | /**
71 | * Returns `true` if there are no items in
72 | * the queue, `false` otherwise.
73 | *
74 | * @returns {Boolean}
75 | */
76 | isEmpty () {
77 | return this.size() === 0
78 | }
79 |
80 | /**
81 | * Removes all items from the queue.
82 | */
83 | clear () {
84 | this._queue = []
85 | }
86 | }
87 |
88 | module.exports = PriorityQueue
89 |
--------------------------------------------------------------------------------
/data-structures/implementations/queue/queue.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class Queue {
4 | /**
5 | * Creates a new Queue instance and
6 | * enqueues the given `items`.
7 | *
8 | * @param {Mixed} items
9 | */
10 | constructor (...items) {
11 | this._queue = [...items]
12 | }
13 |
14 | /**
15 | * Pushes new `items` into the queue.
16 | *
17 | * @param {Mixed} items
18 | */
19 | enqueue (...items) {
20 | this._queue.push(...items)
21 | }
22 |
23 | /**
24 | * Removes and returns the item which is up
25 | * for processing. Returns `undefined`
26 | * if the queue is empty.
27 | *
28 | * @returns {Mixed}
29 | */
30 | dequeue () {
31 | return this._queue.shift()
32 | }
33 |
34 | /**
35 | * Returns the front item without removing it
36 | * from the queue. Returns `undefined` if
37 | * the queue is empty.
38 | *
39 | * @returns {Mixed}
40 | */
41 | peek () {
42 | return this._queue[0]
43 | }
44 |
45 | /**
46 | * Returns the number of items in the queue.
47 | *
48 | * @returns {Integer}
49 | */
50 | size () {
51 | return this._queue.length
52 | }
53 |
54 | /**
55 | * Returns `true` if there are no items in
56 | * the queue, `false` otherwise.
57 | *
58 | * @returns {Boolean}
59 | */
60 | isEmpty () {
61 | return this.size() === 0
62 | }
63 |
64 | /**
65 | * Removes all items from the queue.
66 | */
67 | clear () {
68 | this._queue = []
69 | }
70 | }
71 |
72 | module.exports = Queue
73 |
--------------------------------------------------------------------------------
/data-structures/implementations/set/set.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class Set {
4 | /**
5 | * Creates a new Set instance and
6 | * adds the given `items`.
7 | *
8 | * @param {Mixed} items
9 | */
10 |
11 | constructor (...items) {
12 | this._set = {}
13 | this._size = 0
14 |
15 | this.add(...items)
16 | }
17 |
18 | /**
19 | * Adds new `items` into the set.
20 | *
21 | * @param {Mixed} items
22 | */
23 | add (...items) {
24 | items.forEach(item => {
25 | if (!this.has(item)) {
26 | this._set[item] = item
27 | this._size += 1
28 | }
29 | })
30 | }
31 |
32 | /**
33 | * Removes the given `item` from the set.
34 | */
35 | remove (item) {
36 | if (this.has(item)) {
37 | this._set[item] = false
38 | this._size -= 1
39 | }
40 | }
41 |
42 | /**
43 | * Returns `true` if the given `item` is
44 | * contained in the set, `false`
45 | * otherwise.
46 | *
47 | * @param {Mixed} item
48 | *
49 | * @returns {Boolean}
50 | */
51 | has (item) {
52 | return !!this._set[item]
53 | }
54 |
55 | /**
56 | * Returns an array containing all the values
57 | * contained in the set.
58 | *
59 | * @returns {Array}
60 | */
61 | toArray () {
62 | return Object.values(this._set).map((el) => {
63 | return Number.isNaN(+el) ? el : +el
64 | })
65 | }
66 |
67 | /**
68 | * Returns the number of items in the set.
69 | *
70 | * @returns {Integer}
71 | */
72 | size () {
73 | return this._size
74 | }
75 |
76 | /**
77 | * Returns `true` if there are no items in
78 | * the set, `false` otherwise.
79 | *
80 | * @returns {Boolean}
81 | */
82 | isEmpty () {
83 | return this.size() === 0
84 | }
85 |
86 | /**
87 | * Removes all items from the set.
88 | */
89 | clear () {
90 | this._set = {}
91 | this._size = 0
92 | }
93 | }
94 |
95 | module.exports = Set
96 |
--------------------------------------------------------------------------------
/data-structures/implementations/stack/stack.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class Stack {
4 | /**
5 | * Creates a new Stack instance and
6 | * pushes the given `items` onto
7 | * the stack.
8 | *
9 | * @param {Mixed} items
10 | */
11 | constructor (...items) {
12 | this._stack = [...items.reverse()]
13 | }
14 |
15 | /**
16 | * Push new `items` onto the stack.
17 | *
18 | * @param {Mixed} items
19 | */
20 | push (...items) {
21 | this._stack.unshift(...items.reverse())
22 | }
23 |
24 | /**
25 | * Removes and returns the most-recently added
26 | * item from the stack. Returns `undefined`
27 | * if the stack is empty.
28 | *
29 | * @returns {Mixed}
30 | */
31 | pop () {
32 | return this._stack.shift()
33 | }
34 |
35 | /**
36 | * Returns the most-recently added item from
37 | * the stack. This method won’t remove the
38 | * returned item. Returns `undefined`
39 | * if the stack is empty.
40 | *
41 | * @returns {Mixed}
42 | */
43 | peek () {
44 | return this._stack[0]
45 | }
46 |
47 | /**
48 | * Returns the number of items on the stack.
49 | *
50 | * @returns {Integer}
51 | */
52 | size () {
53 | return this._stack.length
54 | }
55 |
56 | /**
57 | * Returns `true` if there are no items on
58 | * the stack, `false` otherwise.
59 | *
60 | * @returns {Boolean}
61 | */
62 | isEmpty () {
63 | return this.size() === 0
64 | }
65 |
66 | /**
67 | * Removes all items from the stack.
68 | */
69 | clear () {
70 | this._stack = []
71 | }
72 | }
73 |
74 | module.exports = Stack
75 |
--------------------------------------------------------------------------------
/data-structures/starter-files/linked-list.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class LinkedList {
4 | constructor () {
5 | this._head = null
6 | }
7 |
8 | addToHead (value) {}
9 | addBefore (value, newValue) {}
10 | addAfter (value, newValue) {}
11 | addToTail (value) {}
12 | removeHead () {}
13 | removeTail () {}
14 | remove (value) {}
15 | has (value) {}
16 | toArray () {}
17 | size () {}
18 | isEmpty () {}
19 | clear () {}
20 | }
21 |
22 | module.exports = LinkedList
23 |
--------------------------------------------------------------------------------
/data-structures/starter-files/priority-queue.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class PriorityQueue {
4 | constructor () {
5 | this._queue = []
6 | }
7 |
8 | enqueue (item, priority) {}
9 | dequeue () {}
10 | peek () {}
11 | size () {}
12 | isEmpty () {}
13 | clear () {}
14 | }
15 |
16 | module.exports = PriorityQueue
17 |
--------------------------------------------------------------------------------
/data-structures/starter-files/queue.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class Queue {
4 | constructor (...items) {
5 | this._queue = []
6 | }
7 |
8 | enqueue (...items) {}
9 | dequeue () {}
10 | peek () {}
11 | size () {}
12 | isEmpty () {}
13 | clear () {}
14 | }
15 |
16 | module.exports = Queue
17 |
--------------------------------------------------------------------------------
/data-structures/starter-files/set.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class Set {
4 | constructor (...items) {
5 | this._set = {}
6 | }
7 |
8 | add (...items) {}
9 | remove (item) {}
10 | has (item) {}
11 | toArray () {}
12 | size () {}
13 | isEmpty () {}
14 | clear () {}
15 | }
16 |
17 | module.exports = Set
18 |
--------------------------------------------------------------------------------
/data-structures/starter-files/stack.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class Stack {
4 | constructor (...items) {
5 | this._stack = []
6 | }
7 |
8 | push (...items) {}
9 | pop () {}
10 | peek () {}
11 | size () {}
12 | isEmpty () {}
13 | clear () {}
14 | }
15 |
16 | module.exports = Stack
17 |
--------------------------------------------------------------------------------
/date/increase-date-by-one-week.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const today = new Date()
4 | const nextWeek = new Date()
5 |
6 | // add 7 days to the current date
7 | nextWeek.setDate(today.getDate() + 7)
8 |
9 | console.log(nextWeek)
10 |
--------------------------------------------------------------------------------
/date/tomorrows-date.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const today = new Date()
4 | const tomorrow = new Date()
5 |
6 | // add 1 day to today
7 | tomorrow.setDate(today.getDate() + 1)
8 |
9 | console.log(tomorrow)
10 |
--------------------------------------------------------------------------------
/error-handling/unhandled-rejection.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Youch = require('youch')
4 | const forTerminal = require('youch-terminal')
5 |
6 | process.on('unhandledRejection', async (error) => {
7 | const output = await new Youch(error).toJSON()
8 | console.log(forTerminal(output))
9 | process.exit(1)
10 | })
11 |
12 | async function start () {
13 | return Promise.reject(
14 | new Error('Missing coffee exception')
15 | )
16 | }
17 |
18 | start()
19 |
--------------------------------------------------------------------------------
/events/async-event-listener/event.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const EventEmitter = require('events')
4 |
5 | class Clock extends EventEmitter {
6 | constructor () {
7 | super()
8 |
9 | this.timer = null
10 | this.ticks = null
11 | }
12 |
13 | start () {
14 | if (!this.timer) {
15 | this.timer = setInterval(() => this.tick(), 1000)
16 | }
17 |
18 | this.emit('start', { startsAt: new Date() })
19 | }
20 |
21 | stop () {
22 | clearInterval(this.timer)
23 | this.timer = null
24 | this.emit('stop', { endsAt: new Date() })
25 | }
26 |
27 | tick () {
28 | this.ticks++
29 | this.emit('tick', { now: new Date(), tick: this.ticks })
30 |
31 | if (this.ticks > 9) {
32 | this.stop()
33 | }
34 | }
35 | }
36 |
37 | module.exports = Clock
38 |
--------------------------------------------------------------------------------
/events/async-event-listener/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Event = require('./event')
4 | const Listener = require('./listener')
5 |
6 | const clock = new Event()
7 | const handler = new Listener()
8 |
9 | clock.on('start', async (...args) => {
10 | await handler.onStart(...args)
11 | })
12 |
13 | clock.on('stop', async (...args) => {
14 | await handler.onStop(...args)
15 | })
16 |
17 | clock.on('tick', async (...args) => {
18 | await handler.onTick(...args)
19 | })
20 |
21 | clock.start()
22 |
--------------------------------------------------------------------------------
/events/async-event-listener/listener.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class Listener {
4 | onStart () {
5 | console.log(`Opening DevDays Pizza House\n`)
6 | }
7 |
8 | onStop () {
9 | console.log(`\nDevDays Pizza is going home\n`)
10 | }
11 |
12 | async onTick ({ tick }) {
13 | const bakingTime = this._randomTimeout()
14 | console.log(`📥 #${tick} received -> delivery takes ${bakingTime} s`)
15 |
16 | await this._workHardBakeHard(bakingTime)
17 |
18 | console.log(`🍕 ${tick} delivered -> took just: ${bakingTime} s`)
19 | }
20 |
21 | async _workHardBakeHard (seconds) {
22 | await new Promise(resolve => {
23 | setTimeout(resolve, seconds * 1000)
24 | })
25 | }
26 |
27 | _randomTimeout () {
28 | return Math.floor(Math.random() * 20)
29 | }
30 | }
31 |
32 | module.exports = Listener
33 |
--------------------------------------------------------------------------------
/filesystem/create-an-empty-file/.gitignore:
--------------------------------------------------------------------------------
1 | touched.sync
2 | async.touch
3 |
--------------------------------------------------------------------------------
/filesystem/create-an-empty-file/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Fs = require('fs-extra')
4 |
5 | run()
6 |
7 | async function run () {
8 | touchSync('./touched.sync')
9 | console.log('created "touched.sync"')
10 |
11 | await touch('./async.touch')
12 | console.log('created "async.touch"')
13 | }
14 |
15 | /**
16 | * Creates an empty file at `path`. This method does not ensure to create
17 | * missing directories along the path. It won’t override an existing
18 | * file or modify the content. Use the "w" flag to override an
19 | * existing file and its content.
20 | *
21 | * @param {String} path
22 | *
23 | */
24 | function touchSync (path) {
25 | Fs.closeSync(Fs.openSync(path, 'a'))
26 | }
27 |
28 | /**
29 | * Ensures that the file at `path` exists. It will also create
30 | * missing directories along the path. If a file at `path`
31 | * already exists, it won’t modify the content.
32 | *
33 | * @param {String} path
34 | */
35 | async function touch (path) {
36 | await Fs.ensureFile(path)
37 | }
38 |
--------------------------------------------------------------------------------
/filesystem/file-created-date/content.txt:
--------------------------------------------------------------------------------
1 | example file for "file-created-date"
2 |
--------------------------------------------------------------------------------
/filesystem/file-created-date/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Fs = require('fs')
4 | const Path = require('path')
5 |
6 | function createdDate (file) {
7 | const { birthtime } = Fs.statSync(file)
8 |
9 | return birthtime
10 | }
11 |
12 | const file = Path.resolve(__dirname, 'content.txt')
13 |
14 | console.log(
15 | createdDate(file)
16 | )
17 |
--------------------------------------------------------------------------------
/filesystem/file-last-updated-date/content.txt:
--------------------------------------------------------------------------------
1 | example file for "file-last-updated-date"
2 |
--------------------------------------------------------------------------------
/filesystem/file-last-updated-date/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Fs = require('fs')
4 | const Path = require('path')
5 |
6 | function lastUpdatedDate (file) {
7 | const { ctime, mtime } = Fs.statSync(file)
8 | console.log(`File data last modified: ${mtime}`)
9 | console.log(`File status last modified: ${ctime}`)
10 |
11 | return mtime
12 | }
13 |
14 | const file = Path.resolve(__dirname, 'content.txt')
15 |
16 | lastUpdatedDate(file)
17 |
--------------------------------------------------------------------------------
/filesystem/write-json-object-to-file/content.txt:
--------------------------------------------------------------------------------
1 | {
2 | "name": "promisified Marcus"
3 | }
--------------------------------------------------------------------------------
/filesystem/write-json-object-to-file/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Fs = require('fs')
4 | const Path = require('path')
5 |
6 | const filePath = Path.resolve(__dirname, 'content.txt')
7 |
8 | function writeToFile (path, data) {
9 | const json = JSON.stringify(data, null, 2)
10 |
11 | Fs.writeFile(path, json, (err) => {
12 | if (err) {
13 | console.error(err)
14 | throw err
15 | }
16 |
17 | console.log('Saved data to file.')
18 | })
19 | }
20 |
21 | function readFromFile (path) {
22 | Fs.readFile(path, 'utf8', (err, json) => {
23 | if (err) {
24 | console.error(err)
25 | throw err
26 | }
27 |
28 | const data = JSON.parse(json)
29 | console.log(data)
30 | })
31 | }
32 |
33 | writeToFile(filePath, { name: 'Marcus' })
34 | readFromFile(filePath)
35 |
--------------------------------------------------------------------------------
/filesystem/write-json-object-to-file/promisified.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Path = require('path')
4 | const Fs = require('fs-extra')
5 |
6 | const filePath = Path.resolve(__dirname, 'content.txt')
7 |
8 | async function writeToFile (path, data) {
9 | const json = JSON.stringify(data, null, 2)
10 |
11 | try {
12 | await Fs.writeFile(path, json)
13 | console.log('Saved data to file.')
14 | } catch (error) {
15 | console.error(error)
16 | }
17 | }
18 |
19 | async function dataFromFile (path) {
20 | try {
21 | const json = await Fs.readFile(path, 'utf8')
22 | const content = JSON.parse(json)
23 | console.log(content)
24 | } catch (error) {
25 | console.log(error)
26 | }
27 | }
28 |
29 | writeToFile(filePath, { name: 'promisified Marcus' })
30 | dataFromFile(filePath)
31 |
--------------------------------------------------------------------------------
/flow-control/async-constructors/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const SampleDriver = require('./sample-cache-driver')
4 |
5 | class Cache {
6 | constructor (options) {
7 | this.options = options
8 | this.driver = new SampleDriver()
9 |
10 | /**
11 | * Node.js doesn’t support async constructors.
12 | * You can’t await driver.start() here.
13 | * Instead, use an async method to run the initialization.
14 | */
15 | }
16 |
17 | async init () {
18 | await this.driver.start()
19 | }
20 |
21 | static async init (options) {
22 | return new this(options).init()
23 |
24 | // the caller can do something like this:
25 | // const cache = await Cache.init({ options })
26 | // or
27 | // const cache = await new Cache({options}).init()
28 | }
29 | }
30 |
31 | module.exports = Cache
32 |
--------------------------------------------------------------------------------
/flow-control/async-constructors/sample-cache-driver.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class CacheDriver {
4 | constructor (options) {
5 | this.options = options
6 | }
7 |
8 | async start () {}
9 | async stop () {}
10 |
11 | async get () {}
12 | async forget () {}
13 | async remember () {}
14 | }
15 |
16 | module.exports = CacheDriver
17 |
--------------------------------------------------------------------------------
/flow-control/promise-and-callback/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const packageInfo = require('./promise-and-callback.js').packageInfo
4 | const packageInfoPromisified = require('./promise-and-callback.js').packageInfoPromisified
5 |
6 | packageInfo()
7 | .then(data => {
8 | console.log('promise finished')
9 | console.log(data.toString())
10 | })
11 | .catch(err => {
12 | console.log(err.message)
13 | })
14 |
15 | packageInfo((err, data) => {
16 | console.log('callback available')
17 | if (err) {
18 | throw err
19 | }
20 |
21 | if (data) console.log(data.toString())
22 | })
23 |
24 | packageInfoPromisified()
25 | .then(data => {
26 | console.log('promisified version of package info read finished')
27 | console.log(data.toString())
28 | })
29 | .catch(err => {
30 | console.log(err.message)
31 | })
32 |
--------------------------------------------------------------------------------
/flow-control/promise-and-callback/promise-and-callback.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Fs = require('fs')
4 | const Path = require('path')
5 | const Util = require('util')
6 | const packagePath = Path.resolve(__dirname, '..', '..', 'package.json')
7 |
8 | exports.packageInfo = function (callback) {
9 | return new Promise((resolve, reject) => {
10 | Fs.readFile(packagePath, (err, data) => {
11 | if (err) {
12 | // if no callback available, reject the promise
13 | // else, return callback using "error-first-pattern"
14 | return callback ? callback(err) : reject(err)
15 | }
16 |
17 | return callback ? callback(null, data) : resolve(data)
18 | })
19 | })
20 | }
21 |
22 | exports.packageInfoPromisified = function () {
23 | const readFilePromise = Util.promisify(Fs.readFile)
24 | return readFilePromise(packagePath)
25 | }
26 |
--------------------------------------------------------------------------------
/flow-control/promises-in-parallel/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Hoek = require('@hapi/hoek')
4 |
5 | run()
6 |
7 | async function run () {
8 | const timeouts = [10, 600, 200, 775, 125, 990]
9 |
10 | const forResult = await forLoopInParallel(timeouts)
11 | console.log()
12 |
13 | const allResult = await awaitAll(timeouts)
14 | console.log()
15 |
16 | console.log('YES! All done. Here are the results')
17 | console.log(forResult)
18 | console.log(allResult)
19 | }
20 |
21 | async function forLoopInParallel (timeouts) {
22 | const promises = timeouts.map(timeout => asyncProcessing(timeout))
23 | const result = []
24 |
25 | for (const timeoutPromise of promises) {
26 | result.push(await timeoutPromise)
27 | }
28 |
29 | return result
30 | }
31 |
32 | async function awaitAll (timeouts) {
33 | const promises = timeouts.map(timeout => asyncProcessing(timeout))
34 | const result = await Promise.all(promises)
35 |
36 | return result
37 | }
38 |
39 | async function asyncProcessing (ms) {
40 | await Hoek.wait(ms)
41 | console.log(`waited: ${ms}ms`)
42 |
43 | return ms
44 | }
45 |
--------------------------------------------------------------------------------
/flow-control/promises-in-parallel/with-errors.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Hoek = require('@hapi/hoek')
4 |
5 | run()
6 |
7 | async function run () {
8 | const timeouts = [10, 600, 200, 775, 125, 990]
9 | const promises = timeouts.map(timeout => asyncProcessing(timeout))
10 |
11 | try {
12 | const result = await Promise.all(
13 | promises.concat([ throwUp() ])
14 | )
15 |
16 | /**
17 | * The control flow is not what you expect if one of the promises
18 | * within Promise.all rejects. If at least a single promise
19 | * rejects, you won't see the result logged to the console.
20 | */
21 | console.log('Here is the result:')
22 | console.log(result)
23 | } catch (error) {
24 | console.log('WAT WAT WAT ?! Here’s an error. Holy moly!!')
25 |
26 | console.log(error)
27 | }
28 |
29 | console.log()
30 | console.log('YES! All done.')
31 | }
32 |
33 | async function throwUp () {
34 | throw new Error('throwing up')
35 | }
36 |
37 | async function asyncProcessing (timeout) {
38 | await Hoek.wait(timeout)
39 | console.log(`waited: ${timeout}ms`)
40 | }
41 |
--------------------------------------------------------------------------------
/flow-control/promises-in-sequence/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Hoek = require('@hapi/hoek')
4 |
5 | run()
6 |
7 | async function run () {
8 | const timeouts = [10, 600, 200, 775, 125, 990]
9 |
10 | const forResult = await forLoopInSequence(timeouts)
11 | console.log()
12 |
13 | const reduceResult = await reduceInSequence(timeouts)
14 | console.log()
15 |
16 | console.log('YES! All done. Here are the results:')
17 | console.log(forResult)
18 | console.log(reduceResult)
19 | }
20 |
21 | async function forLoopInSequence (timeouts) {
22 | let result = []
23 |
24 | for (const timeout of timeouts) {
25 | result.push(await asyncProcessing(timeout))
26 | }
27 |
28 | return result
29 | }
30 |
31 | async function reduceInSequence (timeouts) {
32 | return timeouts.reduce(async (carry, timeout) => {
33 | return []
34 | .concat(await carry)
35 | .concat(await asyncProcessing(timeout))
36 | }, [])
37 | }
38 |
39 | async function asyncProcessing (ms) {
40 | await Hoek.wait(ms)
41 | console.log(`waited: ${ms}`)
42 |
43 | return ms
44 | }
45 |
--------------------------------------------------------------------------------
/flow-control/promises-in-sequence/with-errors.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Hoek = require('@hapi/hoek')
4 |
5 | run()
6 |
7 | async function run () {
8 | const timeouts = [10, 600, 200, 775, throwUp, 125, 990]
9 | const result = await reduceInSequence(timeouts)
10 |
11 | console.log()
12 | console.log('YES! All done. Here is the result:')
13 | console.log(result)
14 | }
15 |
16 | async function throwUp () {
17 | throw new Error('throwing up')
18 | }
19 |
20 | async function reduceInSequence (timeouts) {
21 | return timeouts.reduce(async (carry, timeout) => {
22 | return [
23 | ...(await carry),
24 | await asyncProcessing(timeout)
25 | ]
26 | }, Promise.resolve([]))
27 | }
28 |
29 | async function asyncProcessing (ms) {
30 | if (typeof ms === 'function') {
31 | return ms()
32 | }
33 |
34 | await Hoek.wait(ms)
35 | console.log(`finished: ${ms}ms`)
36 | }
37 |
--------------------------------------------------------------------------------
/flow-control/promises-waterfall/.gitignore:
--------------------------------------------------------------------------------
1 | data.json
2 |
--------------------------------------------------------------------------------
/flow-control/promises-waterfall/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Path = require('path')
4 | const Fs = require('fs-extra')
5 |
6 | saveIt()
7 |
8 | async function saveIt () {
9 | const content = await downloadContent()
10 | const file = await saveToFile(content)
11 | const { size } = await Fs.stat(file)
12 |
13 | console.log(`File content is HUGE: ${(size / 1000).toFixed(2)} KB`)
14 | }
15 | async function downloadContent () {
16 | return JSON.stringify([
17 | { id: 1, description: 'huge JSON content' },
18 | { id: 2, description: 'more more more!' },
19 | { id: 3, description: 'you rock buddy!' }
20 | ])
21 | }
22 |
23 | async function saveToFile (content) {
24 | const file = Path.resolve(__dirname, 'data.json')
25 | await Fs.writeFile(file, content)
26 |
27 | return file
28 | }
29 |
--------------------------------------------------------------------------------
/loops/for-of-vs-for-in/for-of-vs-for-in.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const types = [ 'object', 'array', 'string', 'integer', 'float', 'boolean' ]
4 |
5 | /**
6 | * Use `for … of` to iterate through the values of an iterable.
7 | * Use for-of on arrays to access the values. Notice that an
8 | * object is not an iterable, but a Map is.
9 | */
10 | for (const type of types) {
11 | console.log(`A JavaScript type is: ${type}`)
12 | }
13 |
14 | const cache = new Map()
15 | cache.set('posts:1', { id: 1, title: 'Post 1' })
16 | cache.set('posts:2', { id: 2, title: 'Post 2' })
17 |
18 | for (const item of cache.values()) {
19 | console.log(`Cache item: ${JSON.stringify(item)}`)
20 | }
21 |
22 | /**
23 | * When iterating through a Map, you’ll receive each item as an array
24 | * with two entries. The first entry in the array is the key and
25 | * the second entry is the related value. The following example
26 | * uses destructuring to assign the properties.
27 | */
28 | for (const [ key, value ] of cache) {
29 | console.log(`Cache item: "${key}" with values ${JSON.stringify(value)}`)
30 | }
31 |
32 | /**
33 | * Use `for … in` to iterate through the keys of an iterable.
34 | * Using for-in on an object will return the properties.
35 | * On arrays, for-in returns the item’s index.
36 | */
37 | const user = { name: 'Marcus', likes: 'Node.js' }
38 |
39 | for (const key in user) {
40 | console.log(`${key}: ${user[key]}`)
41 | }
42 |
43 | const names = [ 'Marcus', 'Norman', 'Christian' ]
44 |
45 | for (const index in names) {
46 | console.log(`${names[index]} is at position ${index}`)
47 | }
48 |
--------------------------------------------------------------------------------
/misc/custom-error/error.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class NotEnoughCoffee extends Error {
4 | constructor (message) {
5 | super(message)
6 |
7 | this.name = this.constructor.name
8 | }
9 | }
10 |
11 | module.exports = NotEnoughCoffee
12 |
--------------------------------------------------------------------------------
/misc/custom-error/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const NotEnoughCoffee = require('./error')
4 |
5 | throw new NotEnoughCoffee('Well, you may need another coffee :)')
6 |
--------------------------------------------------------------------------------
/misc/increased-memory-limit/show-memory-limit.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const v8 = require('v8')
4 |
5 | // console.log(v8.getHeapStatistics())
6 |
7 | const totalHeapSize = v8.getHeapStatistics().total_available_size
8 | let totalHeapSizeInGB = (totalHeapSize / 1024 / 1024 / 1024).toFixed(2)
9 |
10 | console.log(`Total heap size (bytes) ${totalHeapSize}, (GB ~${totalHeapSizeInGB})`)
11 |
--------------------------------------------------------------------------------
/objects/deep-merge.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const merge = require('deepmerge')
4 |
5 | const first = {
6 | name: 'Marcus',
7 | sub: {
8 | eyes: 'blue'
9 | }
10 | }
11 |
12 | const second = {
13 | name: 'Node.js',
14 | sub: {
15 | hair: 'brown'
16 | }
17 | }
18 |
19 | console.log(
20 | merge(first, second)
21 | )
22 |
--------------------------------------------------------------------------------
/objects/merge-objects.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const first = {
4 | name: 'Marcus',
5 | lovesNode: true,
6 | sub: {
7 | eyes: 'blue'
8 | }
9 | }
10 |
11 | const second = {
12 | name: 'Node.js',
13 | isDeveloper: false,
14 | sub: {
15 | hair: 'brown'
16 | }
17 | }
18 |
19 | /**
20 | * Object.assign merges all arguments into the first one.
21 | * It overrides the properties in the first arguments
22 | */
23 | console.log(
24 | Object.assign({}, first, second)
25 | )
26 |
27 | console.log(
28 | { ...first, ...second }
29 | )
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nodejs-tutorials",
3 | "description": "Future Studio tutorials focussed on Node.js",
4 | "version": "1.0.0",
5 | "author": "Future Studio ",
6 | "bugs": {
7 | "url": "https://github.com/futurestudio/nodejs-tutorials/issues"
8 | },
9 | "contributors": [
10 | {
11 | "name": "Marcus Poehls",
12 | "email": "marcus@futurestud.io"
13 | }
14 | ],
15 | "engines": {
16 | "node": ">=8.0.0"
17 | },
18 | "dependencies": {
19 | "@hapi/hoek": "~7.1.0",
20 | "axios": "~0.19.0",
21 | "deepmerge": "~4.2.2",
22 | "fs-extra": "~8.0.1",
23 | "listr": "~0.14.3",
24 | "lodash": "~4.17.11",
25 | "p-iteration": "~1.1.8",
26 | "progress": "~2.0.3",
27 | "youch": "~2.0.10",
28 | "youch-terminal": "~1.0.0"
29 | },
30 | "devDependencies": {
31 | "@hapi/code": "~5.3.1",
32 | "@hapi/lab": "~19.0.1",
33 | "eslint": "~5.16.0",
34 | "eslint-config-standard": "~12.0.0",
35 | "eslint-plugin-import": "~2.17.3",
36 | "eslint-plugin-node": "~9.1.0",
37 | "eslint-plugin-promise": "~4.1.1",
38 | "eslint-plugin-standard": "~4.0.0"
39 | },
40 | "homepage": "https://github.com/futurestudio/nodejs-tutorials#readme",
41 | "license": "MIT",
42 | "main": "index.js",
43 | "repository": {
44 | "type": "git",
45 | "url": "git+https://github.com/futurestudio/nodejs-tutorials.git"
46 | },
47 | "scripts": {
48 | "lint": "eslint --fix",
49 | "test": "lab --assert @hapi/code --leaks --coverage --lint --reporter console --output stdout --reporter html --output ./coverage/coverage.html"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/streaming/async-readline/async-stringify-transform.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Hoek = require('@hapi/hoek')
4 | const { Transform } = require('stream')
5 |
6 | class Stringifier extends Transform {
7 | async _transform (chunk, _, next) {
8 | const transformed = await this._process(chunk.toString().trim())
9 |
10 | next(null, transformed)
11 | }
12 |
13 | async _process (message) {
14 | console.log(`\nStart processing #${message}`)
15 | await Hoek.wait(2000)
16 |
17 | return JSON.stringify({ message }).concat('\n')
18 | }
19 | }
20 |
21 | module.exports = Stringifier
22 |
--------------------------------------------------------------------------------
/streaming/async-readline/input.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Stringifier = require('./async-stringify-transform')
4 |
5 | console.log('Just type what you want!')
6 |
7 | process.stdin
8 | .pipe(new Stringifier())
9 | .pipe(process.stdout)
10 |
--------------------------------------------------------------------------------
/strings/json-stringify-with-spaces-and-line-breaks/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const data = {
4 | name: 'Marcus',
5 | passion: 'Future Studio',
6 | likes: [
7 | {
8 | tag: 'Node.js',
9 | level: 10
10 | }, {
11 | tag: 'hapi',
12 | level: 10
13 | }
14 | ]
15 | }
16 |
17 | console.log(
18 | JSON.stringify(data, null, 2)
19 | )
20 |
--------------------------------------------------------------------------------
/strings/string-replace-all-appearances/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const string = 'e851e2fa-4f00-4609-9dd2-9b3794c59619'
4 |
5 | console.log(string.replace('-', ''))
6 | // -> e851e2fa4f00-4609-9dd2-9b3794c59619
7 |
8 | console.log(string.replace(/-/g, ''))
9 | // -> e851e2fa4f0046099dd29b3794c59619
10 |
--------------------------------------------------------------------------------
/test/data-structures/linked-list.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Lab = require('@hapi/lab')
4 | const { expect } = require('@hapi/code')
5 | const LinkedList = require('../../data-structures/implementations/linked-list/linked-list')
6 |
7 | const { describe, it } = (exports.lab = Lab.script())
8 |
9 | describe('LinkedList', () => {
10 | it('.addToHead()', async () => {
11 | const list = new LinkedList()
12 | list.addToHead(1).addToHead(2)
13 | expect(list.size()).to.equal(2)
14 | expect(list.toArray()).to.equal([ 2, 1 ])
15 | })
16 |
17 | it('.addBefore()', async () => {
18 | const list = new LinkedList()
19 | list.addToHead(2).addToTail(4).addBefore(2, 1).addBefore(4, 3)
20 | expect(list.size()).to.equal(4)
21 | expect(list.toArray()).to.equal([ 1, 2, 3, 4 ])
22 |
23 | try {
24 | list.addBefore(10, 2)
25 | expect(true).to.be.false() // this should not be reached
26 | } catch (error) {
27 | expect(error).to.exist()
28 | }
29 | })
30 |
31 | it('.addAfter()', async () => {
32 | const list = new LinkedList()
33 | list.addToHead(1).addToTail(2).addToTail(4)
34 | list.addAfter(2, 3)
35 | expect(list.size()).to.equal(4)
36 | expect(list.toArray()).to.equal([ 1, 2, 3, 4 ])
37 |
38 | try {
39 | list.addAfter(10, 2)
40 | expect(true).to.be.false() // this should not be reached
41 | } catch (error) {
42 | expect(error).to.exist()
43 | }
44 |
45 | const oneTwo = new LinkedList()
46 | oneTwo.addToHead(1).addAfter(1, 2)
47 |
48 | expect(oneTwo.size()).to.equal(2)
49 | expect(oneTwo.toArray()).to.equal([ 1, 2 ])
50 | })
51 |
52 | it('.addToTail()', async () => {
53 | const list = new LinkedList()
54 | list.addToTail(1).addToTail(2)
55 | expect(list.size()).to.equal(2)
56 | expect(list.toArray()).to.equal([ 1, 2 ])
57 | })
58 |
59 | it('.removeHead()', async () => {
60 | const list = new LinkedList()
61 | list.removeHead().addToHead(1).addToTail(2).addToTail(3)
62 | list.removeHead()
63 | expect(list.size()).to.equal(2)
64 | expect(list.has(1)).to.be.false()
65 | expect(list.has(2)).to.be.true()
66 | expect(list.has(3)).to.be.true()
67 | })
68 |
69 | it('.removeTail()', async () => {
70 | const list = new LinkedList()
71 | list.removeTail().addToHead(1).addToTail(2).addToTail(3)
72 | list.removeTail()
73 |
74 | expect(list.size()).to.equal(2)
75 | expect(list.has(1)).to.be.true()
76 | expect(list.has(2)).to.be.true()
77 | expect(list.has(3)).to.be.false()
78 |
79 | const head = new LinkedList().addToHead(1)
80 | head.removeTail()
81 | expect(head.isEmpty()).to.be.true()
82 | })
83 |
84 | it('.remove()', async () => {
85 | const list = new LinkedList()
86 | list.remove(1).addToHead(1).addToTail(2).addToTail(3)
87 | list.remove(2)
88 | expect(list.size()).to.equal(2)
89 | expect(list.toArray()).to.equal([ 1, 3 ])
90 |
91 | list.remove(1)
92 | expect(list.toArray()).to.equal([ 3 ])
93 |
94 | try {
95 | list.remove(10, 2)
96 | expect(true).to.be.false() // this should not be reached
97 | } catch (error) {
98 | expect(error).to.exist()
99 | }
100 | })
101 |
102 | it('.has()', async () => {
103 | const list = new LinkedList()
104 | list.addToHead(1).addToTail(1)
105 | expect(list.has(1)).to.be.true()
106 | expect(list.has(20)).to.be.false()
107 | })
108 |
109 | it('.traverse()', async () => {
110 | let sum = 0
111 | const list = new LinkedList()
112 | list.addToTail(1).addToTail(2).addToTail(3)
113 | list.traverse((num) => {
114 | sum = num + sum
115 | })
116 | expect(sum).to.equal(6)
117 | })
118 |
119 | it('.toArray()', async () => {
120 | const list = new LinkedList()
121 | list.addToHead(1).addToTail(2).addToTail(3)
122 | expect(list.toArray()).to.equal([ 1, 2, 3 ])
123 | })
124 |
125 | it('.size()', async () => {
126 | const list = new LinkedList()
127 | list.addToHead(1).addToHead(2)
128 | expect(list.size()).to.equal(2)
129 | })
130 |
131 | it('.isEmpty()', async () => {
132 | const list = new LinkedList()
133 | list.addToHead(1)
134 | expect(list.isEmpty()).to.be.false()
135 |
136 | list.removeHead()
137 | expect(list.isEmpty()).to.be.true()
138 | })
139 |
140 | it('.clear()', async () => {
141 | const list = new LinkedList()
142 | list.addToHead(1)
143 | expect(list.isEmpty()).to.be.false()
144 |
145 | list.clear()
146 | expect(list.isEmpty()).to.be.true()
147 | })
148 | })
149 |
--------------------------------------------------------------------------------
/test/data-structures/priority-queue.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Lab = require('@hapi/lab')
4 | const { expect } = require('@hapi/code')
5 | const Queue = require('../../data-structures/implementations/priority-queue/priority-queue')
6 |
7 | const { describe, it } = (exports.lab = Lab.script())
8 |
9 | describe('PriorityQueue', () => {
10 | it('.enqueue()', async () => {
11 | const queue = new Queue()
12 | queue.enqueue(3, 3).enqueue(1, 1)
13 | expect(queue.size()).to.equal(2)
14 |
15 | queue.enqueue(4, 5)
16 | expect(queue.size()).to.equal(3)
17 | expect(queue.peek()).to.equal(1)
18 |
19 | try {
20 | queue.enqueue(1, 0)
21 | expect(true).to.equal(false) // this should not be reached
22 | } catch (error) {
23 | expect(error).to.exist()
24 | }
25 |
26 | try {
27 | queue.enqueue(1, 'priority')
28 | expect(true).to.equal(false) // this should not be reached
29 | } catch (error) {
30 | expect(error).to.exist()
31 | }
32 | })
33 |
34 | it('.dequeue()', async () => {
35 | const queue = new Queue()
36 | queue.enqueue(3, 3).enqueue(1, 1)
37 | expect(queue.dequeue()).to.equal(1)
38 | expect(queue.dequeue()).to.equal(3)
39 | expect(queue.dequeue()).to.be.undefined()
40 | })
41 |
42 | it('.peek()', async () => {
43 | const queue = new Queue()
44 | expect(queue.peek()).to.be.undefined()
45 |
46 | queue.enqueue(3, 3).enqueue(1, 1).enqueue(2, 2)
47 | expect(queue.peek()).to.equal(1)
48 | expect(queue.size()).to.equal(3)
49 | })
50 |
51 | it('.size()', async () => {
52 | const queue = new Queue()
53 | queue.enqueue(1, 1).enqueue(1, 1)
54 | expect(queue.size()).to.equal(2)
55 | })
56 |
57 | it('.isEmpty()', async () => {
58 | const queue = new Queue()
59 | queue.enqueue(1, 1)
60 | expect(queue.isEmpty()).to.be.false()
61 |
62 | queue.dequeue()
63 | expect(queue.isEmpty()).to.be.true()
64 | })
65 |
66 | it('.clear()', async () => {
67 | const queue = new Queue()
68 | queue.enqueue(1, 1)
69 | expect(queue.isEmpty()).to.be.false()
70 |
71 | queue.clear()
72 | expect(queue.isEmpty()).to.be.true()
73 | })
74 | })
75 |
--------------------------------------------------------------------------------
/test/data-structures/queue.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Lab = require('@hapi/lab')
4 | const { expect } = require('@hapi/code')
5 | const Queue = require('../../data-structures/implementations/queue/queue')
6 |
7 | const { describe, it } = (exports.lab = Lab.script())
8 |
9 | describe('Queue', () => {
10 | it('.enqueue()', async () => {
11 | const queue = new Queue(1, 2)
12 | queue.enqueue(3)
13 | expect(queue.size()).to.equal(3)
14 |
15 | queue.enqueue(4, 5)
16 | expect(queue.size()).to.equal(5)
17 | expect(queue.peek()).to.equal(1)
18 | })
19 |
20 | it('.dequeue()', async () => {
21 | const queue = new Queue(1)
22 | expect(queue.dequeue()).to.equal(1)
23 | expect(queue.size()).to.equal(0)
24 | expect(queue.dequeue()).to.be.undefined()
25 | })
26 |
27 | it('.peek()', async () => {
28 | const queue = new Queue(1, 2, 3)
29 | const item = queue.peek()
30 | expect(item).to.equal(1)
31 | expect(queue.size()).to.equal(3)
32 | })
33 |
34 | it('.size()', async () => {
35 | const queue = new Queue(1, 2)
36 | expect(queue.size()).to.equal(2)
37 | })
38 |
39 | it('.isEmpty()', async () => {
40 | const queue = new Queue(1)
41 | expect(queue.isEmpty()).to.be.false()
42 |
43 | queue.dequeue()
44 | expect(queue.isEmpty()).to.be.true()
45 | })
46 |
47 | it('.clear()', async () => {
48 | const queue = new Queue(1)
49 | expect(queue.isEmpty()).to.be.false()
50 |
51 | queue.clear()
52 | expect(queue.isEmpty()).to.be.true()
53 | })
54 | })
55 |
--------------------------------------------------------------------------------
/test/data-structures/set.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Lab = require('@hapi/lab')
4 | const { expect } = require('@hapi/code')
5 | const Set = require('../../data-structures/implementations/set/set')
6 |
7 | const { describe, it } = (exports.lab = Lab.script())
8 |
9 | describe('Set', () => {
10 | it('.add()', async () => {
11 | const set = new Set(1, 2, 1)
12 | set.add(3)
13 | expect(set.size()).to.equal(3)
14 |
15 | set.add(4, 4, 5)
16 | expect(set.size()).to.equal(5)
17 | })
18 |
19 | it('.remove()', async () => {
20 | const set = new Set(1, 2)
21 | set.remove(1)
22 | expect(set.size()).to.equal(1)
23 |
24 | set.remove(1)
25 | expect(set.size()).to.equal(1)
26 | })
27 |
28 | it('.has()', async () => {
29 | const set = new Set(1, 2, 3)
30 | expect(set.has(2)).to.be.true()
31 | expect(set.has(4)).to.be.false()
32 | })
33 |
34 | it('.toArray()', async () => {
35 | const numbers = new Set(1, 2, 3)
36 | expect(numbers.toArray()).to.equal([1, 2, 3])
37 |
38 | const strings = new Set('Marcus', 'Norman', 'Christian')
39 | expect(strings.toArray()).to.equal(['Marcus', 'Norman', 'Christian'])
40 | })
41 |
42 | it('.size()', async () => {
43 | const set = new Set(1, 2, 1, 2)
44 | expect(set.size()).to.equal(2)
45 | })
46 |
47 | it('.isEmpty()', async () => {
48 | const set = new Set(1)
49 | expect(set.isEmpty()).to.be.false()
50 |
51 | set.remove(1)
52 | expect(set.isEmpty()).to.be.true()
53 | })
54 |
55 | it('.clear()', async () => {
56 | const set = new Set(1)
57 | expect(set.isEmpty()).to.be.false()
58 |
59 | set.clear()
60 | expect(set.isEmpty()).to.be.true()
61 | })
62 | })
63 |
--------------------------------------------------------------------------------
/test/data-structures/stack.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Lab = require('@hapi/lab')
4 | const { expect } = require('@hapi/code')
5 | const Stack = require('../../data-structures/implementations/stack/stack')
6 |
7 | const { describe, it } = (exports.lab = Lab.script())
8 |
9 | describe('Stack', () => {
10 | it('.push()', async () => {
11 | const stack = new Stack(1, 2)
12 | stack.push(3)
13 | expect(stack.size()).to.equal(3)
14 |
15 | stack.push(4, 5)
16 | expect(stack.size()).to.equal(5)
17 | expect(stack.peek()).to.equal(5)
18 | })
19 |
20 | it('.pop()', async () => {
21 | const stack = new Stack(1)
22 | expect(stack.pop()).to.equal(1)
23 | expect(stack.size()).to.equal(0)
24 | expect(stack.pop()).to.be.undefined()
25 | })
26 |
27 | it('.peek()', async () => {
28 | const stack = new Stack(1, 2, 3)
29 | const item = stack.peek()
30 | expect(item).to.equal(3)
31 | expect(stack.size()).to.equal(3)
32 | })
33 |
34 | it('.size()', async () => {
35 | const stack = new Stack(1, 2)
36 | expect(stack.size()).to.equal(2)
37 | })
38 |
39 | it('.isEmpty()', async () => {
40 | const stack = new Stack(1)
41 | expect(stack.isEmpty()).to.be.false()
42 |
43 | stack.pop()
44 | expect(stack.isEmpty()).to.be.true()
45 | })
46 |
47 | it('.clear()', async () => {
48 | const stack = new Stack(1)
49 | expect(stack.isEmpty()).to.be.false()
50 |
51 | stack.clear()
52 | expect(stack.isEmpty()).to.be.true()
53 | })
54 | })
55 |
--------------------------------------------------------------------------------