diff --git a/docs/content/ds/data-structures/graphs/README.adoc b/docs/content/ds/data-structures/graphs/README.adoc new file mode 100644 index 0000000000..90a88bd513 --- /dev/null +++ b/docs/content/ds/data-structures/graphs/README.adoc @@ -0,0 +1 @@ +include::../../../book/content/part03/graph.asc[] diff --git a/docs/content/ds/data-structures/graphs/graph-scale.spec.js b/docs/content/ds/data-structures/graphs/graph-scale.spec.js new file mode 100644 index 0000000000..3bb3e35420 --- /dev/null +++ b/docs/content/ds/data-structures/graphs/graph-scale.spec.js @@ -0,0 +1,52 @@ +const Graph = require('./graph'); + +xdescribe('Graph (social network)', () => { + let graph; + + beforeEach(() => { + graph = new Graph(Graph.UNDIRECTED); + + graph.addEdge('You', 'James'); + graph.addEdge('James', 'Michael'); + graph.addEdge('James', 'William'); + graph.addEdge('You', 'John'); + graph.addEdge('John', 'Linda'); + graph.addEdge('Linda', 'Elizabeth'); + graph.addEdge('You', 'Robert'); + graph.addEdge('You', 'Mary'); + graph.addEdge('You', 'Patricia'); + graph.addEdge('You', 'Jennifer'); + graph.addEdge('You', 'Larry'); + graph.addEdge('You', 'Eric'); + graph.addEdge('David', 'Barbara'); + graph.addEdge('Richard', 'Susan'); + graph.addEdge('Joseph', 'Jessica'); + graph.addEdge('Susan', 'Joseph'); + graph.addEdge('Mark', 'Jan'); + graph.addEdge('Mark', 'David'); + graph.addEdge('Mark', 'Dustin'); + graph.addEdge('Mark', 'Owen'); + graph.addEdge('Mark', 'Pricilla'); + graph.addEdge('Mark', 'Andrew'); + graph.addEdge('Mark', 'Adam'); + graph.addEdge('Pricilla', 'Richard'); + graph.addEdge('Jessica', 'Elizabeth'); + graph.addEdge('Mary', 'Barbara'); + graph.addEdge('William', 'Jan'); + graph.addEdge('Joseph', 'Robert'); + graph.addEdge('Dustin', 'Michael'); + graph.addEdge('Andrew', 'William'); + graph.addEdge('Jessica', 'John'); + graph.addEdge('Adam', 'Susan'); + graph.addEdge('William', 'Barbara'); + graph.addEdge('Joseph', 'Patricia'); + graph.addEdge('Joseph', 'Michael'); + graph.addEdge('Elizabeth', 'Adam'); + graph.addEdge('Jan', 'Elizabeth'); + graph.addEdge('Richard', 'Joseph'); + }); + + it('should return all paths connecting you and mark', () => { + expect(graph.findPath('You', 'Mark')).toEqual([]); + }); +}); diff --git a/docs/content/ds/data-structures/graphs/graph.js b/docs/content/ds/data-structures/graphs/graph.js new file mode 100644 index 0000000000..5342b6d3de --- /dev/null +++ b/docs/content/ds/data-structures/graphs/graph.js @@ -0,0 +1,258 @@ +// npx eslint --fix -f codeframe lib/data-structures/graphs/graph.js +const Node = require('./node'); +const Stack = require('../stacks/stack'); +const Queue = require('../queues/queue'); +const HashMap = require('../maps/hash-maps/hash-map'); + +// tag::constructor[] +/** + * Graph data structure implemented with an adjacent list + */ +class Graph { + /** + * Initialize the nodes map + * @param {Symbol} edgeDirection either `Graph.DIRECTED` or `Graph.UNDIRECTED` + */ + constructor(edgeDirection = Graph.DIRECTED) { + this.nodes = new HashMap(); + this.edgeDirection = edgeDirection; + } + // end::constructor[] + + // tag::addVertex[] + /** + * Add a node to the graph. + * Runtime: O(1) + * @param {any} value node's value + * @returns {Node} the new node or the existing one if it already exits. + */ + addVertex(value) { + if (this.nodes.has(value)) { // <1> + return this.nodes.get(value); + } + const vertex = new Node(value); // <2> + this.nodes.set(value, vertex); // <3> + return vertex; + } + // end::addVertex[] + + // tag::removeVertex[] + /** + * Removes node from graph + * It also removes the reference of the deleted node from + * anywhere it was adjacent to. + * Runtime: O(|V|) because adjacency list is implemented with a HashSet. + * It were implemented with an array then it would be O(|V| + |E|). + * @param {any} value node's value + */ + removeVertex(value) { + const current = this.nodes.get(value); // <1> + if (current) { + Array.from(this.nodes.values()).forEach((node) => node.removeAdjacent(current)); // <2> + } + return this.nodes.delete(value); // <3> + } + // end::removeVertex[] + + // tag::addEdge[] + /** + * Create a connection between the source node and the destination node. + * If the graph is undirected, it will also create the link from destination to source. + * If the nodes don't exist, then it will make them on the fly. + * Runtime: O(1) + * @param {any} source + * @param {any} destination + * @returns {[Node, Node]} source/destination node pair + */ + addEdge(source, destination) { + const sourceNode = this.addVertex(source); // <1> + const destinationNode = this.addVertex(destination); // <1> + + sourceNode.addAdjacent(destinationNode); // <2> + + if (this.edgeDirection === Graph.UNDIRECTED) { + destinationNode.addAdjacent(sourceNode); // <3> + } + + return [sourceNode, destinationNode]; + } + // end::addEdge[] + + // tag::removeEdge[] + /** + * Remove the connection between source node and destination. + * If the graph is undirected, it will also create the link from destination to source. + * + * Runtime: O(1): implemented with HashSet. + * If implemented with array, would be O(|E|). + * + * @param {any} source + * @param {any} destination + */ + removeEdge(source, destination) { + const sourceNode = this.nodes.get(source); + const destinationNode = this.nodes.get(destination); + + if (sourceNode && destinationNode) { + sourceNode.removeAdjacent(destinationNode); + + if (this.edgeDirection === Graph.UNDIRECTED) { + destinationNode.removeAdjacent(sourceNode); + } + } + + return [sourceNode, destinationNode]; + } + // end::removeEdge[] + + // tag::areAdjacents[] + /** + * True if two nodes are adjacent. + * @param {any} source node's value + * @param {any} destination node's value + */ + areAdjacents(source, destination) { + const sourceNode = this.nodes.get(source); + const destinationNode = this.nodes.get(destination); + + if (sourceNode && destinationNode) { + return sourceNode.isAdjacent(destinationNode); + } + + return false; + } + // end::areAdjacents[] + + // tag::graphSearch[] + /** + * Depth-first search + * Use a stack to visit nodes (LIFO) + * @param {Node} first node to start the dfs + */ + static* dfs(first) { + yield* Graph.graphSearch(first, Stack); + } + + /** + * Breadth-first search + * Use a queue to visit nodes (FIFO) + * @param {Node} first node to start the dfs + */ + static* bfs(first) { + yield* Graph.graphSearch(first, Queue); + } + + /** + * Generic graph search where we can pass a Stack or Queue + * @param {Node} first node to start the search + * @param {Stack|Queue} Type Stack for DFS or Queue for BFS + */ + static* graphSearch(first, Type = Stack) { + const visited = new Map(); + const visitList = new Type(); + + visitList.add(first); + + while (!visitList.isEmpty()) { + const node = visitList.remove(); + if (node && !visited.has(node)) { + yield node; + visited.set(node); + node.getAdjacents().forEach((adj) => visitList.add(adj)); + } + } + } + // end::graphSearch[] + + /** + * Return true if two nodes are connected and false if not + * @param {any} source vertex's value + * @param {*} destination vertex's value + */ + areConnected(source, destination) { + const sourceNode = this.nodes.get(source); + const destinationNode = this.nodes.get(destination); + + if (sourceNode && destinationNode) { + const bfsFromFirst = Graph.bfs(sourceNode); + // eslint-disable-next-line no-restricted-syntax + for (const node of bfsFromFirst) { + if (node === destinationNode) { + return true; + } + } + } + + return false; + } + + /** + * Find a path between source and destination + * It might not be the optimal path + * + * @param {any} source vertex's value + * @param {any} destination vertex's value + * @param {Map} newPath current path from source to destination + * @returns list of nodes from source to destination + */ + findPath(source, destination, path = new Map()) { + const sourceNode = this.nodes.get(source); + const destinationNode = this.nodes.get(destination); + const newPath = new Map(path); + + if (!destinationNode || !sourceNode) return []; + + newPath.set(sourceNode); + + if (source === destination) { + return Array.from(newPath.keys()); + } + + // eslint-disable-next-line no-restricted-syntax + for (const node of sourceNode.getAdjacents()) { + if (!newPath.has(node)) { + const nextPath = this.findPath(node.value, destination, newPath); + if (nextPath.length) { + return nextPath; + } + } + } + + return []; + } + + /** + * Find all paths from source to destination + * + * @param {any} source vertex'value + * @param {any} destination vertex'value + * @param {Map} path (optional) used for recursion + */ + findAllPaths(source, destination, path = new Map()) { + const sourceNode = this.nodes.get(source); + const destinationNode = this.nodes.get(destination); + const newPath = new Map(path); + + if (!destinationNode || !sourceNode) return []; + + newPath.set(sourceNode); + + if (source === destination) { + return [Array.from(newPath.keys())]; + } + + const paths = []; + sourceNode.getAdjacents().forEach((node) => { + if (!newPath.has(node)) { + const nextPaths = this.findAllPaths(node.value, destination, newPath); + nextPaths.forEach((nextPath) => paths.push(nextPath)); + } + }); + return paths; + } +} + +Graph.UNDIRECTED = Symbol('undirected graph'); // two-way edges +Graph.DIRECTED = Symbol('directed graph'); // one-way edges + +module.exports = Graph; diff --git a/docs/content/ds/data-structures/graphs/graph.spec.js b/docs/content/ds/data-structures/graphs/graph.spec.js new file mode 100644 index 0000000000..4ff1fb3b48 --- /dev/null +++ b/docs/content/ds/data-structures/graphs/graph.spec.js @@ -0,0 +1,304 @@ +const { Graph } = require('../../index'); + +describe('Graph', () => { + let graph; + const getValues = (node) => (Array.isArray(node) ? node.map((a) => getValues(a)) : node.value); + + beforeEach(() => { + graph = new Graph(); + }); + + describe('#addVertex', () => { + it('should add vertex to graph', () => { + const node = graph.addVertex('a'); + expect(node.value).toBe('a'); + expect(graph.nodes.size).toBe(1); + }); + + it('should not add duplicated values', () => { + const node1 = graph.addVertex('a'); + const node2 = graph.addVertex('a'); + expect(graph.nodes.size).toBe(1); + expect(node1).toBe(node2); + }); + }); + + describe('#removeVertex', () => { + beforeEach(() => { + graph.addVertex('a'); + }); + + it('should remove vertex', () => { + expect(graph.removeVertex('a')).toBe(true); + expect(graph.nodes.size).toBe(0); + expect(graph.removeVertex('a')).toBe(false); + }); + }); + + describe('#addEdge', () => { + it('should create node if they dont exist', () => { + graph.addEdge('a', 'b'); + expect(graph.nodes.size).toBe(2); + }); + + it('should add node a as adjacent of b', () => { + const [a, b] = graph.addEdge('a', 'b'); + expect(a.getAdjacents().map(getValues)).toEqual(['b']); + expect(b.getAdjacents().map(getValues)).toEqual([]); + + graph.addEdge('b', 'a'); + expect(b.getAdjacents().map(getValues)).toEqual(['a']); + }); + + it('should add both connection on undirected graph', () => { + graph = new Graph(Graph.UNDIRECTED); + const [a, b] = graph.addEdge('a', 'b'); + expect(a.getAdjacents().map(getValues)).toEqual(['b']); + expect(b.getAdjacents().map(getValues)).toEqual(['a']); + }); + + it('should add falsy values', () => { + const [first, second] = graph.addEdge(0, false); + expect(first.value).toBe(0); + expect(second.value).toBe(false); + }); + }); + + describe('#removeEdge', () => { + beforeEach(() => { + graph.addEdge('a', 'b'); + }); + + it('should remove edges if they exist', () => { + const [a, b] = graph.removeEdge('a', 'b'); + expect(a.getAdjacents().map(getValues)).toEqual([]); + expect(b.getAdjacents().map(getValues)).toEqual([]); + }); + + it('should remove edges with falsy values', () => { + const [a, b] = graph.addEdge(0, false); + expect(a.getAdjacents().map(getValues)).toEqual([false]); + expect(b.getAdjacents().map(getValues)).toEqual([]); + graph.removeEdge(0, false); + expect(a.getAdjacents().map(getValues)).toEqual([]); + expect(b.getAdjacents().map(getValues)).toEqual([]); + }); + + it('should not create node when removing unexisting target', () => { + const [a, c] = graph.removeEdge('a', 'c'); + expect(graph.nodes.size).toBe(2); + expect(a.getAdjacents().map(getValues)).toEqual(['b']); + expect(c).toBe(undefined); + }); + + it('should not create node when removing unexisting nodes', () => { + const [d, c] = graph.removeEdge('d', 'c'); + expect(graph.nodes.size).toBe(2); + expect(c).toBe(undefined); + expect(d).toBe(undefined); + }); + }); + + describe('#areAdjacents', () => { + it('should return true if nodes are adjacent', () => { + graph.addEdge('a', 'b'); + expect(graph.areAdjacents('a', 'b')).toBe(true); + }); + + it('should return false if a digraph nodes are adjacent in one direction only', () => { + graph.addEdge('a', 'b'); + expect(graph.areAdjacents('b', 'a')).toBe(false); + }); + + it('should return true if a undirected graph nodes are adjacent', () => { + graph = new Graph(Graph.UNDIRECTED); + graph.addEdge('a', 'b'); + expect(graph.areAdjacents('b', 'a')).toBe(true); + }); + + it('should return false if nodes does not exist', () => { + expect(graph.areAdjacents('a', 'b')).toBe(false); + }); + + it('should return false if nodes are not adjacent', () => { + graph.addVertex('a'); + graph.addVertex('b'); + expect(graph.areAdjacents('a', 'b')).toBe(false); + }); + }); + + describe('remove vertex and update adjacency list', () => { + let n1; + let n2; + let n4; + + beforeEach(() => { + // 5 + // /^ + // 0 -> 1 <- 2 + // ^\-> 4 -> 3 + graph.addEdge(0, 1); + [n2] = graph.addEdge(2, 1); + [n1, n4] = graph.addEdge(1, 4); + graph.addEdge(4, 1); + graph.addEdge(4, 3); + }); + + it('should remove nodes from adjacent list', () => { + expect(n4.getAdjacents().map(getValues)).toEqual([1, 3]); + expect(n2.getAdjacents().map(getValues)).toEqual([1]); + expect(graph.nodes.has(n1.value)).toBe(true); + graph.removeVertex(1); + expect(n4.getAdjacents().map(getValues)).toEqual([3]); + expect(graph.nodes.has(n1.value)).toBe(false); + }); + }); + + describe('Graph Search', () => { + let first; + + beforeEach(() => { + graph.addEdge(0, 5); + graph.addEdge(0, 4); + [first] = graph.addEdge(0, 1); + + graph.addEdge(1, 4); + graph.addEdge(1, 3); + + graph.addEdge(2, 1); + + graph.addEdge(3, 4); + graph.addEdge(3, 2); + }); + + describe('#dfs', () => { + it('should visit nodes using depth-first search', () => { + const visitedOrder = Array.from(Graph.dfs(first)).map(getValues); + expect(visitedOrder).toEqual([0, 1, 3, 2, 4, 5]); + }); + }); + + describe('#bfs', () => { + it('should visit nodes using breadth-first search', () => { + const visitedOrder = Array.from(Graph.bfs(first)).map(getValues); + expect(visitedOrder).toEqual([0, 5, 4, 1, 3, 2]); + }); + }); + }); + + describe('Graph Search: UNDIRECTED', () => { + let first; + + beforeEach(() => { + graph = new Graph(Graph.UNDIRECTED); + [first] = graph.addEdge(1, 2); + graph.addEdge(1, 3); + graph.addEdge(1, 4); + + graph.addEdge(5, 2); + graph.addEdge(6, 3); + graph.addEdge(7, 3); + graph.addEdge(8, 4); + graph.addEdge(9, 5); + graph.addEdge(10, 6); + }); + + describe('#dfs', () => { + it('should visit nodes using depth-first search', () => { + const visitedOrder = Array.from(Graph.dfs(first)).map(getValues); + expect(visitedOrder).toEqual([1, 4, 8, 3, 7, 6, 10, 2, 5, 9]); + }); + + it('should use iterator', () => { + const dfs = Graph.dfs(first); + + expect(dfs.next().value.value).toBe(1); + expect(dfs.next().value.value).toBe(4); + expect(dfs.next().value.value).toBe(8); + expect(dfs.next().value.value).toBe(3); + expect(dfs.next().value.value).toBe(7); + expect(dfs.next().value.value).toBe(6); + expect(dfs.next().value.value).toBe(10); + expect(dfs.next().value.value).toBe(2); + expect(dfs.next().value.value).toBe(5); + expect(dfs.next().value.value).toBe(9); + + expect(dfs.next().value).toBe(undefined); + expect(dfs.next().done).toBe(true); + }); + }); + + describe('#bfs', () => { + it('should visit nodes using breadth-first search', () => { + const visitedOrder = Array.from(Graph.bfs(first)).map(getValues); + expect(visitedOrder).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + }); + }); + }); + + describe('with (people) edges on undigraph', () => { + let you; + let mary; + let barbara; + + beforeEach(() => { + graph = new Graph(Graph.UNDIRECTED); + [you] = graph.addEdge('you', 'mary'); + [mary, barbara] = graph.addEdge('mary', 'barbara'); + }); + + describe('#areConnected', () => { + it('should return true if two nodes are connected', () => { + expect(graph.areConnected('you', 'barbara')).toBe(true); + }); + + it('should return true if two nodes are connected to itself', () => { + expect(graph.areConnected('you', 'you')).toBe(true); + }); + + it('should return true if two nodes are connected to other', () => { + expect(graph.areConnected('you', 'John')).toBe(false); + }); + }); + + describe('#findPath', () => { + it('should handle source === destination', () => { + expect(graph.findPath('you', 'you')).toEqual([you]); + }); + + it('should get connecting path', () => { + expect(graph.findPath('you', 'barbara').map(getValues)).toEqual(['you', 'mary', 'barbara']); + }); + + it('should get adjacent connecting path', () => { + expect(graph.findPath('mary', 'barbara').map(getValues)).toEqual(['mary', 'barbara']); + expect(graph.findPath('mary', 'barbara')).toEqual([mary, barbara]); + }); + + it('should return empty if there is no connection', () => { + expect(graph.findPath('you', 'Obama').map(getValues)).toEqual([]); + }); + }); + + describe('#findAllPaths', () => { + it('should handle source === destination', () => { + expect(graph.findAllPaths('you', 'you')).toEqual([[you]]); + expect(getValues(graph.findAllPaths('you', 'you'))).toEqual([['you']]); + }); + + it('should find all paths when only one', () => { + expect(getValues(graph.findAllPaths('mary', 'barbara'))).toEqual([ + ['mary', 'barbara'], + ]); + }); + + it('should find all paths', () => { + graph.addEdge('you', 'barbara'); + expect(getValues(graph.findAllPaths('you', 'barbara'))).toEqual([ + ['you', 'mary', 'barbara'], + ['you', 'barbara'], + ]); + }); + }); + }); +}); diff --git a/docs/content/ds/data-structures/graphs/node-1.js b/docs/content/ds/data-structures/graphs/node-1.js new file mode 100644 index 0000000000..801abef8dc --- /dev/null +++ b/docs/content/ds/data-structures/graphs/node-1.js @@ -0,0 +1,53 @@ +// tag::constructor[] +/** + * Graph node/vertex that hold adjacencies nodes + */ +class Node { + constructor(value) { + this.value = value; + this.adjacents = []; // adjacency list + } + // end::constructor[] + + /** + * Add node to adjacency list + * Runtime: O(1) + * @param {Node} node + */ + addAdjacent(node) { + this.adjacents.push(node); + } + + /** + * Remove node from adjacency list + * Runtime: O(n) + * @param {Node} node + * @returns removed node or `undefined` if node was not found + */ + removeAdjacent(node) { + const index = this.adjacents.indexOf(node); + if (index > -1) { + this.adjacents.splice(index, 1); + return node; + } + return undefined; + } + + /** + * Check if a Node is adjacent to other + * Runtime: O(n) + * @param {Node} node + */ + isAdjacent(node) { + return this.adjacents.indexOf(node) > -1; + } + + /** + * Get all adjacent nodes + */ + getAdjacents() { + return this.adjacents; + } +} + +module.exports = Node; diff --git a/docs/content/ds/data-structures/graphs/node.js b/docs/content/ds/data-structures/graphs/node.js new file mode 100644 index 0000000000..9e11b1eec0 --- /dev/null +++ b/docs/content/ds/data-structures/graphs/node.js @@ -0,0 +1,59 @@ +const HashSet = require('../sets/hash-set'); + +// tag::constructor[] +/** + * Graph node/vertex that hold adjacencies nodes + * For performance, uses a HashSet instead of array for adjacents. + */ +class Node { + constructor(value) { + this.value = value; + this.adjacents = new HashSet(); // adjacency list + } + // end::constructor[] + + // tag::addAdjacent[] + /** + * Add node to adjacency list + * Runtime: O(1) + * @param {Node} node + */ + addAdjacent(node) { + this.adjacents.add(node); + } + // end::addAdjacent[] + + // tag::removeAdjacent[] + /** + * Remove node from adjacency list + * Runtime: O(1) + * @param {Node} node + * @returns removed node or `false` if node was not found + */ + removeAdjacent(node) { + return this.adjacents.delete(node); + } + // end::removeAdjacent[] + + // tag::isAdjacent[] + /** + * Check if a Node is adjacent to other + * Runtime: O(1) + * @param {Node} node + */ + isAdjacent(node) { + return this.adjacents.has(node); + } + // end::isAdjacent[] + + /** + * Get all adjacent nodes + */ + getAdjacents() { + return Array.from(this.adjacents); + } + // tag::constructor[] +} +// end::constructor[] + +module.exports = Node; diff --git a/docs/content/ds/data-structures/graphs/node.spec.js b/docs/content/ds/data-structures/graphs/node.spec.js new file mode 100644 index 0000000000..360480be8f --- /dev/null +++ b/docs/content/ds/data-structures/graphs/node.spec.js @@ -0,0 +1,70 @@ +const Node = require('./node'); + +describe('Node (Graph)', () => { + let node; + + beforeEach(() => { + node = new Node('a'); + }); + + describe('#addAdjacent', () => { + it('should add node to adjacent list', () => { + node.addAdjacent(new Node(2)); + expect(node.getAdjacents().map((n) => n.value)).toEqual([2]); + }); + }); + + describe('#removeAdjacent', () => { + let a; + let b; + let c; + let d; + + beforeEach(() => { + a = node; + b = new Node('b'); + c = new Node('c'); + d = new Node('d'); + + a.addAdjacent(b); + a.addAdjacent(c); + }); + + it('should remove node to adjacent list', () => { + expect(a.removeAdjacent(c)).toBe(true); + expect(node.getAdjacents().map((n) => n.value)).toEqual(['b']); + + expect(a.removeAdjacent(b)).toBe(true); + expect(node.getAdjacents().map((n) => n.value)).toEqual([]); + }); + + it('should return undefined if not found', () => { + expect(a.removeAdjacent(d)).toBe(false); + expect(node.getAdjacents().map((n) => n.value)).toEqual(['b', 'c']); + }); + }); + + describe('#getAdjacents', () => { + it('should get adjacents', () => { + node.addAdjacent(new Node('b')); + expect(node.getAdjacents().map((n) => n.value)).toEqual(['b']); + }); + + it('should get adjacents when empty', () => { + expect(node.getAdjacents().map((n) => n.value)).toEqual([]); + }); + }); + + describe('#isAdjacent', () => { + it('should return true if they are adjacent', () => { + const b = new Node('b'); + node.addAdjacent(b); + expect(node.isAdjacent(b)).toBe(true); + }); + + it('should return true if they are adjacent on c', () => { + const c = new Node('c'); + expect(node.isAdjacent(c)).toBe(false); + }); + }); +}); diff --git a/docs/content/ds/data-structures/heaps/heap.js b/docs/content/ds/data-structures/heaps/heap.js new file mode 100644 index 0000000000..54863c39b7 --- /dev/null +++ b/docs/content/ds/data-structures/heaps/heap.js @@ -0,0 +1,103 @@ +/** + * Heap data structure a.k.a Priority Queue + * + * Used to get min or max values from a collection in constant time. + * + * @author Adrian Mejia + */ +class Heap { + constructor(comparator = (a, b) => a - b) { + this.array = []; + this.comparator = (i1, i2) => { + const value = comparator(this.array[i1], this.array[i2]); + if (Number.isNaN(value)) { throw new Error(`Comparator should evaluate to a number. Got ${value} when comparing ${this.array[i1]} with ${this.array[i2]}`); } + return value; + }; + } + + /** + * Insert element + * @runtime O(log n) + * @param {any} value + */ + add(value) { + this.array.push(value); + this.bubbleUp(); + } + + /** + * Retrieves, but does not remove, the head of this heap + * @runtime O(1) + */ + peek() { + return this.array[0]; + } + + /** + * Retrieves and removes the head of this heap, or returns null if this heap is empty. + * @runtime O(log n) + */ + remove(index = 0) { + if (!this.size) return null; + this.swap(index, this.size - 1); // swap with last + const value = this.array.pop(); // remove element + this.bubbleDown(index); + return value; + } + + /** + * Returns the number of elements in this collection. + * @runtime O(1) + */ + get size() { + return this.array.length; + } + + /** + * Move new element upwards on the heap, if it's out of order + * @runtime O(log n) + */ + bubbleUp() { + let index = this.size - 1; + const parent = (i) => Math.ceil(i / 2 - 1); + while (parent(index) >= 0 && this.comparator(parent(index), index) > 0) { + this.swap(parent(index), index); + index = parent(index); + } + } + + /** + * After removal, moves element downwards on the heap, if it's out of order + * @runtime O(log n) + */ + bubbleDown(index = 0) { + let curr = index; + const left = (i) => 2 * i + 1; + const right = (i) => 2 * i + 2; + const getTopChild = (i) => (right(i) < this.size + && this.comparator(left(i), right(i)) > 0 ? right(i) : left(i)); + + while (left(curr) < this.size && this.comparator(curr, getTopChild(curr)) > 0) { + const next = getTopChild(curr); + this.swap(curr, next); + curr = next; + } + } + + /** + * Swap elements on the heap + * @runtime O(1) + * @param {number} i1 index 1 + * @param {number} i2 index 2 + */ + swap(i1, i2) { + [this.array[i1], this.array[i2]] = [this.array[i2], this.array[i1]]; + } +} + +// aliases +Heap.prototype.poll = Heap.prototype.remove; +Heap.prototype.offer = Heap.prototype.add; +Heap.prototype.element = Heap.prototype.peek; + +module.exports = Heap; diff --git a/docs/content/ds/data-structures/heaps/heap.spec.js b/docs/content/ds/data-structures/heaps/heap.spec.js new file mode 100644 index 0000000000..f1d6bb096c --- /dev/null +++ b/docs/content/ds/data-structures/heaps/heap.spec.js @@ -0,0 +1,172 @@ +const Heap = require('./heap'); +const PriorityQueue = require('./priority-queue'); +const MaxHeap = require('./max-heap'); +const MinHeap = require('./min-heap'); + +[ + [Heap], + [PriorityQueue, [], (a, b) => a - b], + [MinHeap], +].forEach(([DS, ...arg]) => { + describe('Min-Heap and Priority Queue', () => { + let heap; + + beforeEach(() => { + heap = new DS(...arg); + }); + + describe('#contructor', () => { + it('should initialize', () => { + expect(heap).not.toBe(undefined); + }); + }); + + describe('#add', () => { + it('should add an element', () => { + expect(heap.add(1)).toBe(undefined); + expect(heap.array).toEqual([1]); + expect(heap.size).toBe(1); + }); + + it('should keep things in order', () => { + heap.add(3); + expect(heap.array[0]).toEqual(3); + heap.add(2); + expect(heap.array[0]).toEqual(2); + heap.add(1); + expect(heap.array[0]).toEqual(1); + expect(heap.size).toEqual(3); + }); + }); + + describe('#remove', () => { + it('should work', () => { + heap.add(1); + heap.add(0); + expect(heap.remove()).toBe(0); + expect(heap.size).toBe(1); + expect(heap.array).toEqual([1]); + }); + + it('should return null if empty', () => { + heap = new Heap(); + expect(heap.remove()).toBe(null); + }); + }); + + describe('when has elements', () => { + beforeEach(() => { + heap.add(1); + heap.add(2); + heap.add(3); + heap.add(0); + }); + + describe('#peek', () => { + it('should get min', () => { + expect(heap.peek()).toEqual(0); + }); + }); + + describe('#remove', () => { + it('should get min', () => { + expect(heap.remove()).toEqual(0); + expect(heap.remove()).toEqual(1); + expect(heap.remove()).toEqual(2); + expect(heap.remove()).toEqual(3); + expect(heap.size).toBe(0); + }); + }); + }); + }); +}); + +[ + [Heap, (a, b) => b - a], + [PriorityQueue, [], (a, b) => b - a], + [MaxHeap], +].forEach(([DS, ...arg]) => { + describe('Max-Heap (Priority Queue)', () => { + let heap; + + beforeEach(() => { + heap = new DS(...arg); + }); + + describe('#contructor', () => { + it('should initialize', () => { + expect(heap).not.toBe(undefined); + }); + }); + + describe('#add', () => { + it('should add an element', () => { + expect(heap.add(1)).toBe(undefined); + expect(heap.array).toEqual([1]); + expect(heap.size).toBe(1); + }); + + it('should keep things in order', () => { + heap.add(1); + expect(heap.array[0]).toEqual(1); + heap.add(2); + expect(heap.array[0]).toEqual(2); + heap.add(3); + expect(heap.array[0]).toEqual(3); + expect(heap.size).toEqual(3); + }); + }); + + describe('#remove', () => { + it('should work', () => { + heap.add(1); + heap.add(0); + expect(heap.remove()).toBe(1); + expect(heap.size).toBe(1); + expect(heap.array).toEqual([0]); + }); + + it('should work with duplicates', () => { + heap.add(3); + heap.add(2); + heap.add(3); + heap.add(1); + heap.add(2); + heap.add(4); + heap.add(5); + heap.add(5); + heap.add(6); + + expect(heap.remove()).toEqual(6); + expect(heap.remove()).toEqual(5); + expect(heap.remove()).toEqual(5); + expect(heap.remove()).toEqual(4); + }); + }); + + describe('when has elements', () => { + beforeEach(() => { + heap.add(1); + heap.add(2); + heap.add(3); + heap.add(0); + }); + + describe('#peek', () => { + it('should get min', () => { + expect(heap.peek()).toEqual(3); + }); + }); + + describe('#remove', () => { + it('should get min when duplicates', () => { + expect(heap.remove()).toEqual(3); + expect(heap.remove()).toEqual(2); + expect(heap.remove()).toEqual(1); + expect(heap.remove()).toEqual(0); + expect(heap.size).toBe(0); + }); + }); + }); + }); +}); diff --git a/docs/content/ds/data-structures/heaps/max-heap.js b/docs/content/ds/data-structures/heaps/max-heap.js new file mode 100644 index 0000000000..46dbb89e8d --- /dev/null +++ b/docs/content/ds/data-structures/heaps/max-heap.js @@ -0,0 +1,8 @@ +const Heap = require('./heap'); + +class MaxHeap extends Heap { + constructor() { + super((a, b) => b - a); + } +} +module.exports = MaxHeap; diff --git a/docs/content/ds/data-structures/heaps/median-heap.js b/docs/content/ds/data-structures/heaps/median-heap.js new file mode 100644 index 0000000000..e829596359 --- /dev/null +++ b/docs/content/ds/data-structures/heaps/median-heap.js @@ -0,0 +1,75 @@ +const Heap = require('./heap'); + +/** + * Median Heap using one MaxHeap and one MinHeap. + * + * Each heap contains about one half of the data. + * Every element in the min-heap is greater or equal to the median, + * and every element in the max-heap is less or equal to the median. + * + * @author Adrian Mejia + */ +class MedianHeap { + constructor() { + this.min = new Heap((a, b) => a - b); + this.max = new Heap((a, b) => b - a); + } + + /** + * Add a value to the heap + * @runtime O(log n) + * @param {any} value + */ + add(value) { + if (value > this.findMedian()) { + // If the new element is greater than the current median, it goes to the min-heap. + this.min.add(value); + } else { + // If it is less than the current median, it goes to the max heap. + this.max.add(value); + } + + // rebalance if the sizes of the heaps differ by more than one element + if (Math.abs(this.min.size - this.max.size) > 1) { + // extract the min/max from the heap with more elements and insert it into the other heap. + if (this.min.size > this.max.size) { + this.max.add(this.min.remove()); + } else { + this.min.add(this.max.remove()); + } + } + } + + /** + * Find median + * @runtime O(1) + */ + findMedian() { + let median; + + if (this.max.size === this.min.size) { + // When both heaps contain the same number of elements, + // the total number of elements is even. + // The median is the mean of the two middle elements. + median = (this.max.peek() + this.min.peek()) / 2; + } else if (this.max.size > this.min.size) { + // when the max-heap contains one more element than the min-heap, + // the median is in the top of the max-heap. + median = this.max.peek(); + } else { + // When the min-heap contains one more element than the max-heap, + // the median is in the top of the min-heap. + median = this.min.peek(); + } + return median; + } + + /** + * Return size of the heap. + */ + get size() { + return this.min.size + this.max.size; + } +} + +module.exports = MedianHeap; diff --git a/docs/content/ds/data-structures/heaps/median-heap.spec.js b/docs/content/ds/data-structures/heaps/median-heap.spec.js new file mode 100644 index 0000000000..4f7a8a6b1e --- /dev/null +++ b/docs/content/ds/data-structures/heaps/median-heap.spec.js @@ -0,0 +1,61 @@ +const MedianHeap = require('./median-heap'); + +describe('Median Heap', () => { + let medianHeap; + + beforeEach(() => { + medianHeap = new MedianHeap(); + }); + + describe('#add', () => { + it('should work', () => { + expect(medianHeap.add(1)).toEqual(undefined); + expect(medianHeap.size).toEqual(1); + }); + + it('should work with 2 additions', () => { + expect(medianHeap.add(1)).toEqual(undefined); + expect(medianHeap.add(1)).toEqual(undefined); + expect(medianHeap.size).toEqual(2); + }); + }); + + describe('#findMedian', () => { + it('should work', () => { + medianHeap.add(5); + expect(medianHeap.findMedian()).toEqual(5); + medianHeap.add(15); + expect(medianHeap.findMedian()).toEqual(10); + medianHeap.add(10); + expect(medianHeap.findMedian()).toEqual(10); + }); + + it('should work with even numbers', () => { + const values = [5, 15, 1, 3]; + const medians = values.map((v) => { + medianHeap.add(v); + return medianHeap.findMedian(); + }); + expect(medians).toEqual([5, 10, 5, 4]); + }); + + it('should work with odd numbers', () => { + const values = [2, 4, 7, 1, 5, 3]; + const medians = values.map((v) => { + medianHeap.add(v); + return medianHeap.findMedian(); + }); + expect(medians).toEqual([2, 3, 4, 3, 4, 3.5]); + }); + + it('should work with negative numbers', () => { + const values = [-1, -2, -3]; + const expected = [-1, -1.5, -2]; + const medians = values.map((v) => { + medianHeap.add(v); + return medianHeap.findMedian(); + }); + expect(medians).toEqual(expected); + }); + }); +}); diff --git a/docs/content/ds/data-structures/heaps/min-heap.js b/docs/content/ds/data-structures/heaps/min-heap.js new file mode 100644 index 0000000000..0b77c76072 --- /dev/null +++ b/docs/content/ds/data-structures/heaps/min-heap.js @@ -0,0 +1,8 @@ +const Heap = require('./heap'); + +class MinHeap extends Heap { + constructor() { + super((a, b) => a - b); + } +} +module.exports = MinHeap; diff --git a/docs/content/ds/data-structures/heaps/priority-queue.js b/docs/content/ds/data-structures/heaps/priority-queue.js new file mode 100644 index 0000000000..b81772e86e --- /dev/null +++ b/docs/content/ds/data-structures/heaps/priority-queue.js @@ -0,0 +1,28 @@ +const Heap = require('./heap'); + +class PriorityQueue extends Heap { + constructor(iterable = [], comparator = (a, b) => a[0] - b[0]) { + super(comparator); + Array.from(iterable).forEach((el) => this.add(el)); + } + + /** + * Add data to the Queue with Priority + * @param {[number, any]|any} value - Pair with [priority, value] + * any object as value is also possible if a custom comparator is passed in. + * @returns {void} + */ + enqueue(value) { + super.add(value); + } + + /** + * Remove from the queue the element with the highest priority. + * @returns {[number, any]|any} + */ + dequeue() { + return super.remove(); + } +} + +module.exports = PriorityQueue; diff --git a/docs/content/ds/data-structures/heaps/priority-queue.spec.js b/docs/content/ds/data-structures/heaps/priority-queue.spec.js new file mode 100644 index 0000000000..860480da8b --- /dev/null +++ b/docs/content/ds/data-structures/heaps/priority-queue.spec.js @@ -0,0 +1,75 @@ +const { PriorityQueue } = require('../..'); + +describe('Priorty Queue (as MinHeap default)', () => { + const num = 1; + const obj = { a: 1, b: 2 }; + let pq; + + describe('with default contructor', () => { + beforeEach(() => { + pq = new PriorityQueue(); + }); + + describe('.enqueue', () => { + it('should enqueue [priority, element]', () => { + pq.enqueue([Infinity, 2]); + pq.enqueue([0, 1]); + pq.enqueue([100, { a: 1, b: 2 }]); + expect(pq.size).toEqual(3); + expect(pq.peek()).toEqual([0, 1]); + }); + }); + + describe('.dequeue', () => { + it('should enqueue and dequeue elements on priority order', () => { + pq.enqueue([100, obj]); + pq.enqueue([Infinity, 2]); + pq.enqueue([0, num]); + + expect(pq.dequeue()).toEqual([0, num]); + expect(pq.size).toEqual(2); + expect(pq.dequeue()).toEqual([100, obj]); + expect(pq.dequeue()).toEqual([Infinity, 2]); + expect(pq.size).toEqual(0); + }); + + it('should handle case when priorty was forgotten', () => { + expect(() => pq.enqueue({ a: 100 })).not.toThrow(); + expect(() => pq.enqueue({ b: 200 })).toThrow(); + }); + }); + }); + + describe('with default values', () => { + it('should add values on creation', () => { + pq = new PriorityQueue([[100, obj], [Infinity, 2], [0, num]]); + expect(pq.size).toEqual(3); + expect(pq.peek()).toEqual([0, num]); + expect(pq.dequeue()).toEqual([0, num]); + expect(pq.size).toEqual(2); + }); + }); + + describe('with custom comparator', () => { + const alice = { name: 'Alice', grade: 80, assistance: 1 }; + const bob = { name: 'Bob', grade: 93, assistance: 0.7 }; + const ana = { name: 'Ana', grade: 98, assistance: 0.8 }; + + it('should become MaxPriortyQueue and compare objects', () => { + pq = new PriorityQueue([], (a, b) => b.grade * b.assistance - a.grade * a.assistance); + pq.enqueue(alice); + pq.enqueue(ana); + pq.enqueue(bob); + expect(pq.size).toEqual(3); + expect(pq.dequeue()).toEqual(alice); + expect(pq.dequeue()).toEqual(ana); + expect(pq.dequeue()).toEqual(bob); + }); + + it('should handle errors', () => { + pq = new PriorityQueue([], (a, b) => b.grade - a.grade); + expect(() => pq.enqueue(alice)).not.toThrow(); + expect(() => pq.enqueue({ name: 'Oops', error: 98 })).toThrow(); + }); + }); +}); diff --git a/docs/content/ds/data-structures/linked-lists/README.adoc b/docs/content/ds/data-structures/linked-lists/README.adoc new file mode 100644 index 0000000000..62290fddf9 --- /dev/null +++ b/docs/content/ds/data-structures/linked-lists/README.adoc @@ -0,0 +1 @@ +include::../../../book/content/part02/linked-list.asc[] diff --git a/docs/content/ds/data-structures/linked-lists/linked-list-1.js b/docs/content/ds/data-structures/linked-lists/linked-list-1.js new file mode 100644 index 0000000000..458631cce2 --- /dev/null +++ b/docs/content/ds/data-structures/linked-lists/linked-list-1.js @@ -0,0 +1,116 @@ +const Node = require('./node'); + +/** + * Singly linked list + */ +class LinkedList { + constructor() { + this.first = null; // head/root element + } + + /** + * Adds element to the end of the list (tail). Similar to Array.push + * Runtime: O(n) + * @param {any} value + */ + addLast(value) { + const node = new Node(value); + + if (this.first) { + let currentNode = this.first; + while (currentNode && currentNode.next) { + currentNode = currentNode.next; + } + currentNode.next = node; + } else { + this.first = node; + } + } + + /** + * Removes element from the start of the list (head/root). similar Array.shift + * Runtime: O(1) + */ + removeFirst() { + const first = this.first; + + if (first) { + this.first = first.next; + return first.value; + } + } + + /** + * Adds element to the begining of the list. Similar to Array.unshift + * Runtime: O(1) + * @param {any} value + */ + addFirst(value) { + const node = new Node(value); + node.next = this.first; + this.first = node; + } + + /** + * Removes element to the end of the list + * similar to Array.pop + * Runtime: O(n) + */ + removeLast() { + let current = this.first; + let target; + + if (current && current.next) { + while (current && current.next && current.next.next) { + current = current.next; + } + target = current.next; + current.next = null; + } else { + this.first = null; + target = current; + } + + if (target) { + return target.value; + } + } + + /** + * Find first occurence of the element matching the value + * return index or undefined + * Runtime: O(n) + * @param {any} value + */ + contains(value) { + for (let current = this.first, index = 0; current; index++, current = current.next) { + if (current.value === value) { + return index; + } + } + } + + /** + * Remove the nth element from the list. Starting with 0 + * Returns value if found or undefined if it was not found + * Runtime: O(n) + * @param {any} nth + */ + removeAt(nth) { + if (nth === 0) { + return this.removeFirst(); + } + + for (let current = this.first, index = 0; current; index++, current = current.next) { + if (index === nth) { + if (!current.next) { // if it doesn't have next it means that it is the last + return this.removeLast(); + } + current.previous = current.next; + return current.value; + } + } + } +} + +module.exports = LinkedList; diff --git a/docs/content/ds/data-structures/linked-lists/linked-list-2.js b/docs/content/ds/data-structures/linked-lists/linked-list-2.js new file mode 100644 index 0000000000..7acc8e9794 --- /dev/null +++ b/docs/content/ds/data-structures/linked-lists/linked-list-2.js @@ -0,0 +1,130 @@ +const Node = require('./node'); + +/** + * Singly linked list with `last` element reference + */ +class LinkedList { + constructor() { + this.first = null; // head/root element + this.last = null; + } + + /** + * Adds element to the end of the list (tail). Similar to Array.push + * Using the element last reference instead of finding last, we can reduce the runtime from O(n) to O(1) + * Runtime: O(1) + * @param {any} value + */ + addLast(value) { + const node = new Node(value); + + if (this.first) { + const currentNode = this.first; + this.last.next = node; + this.last = node; + } else { + this.first = node; + this.last = node; + } + } + + /** + * Removes element from the start of the list (head/root). similar Array.shift + * Runtime: O(1) + */ + removeFirst() { + const first = this.first; + + if (first) { + this.first = first.next; + return first.value; + } + this.last = null; + } + + /** + * Adds element to the begining of the list. Similar to Array.unshift + * Runtime: O(1) + * @param {any} value + */ + addFirst(value) { + const node = new Node(value); + node.next = this.first; + if (!this.first) { + this.last = node; + } + this.first = node; + } + + /** + * Removes element to the end of the list + * similar to Array.pop + * Runtime: O(n) + */ + removeLast() { + let current = this.first; + let target; + + if (current && current.next) { + while (current && current.next && current.next.next) { + current = current.next; + } + this.last = current; + target = current.next; + current.next = null; + } else { + this.first = null; + this.last = null; + target = current; + } + + if (target) { + return target.value; + } + } + + /** + * Find first occurence of the element matching the value + * return index or undefined + * Runtime: O(n) + * @param {any} value + */ + contains(value) { + for (let current = this.first, index = 0; current; index++, current = current.next) { + if (current.value === value) { + return index; + } + } + } + + /** + * Remove the nth element from the list. Starting with 0 + * Returns value if found or undefined if it was not found + * Runtime: O(n) + * @param {any} nth + */ + removeAt(nth) { + if (nth === 0) { + return this.removeFirst(); + } + + for (let current = this.first, index = 0; current; index++, current = current.next) { + if (index === nth) { + if (!current.next) { // if it doesn't have next it means that it is the last + return this.removeLast(); + } + current.previous = current.next; + this.size--; + return current.value; + } + } + } +} + +// Aliases +LinkedList.prototype.add = LinkedList.prototype.push = LinkedList.prototype.addLast; +LinkedList.prototype.remove = LinkedList.prototype.pop = LinkedList.prototype.removeLast; +LinkedList.prototype.unshift = LinkedList.prototype.addFirst; +LinkedList.prototype.shift = LinkedList.prototype.removeFirst; + +module.exports = LinkedList; diff --git a/docs/content/ds/data-structures/linked-lists/linked-list.js b/docs/content/ds/data-structures/linked-lists/linked-list.js new file mode 100644 index 0000000000..987069ca81 --- /dev/null +++ b/docs/content/ds/data-structures/linked-lists/linked-list.js @@ -0,0 +1,330 @@ +const util = require('util'); +const Node = require('./node'); // Doubly + +// tag::constructor[] +/** + * Doubly linked list that keeps track of + * the last and first element + */ +class LinkedList { + constructor( + iterable = [], + // end::constructor[] + ListNode = Node, // Node class (e.g. singly, doubly, multilevel) + // tag::constructor[] + ) { + this.first = null; // head/root element + this.last = null; // last element of the list + this.size = 0; // total number of elements in the list + // end::constructor[] + this.ListNode = ListNode; // ListNode class + // tag::constructor[] + + Array.from(iterable, (i) => this.addLast(i)); + } + // end::constructor[] + + // tag::addFirst[] + /** + * Adds element to the begining of the list. Similar to Array.unshift + * Runtime: O(1) + * @param {Node} value + */ + addFirst(value) { + const newNode = new this.ListNode(value); + + newNode.next = this.first; + + if (this.first) { // check if first node exists (list not empty) + this.first.previous = newNode; // <1> + } else { // if list is empty, first & last will point to newNode. + this.last = newNode; + } + + this.first = newNode; // update head + this.size += 1; + + return newNode; + } + // end::addFirst[] + + // tag::addLast[] + /** + * Adds element to the end of the list (tail). Similar to Array.push + * Using the element last reference instead of navigating through the list, + * we can reduced from linear to a constant runtime. + * Runtime: O(1) + * @param {any} value node's value + * @returns {Node} newly created node + */ + addLast(value) { + const newNode = new Node(value); + + if (this.first) { // check if first node exists (list not empty) + newNode.previous = this.last; + this.last.next = newNode; + this.last = newNode; + } else { // if list is empty, first & last will point to newNode. + this.first = newNode; + this.last = newNode; + } + + this.size += 1; + + return newNode; + } + // end::addLast[] + + // tag::addMiddle[] + /** + * Insert new element at the given position (index) + * + * Runtime: O(n) + * + * @param {any} value new node's value + * @param {Number} position position to insert element + * @returns {Node|undefined} new node or 'undefined' if the index is out of bound. + */ + addAt(value, position = 0) { + if (position === 0) return this.addFirst(value); // <1> + if (position === this.size) return this.addLast(value); // <2> + + // Adding element in the middle + const current = this.findBy({ index: position }).node; + if (!current) return undefined; // out of bound index + + const newNode = new Node(value); // <3> + newNode.previous = current.previous; // <4> + newNode.next = current; // <5> + current.previous.next = newNode; // <6> + current.previous = newNode; // <7> + this.size += 1; + return newNode; + } + // end::addMiddle[] + + // tag::searchByValue[] + /** + * @deprecated use findBy + * Search by value. It finds first occurrence of + * the position of element matching the value. + * Similar to Array.indexOf. + * + * Runtime: O(n) + * + * @example: assuming a linked list with: a -> b -> c + * linkedList.getIndexByValue('b') // ↪️ 1 + * linkedList.getIndexByValue('z') // ↪️ undefined + * @param {any} value + * @returns {number} return index or undefined + */ + getIndexByValue(value) { + return this.findBy({ value }).index; + } + // end::searchByValue[] + + // tag::searchByIndex[] + /** + * @deprecated use findBy directly + * Search by index + * Runtime: O(n) + * @example: assuming a linked list with: a -> b -> c + * linkedList.get(1) // ↪️ 'b' + * linkedList.get(40) // ↪️ undefined + * @param {Number} index position of the element + * @returns {Node|undefined} element at the specified position in + * this list or undefined if was not found. + */ + get(index = 0) { + return this.findBy({ index }).node; + } + // end::searchByIndex[] + + // tag::find[] + /** + * Find by index or by value, whichever happens first. + * Runtime: O(n) + * @example + * this.findBy({ index: 10 }).node; // node at index 10. + * this.findBy({ value: 10 }).node; // node with value 10. + * this.findBy({ value: 10 }).index; // node's index with value 10. + * + * @param {Object} params - The search params + * @param {number} params.index - The index/position to search for. + * @param {any} params.value - The value to search for. + * @returns {{node: any, index: number}} + */ + findBy({ value, index = Infinity } = {}) { + for (let current = this.first, position = 0; // <1> + current && position <= index; // <2> + position += 1, current = current.next) { // <3> + if (position === index || value === current.value) { // <4> + return { node: current, index: position }; // <5> + } + } + return {}; // not found + } + // end::find[] + + + // tag::removeFirst[] + /** + * Removes element from the start of the list (head/root). + * Similar to Array.shift(). + * Runtime: O(1) + * @returns {any} the first element's value which was removed. + */ + removeFirst() { + if (!this.first) return null; // Check if list is already empty. + const head = this.first; + + this.first = head.next; // move first pointer to the next element. + if (this.first) { + this.first.previous = null; + } else { // if list has size zero, then we need to null out last. + this.last = null; + } + this.size -= 1; + return head.value; + } + // end::removeFirst[] + + // tag::removeLast[] + /** + * Removes element to the end of the list. + * Similar to Array.pop(). + * Runtime: O(1) + * @returns {any} the last element's value which was removed + */ + removeLast() { + if (!this.last) return null; // Check if list is already empty. + const tail = this.last; + + this.last = tail.previous; + if (this.last) { + this.last.next = null; + } else { // if list has size zero, then we need to null out first. + this.first = null; + } + this.size -= 1; + return tail.value; + } + // end::removeLast[] + + // tag::removeByPosition[] + /** + * Removes the element at the given position (index) in this list. + * Runtime: O(n) + * @param {any} position + * @returns {any} the element's value at the specified position that was removed. + */ + removeByPosition(position = 0) { + if (position === 0) return this.removeFirst(); + if (position === this.size - 1) return this.removeLast(); + const current = this.findBy({ index: position }).node; + if (!current) return null; + current.previous.next = current.next; + current.next.previous = current.previous; + this.size -= 1; + return current && current.value; + } + // end::removeByPosition[] + + /** + * Remove element by Node + * O(1) + */ + removeByNode(node) { + if (!node) { return null; } + if (node === this.first) { + return this.removeFirst(); + } + if (node === this.last) { + return this.removeLast(); + } + node.previous.next = node.next; + node.next.previous = node.previous; + this.size -= 1; + + return node.value; + } + + /** + * Iterate through the list yield on each node + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators#User-defined_iterables + */ + * [Symbol.iterator]() { + for (let node = this.first; node; node = node.next) { + yield node; + } + } + + toString() { + const parts = [...this]; // see [Symbol.iterator]() + return parts.map((n) => util.inspect(n.value)).join(' -> '); + } + + /** + * Alias for size + */ + get length() { + return this.size; + } + + /** + * @deprecated use findBy + * Iterate through the list until callback returns a truthy value + * @example see #get and #getIndexByValue + * @param {Function} callback evaluates current node and index. + * If any value other than undefined it's returned it will stop the search. + * @returns {any} callbacks's return value or undefined + */ + find(callback) { + for (let current = this.first, position = 0; // <1> + current; // <2> + position += 1, current = current.next) { // <3> + const result = callback(current, position); // <4> + + if (result !== undefined) { + return result; // <5> + } + } + return undefined; // not found + } + + /** + * @deprecated use removeByNode or removeByPosition + * Removes the first occurrence of the specified elementt + * from this list, if it is present. + * Runtime: O(n) + * @param {any} callbackOrIndex callback or position index to remove + */ + remove(callbackOrIndex) { + if (typeof callbackOrIndex !== 'function') { + return this.removeByPosition(parseInt(callbackOrIndex, 10) || 0); + } + + // find desired position to remove using #find + const position = this.find((node, index) => { + if (callbackOrIndex(node, index)) { + return index; + } + return undefined; + }); + + if (position !== undefined) { // zero-based position. + return this.removeByPosition(position); + } + + return false; + } +} + +// Aliases +LinkedList.prototype.push = LinkedList.prototype.addLast; +LinkedList.prototype.pop = LinkedList.prototype.removeLast; +LinkedList.prototype.unshift = LinkedList.prototype.addFirst; +LinkedList.prototype.shift = LinkedList.prototype.removeFirst; +LinkedList.prototype.search = LinkedList.prototype.contains; + +module.exports = LinkedList; diff --git a/docs/content/ds/data-structures/linked-lists/linked-list.spec.js b/docs/content/ds/data-structures/linked-lists/linked-list.spec.js new file mode 100644 index 0000000000..8285e31643 --- /dev/null +++ b/docs/content/ds/data-structures/linked-lists/linked-list.spec.js @@ -0,0 +1,452 @@ +const { LinkedList } = require('../../index'); + +describe('LinkedList Test', () => { + let linkedList; + + beforeEach(() => { + linkedList = new LinkedList(); + }); + + describe('#addLast', () => { + it('should add element to end/tail of the list', () => { + linkedList.addLast('a'); + expect(linkedList.first.value).toBe('a'); + expect(linkedList.last.value).toBe('a'); + }); + + it('should link values', () => { + linkedList.addLast('a'); + linkedList.addLast('b'); + linkedList.addLast('c'); + + expect(linkedList.last.value).toBe('c'); + expect(linkedList.first.value).toBe('a'); + expect(linkedList.first.next.value).toBe('b'); + expect(linkedList.first.next.next.value).toBe('c'); + }); + }); + + describe('#removeFirst', () => { + beforeEach(() => { + linkedList.addLast('a'); + linkedList.addLast('b'); + }); + + it('should remove with only one element', () => { + linkedList = new LinkedList(); + linkedList.addLast('a'); + + expect(linkedList.removeFirst()).toBe('a'); + + expect(linkedList.size).toBe(0); + expect(linkedList.first).toBe(null); + expect(linkedList.last).toBe(null); + }); + + it('should remove first item: a', () => { + expect(linkedList.removeFirst()).toBe('a'); + expect(linkedList.first.value).toBe('b'); + expect(linkedList.first.next).toBe(null); + + expect(linkedList.last.value).toBe('b'); + + expect(linkedList.removeFirst()).toBe('b'); + expect(linkedList.removeFirst()).toBe(null); + + expect(linkedList.first).toBe(null); + expect(linkedList.last).toBe(null); + expect(linkedList.size).toBe(0); + }); + }); + + describe('#addFirst', () => { + it('add 1 element to the head/root of the list', () => { + linkedList.addFirst('a'); + expect(linkedList.first.value).toBe('a'); + expect(linkedList.last.value).toBe('a'); + }); + + it('add 2 elements to the head/root of the list', () => { + linkedList.addFirst('a'); + linkedList.addFirst('b'); + expect(linkedList.first.value).toBe('b'); + expect(linkedList.first.next.value).toBe('a'); + + expect(linkedList.last.value).toBe('a'); + }); + }); + + describe('#removeLast', () => { + beforeEach(() => { + linkedList.addLast('a'); + linkedList.addLast('b'); + linkedList.addLast('c'); + }); + + it('should remove first item', () => { + expect(linkedList.size).toBe(3); + expect(linkedList.first.value).toBe('a'); + expect(linkedList.last.value).toBe('c'); + + expect(linkedList.removeLast()).toBe('c'); + expect(linkedList.last.value).toBe('b'); + expect(linkedList.first.value).toBe('a'); + expect(linkedList.size).toBe(2); + + expect(linkedList.removeLast()).toBe('b'); + expect(linkedList.last.value).toBe('a'); + expect(linkedList.first.value).toBe('a'); + expect(linkedList.first.next).toBe(null); + expect(linkedList.size).toBe(1); + + expect(linkedList.removeLast()).toBe('a'); + expect(linkedList.first).toBe(null); + expect(linkedList.last).toBe(null); + expect(linkedList.removeLast()).toBe(null); + expect(linkedList.size).toBe(0); + + expect(linkedList.first).toBe(null); + expect(linkedList.last).toBe(null); + }); + }); + + describe('with elements', () => { + beforeEach(() => { + linkedList.addLast(0); + linkedList.addLast('found'); + }); + + describe('#length', () => { + it('should have length property', () => { + expect(linkedList.length).toBe(2); + }); + }); + + describe('#getIndexByValue', () => { + it('should find element index', () => { + expect(linkedList.getIndexByValue(0)).toBe(0); + expect(linkedList.getIndexByValue('found')).toBe(1); + }); + + it('should return undefined', () => { + expect(linkedList.getIndexByValue('hola')).toBe(undefined); + }); + }); + + describe('#get', () => { + it('should get the element at index 1', () => { + expect(linkedList.get(1).value).toBe('found'); + expect(linkedList.get(0).value).toBe(0); + }); + + it('should return undefined when index is out of bound', () => { + expect(linkedList.get(1000)).toBe(undefined); + }); + }); + + describe('#remove', () => { + it('should remove element and return value', () => { + expect(linkedList.size).toBe(2); + expect(linkedList.remove(1)).toBe('found'); + expect(linkedList.size).toBe(1); + + expect(linkedList.remove(0)).toBe(0); + expect(linkedList.size).toBe(0); + }); + + it('should return undefined if not found', () => { + expect(linkedList.remove(2)).toBe(null); + expect(linkedList.remove(-2)).toBe(null); + }); + + it('should update size, last and first', () => { + expect(linkedList.remove(0)).toBe(0); + expect(linkedList.size).toBe(1); + expect(linkedList.remove(0)).toBe('found'); + expect(linkedList.size).toBe(0); + expect(linkedList.remove(0)).toBe(null); + expect(linkedList.size).toBe(0); + expect(linkedList.first).toBe(null); + expect(linkedList.last).toBe(null); + }); + }); + + describe('#addAt', () => { + it('should insert at the beginning', () => { + const newNode = linkedList.addAt('first', 0); + expect(newNode.value).toBe('first'); + expect(newNode.next.value).toBe(0); + expect(linkedList.size).toBe(3); + expect(linkedList.first).toBe(newNode); + }); + + it('should insert at the middle', () => { + const newNode = linkedList.addAt('middle', 1); + expect(newNode.value).toBe('middle'); + // checking the 4 surrounding links were updated + expect(newNode.next.value).toBe('found'); + expect(newNode.previous.value).toBe(0); + expect(linkedList.get(0).next.value).toBe('middle'); + expect(linkedList.get(2).previous.value).toBe('middle'); + + expect(linkedList.size).toBe(3); + expect(linkedList.first.value).toBe(0); + }); + + it('should insert at the end', () => { + const newNode = linkedList.addAt('end', 2); + expect(newNode.value).toBe('end'); + expect(newNode.next).toBe(null); + expect(newNode.previous.value).toBe('found'); + expect(linkedList.size).toBe(3); + expect(linkedList.last.value).toBe('end'); + }); + + it('should not insert out of bound', () => { + const newNode = linkedList.addAt('out-of-bound', 3); + expect(newNode).toBe(undefined); + expect(linkedList.last.value).toBe('found'); + expect(linkedList.size).toBe(2); + }); + }); + + describe('#removeByPosition', () => { + it('should remove last element', () => { + expect(linkedList.length).toBe(2); + expect(linkedList.removeByPosition(1)).toBe('found'); + expect(linkedList.length).toBe(1); + }); + + it('should remove first element', () => { + expect(linkedList.length).toBe(2); + expect(linkedList.removeByPosition(0)).toBe(0); + expect(linkedList.length).toBe(1); + }); + }); + + describe('#removeByNode', () => { + it('should remove first node', () => { + const node = linkedList.first; + linkedList.removeByNode(node); + expect(linkedList.first.value).toEqual('found'); + expect(linkedList.first.previous).toEqual(null); + expect(linkedList.size).toEqual(1); + }); + + it('should remove last node', () => { + const node = linkedList.last; + linkedList.removeByNode(node); + expect(linkedList.first.value).toEqual(0); + expect(linkedList.first.next).toEqual(null); + expect(linkedList.size).toEqual(1); + }); + + it('should remove from the middle', () => { + const node = linkedList.first; + linkedList.addLast('last'); + linkedList.removeByNode(node); + expect(linkedList.first.next).toEqual(linkedList.last); + expect(linkedList.last.previous).toEqual(linkedList.first); + expect(linkedList.size).toEqual(2); + }); + }); + }); + + describe('Doubly Linked List and aliases', () => { + describe('#addLast', () => { + beforeEach(() => { + linkedList.addLast('a'); + linkedList.addLast('b'); + linkedList.addLast('c'); + linkedList.addLast('d'); + }); + + it('should have a reference to previous node on the last element', () => { + // expect(linkedList.last.value).toBe('d'); + expect(linkedList.last.previous.value).toBe('c'); + expect(linkedList.size).toBe(4); + }); + + it('should have a reference to previous node on the first element', () => { + expect(linkedList.first.previous).toBe(null); + }); + + it('should have a reference to previous node on the 2nd element', () => { + // expect(linkedList.first.next.value).toBe('b'); + expect(linkedList.first.next.previous.value).toBe('a'); + }); + }); + + describe('#removeFirst', () => { + beforeEach(() => { + linkedList.addLast('a'); + linkedList.addLast('b'); + linkedList.addLast('c'); + linkedList.addLast('d'); + + linkedList.removeFirst(); + }); + + it('should update reference when removing first', () => { + // b -> c -> d + expect(linkedList.first.next.previous.value).toBe('b'); + // expect(linkedList.first.value).toBe('b'); + expect(linkedList.first.previous).toBe(null); + expect(linkedList.size).toBe(3); + }); + + it('should keep previous updated when deleting everything', () => { + linkedList.removeFirst(); + expect(linkedList.size).toBe(2); + // c -> d + // expect(linkedList.first.value).toBe('c'); + expect(linkedList.first.previous).toBe(null); + expect(linkedList.first.next.previous.value).toBe('c'); + + linkedList.removeFirst(); + // d + expect(linkedList.first.value).toBe('d'); + expect(linkedList.first.previous).toBe(null); + expect(linkedList.first.next).toBe(null); + + linkedList.removeFirst(); + expect(linkedList.first).toBe(null); + + linkedList.removeFirst(); + expect(linkedList.size).toBe(0); + }); + }); + + describe('#addFirst', () => { + beforeEach(() => { + linkedList.addFirst('b'); + linkedList.addFirst('a'); + }); + + it('should have a previous reference', () => { + expect(linkedList.last.value).toBe('b'); + expect(linkedList.last.previous.value).toBe('a'); + expect(linkedList.first.previous).toBe(null); + expect(linkedList.size).toBe(2); + }); + }); + + describe('#removeLast (and aliases)', () => { + beforeEach(() => { + // a -> b -> c + linkedList.push('b'); + linkedList.unshift('a'); + linkedList.addLast('c'); + }); + + it('should do nothing with previous', () => { + expect(linkedList.size).toBe(3); + expect(linkedList.removeLast()).toBe('c'); + expect(linkedList.pop()).toBe('b'); + expect(linkedList.remove()).toBe('a'); + expect(linkedList.shift()).toBe(null); + linkedList.removeLast(); + expect(linkedList.size).toBe(0); + }); + }); + + describe('#remove with callback', () => { + const a = { k: 1, v: 'a' }; + const b = { k: 2, v: 'b' }; + const c = { k: 3, v: 'c' }; + + beforeEach(() => { + // a -> b -> c + linkedList.push(b); + linkedList.unshift(a); + linkedList.addLast(c); + }); + + it('should remove head', () => { + linkedList.remove((node) => { + if (node.value.v === 'a') { + return true; + } + return false; + }); + expect(linkedList.first.value).toMatchObject(b); + expect(linkedList.first.next.value).toMatchObject(c); + expect(linkedList.last.value).toMatchObject(c); + expect(linkedList.size).toBe(2); + }); + + it('should remove middle', () => { + linkedList.remove((node) => { + if (node.value.v === 'b') { + return true; + } + return false; + }); + expect(linkedList.size).toBe(2); + expect(linkedList.first.value).toMatchObject(a); + expect(linkedList.first.next.value).toMatchObject(c); + expect(linkedList.first.previous).toBe(null); + expect(linkedList.last.value).toMatchObject(c); + expect(linkedList.last.previous.value).toMatchObject(a); + }); + + it('should remove last', () => { + linkedList.remove((node) => { + if (node.value.v === 'c') { + return true; + } + return false; + }); + expect(linkedList.size).toBe(2); + expect(linkedList.first.value).toMatchObject(a); + expect(linkedList.first.next.value).toMatchObject(b); + expect(linkedList.last.value).toMatchObject(b); + }); + + it('should remove none if not found', () => { + linkedList.remove((node) => { + if (node.value.v === 'z') { + return true; + } + return false; + }); + expect(linkedList.size).toBe(3); + expect(linkedList.first.value).toMatchObject(a); + expect(linkedList.first.next.value).toMatchObject(b); + expect(linkedList.last.value).toMatchObject(c); + }); + }); + + describe('#toString', () => { + beforeEach(() => { + linkedList.addLast('a'); + linkedList.addLast(2); + linkedList.addLast('c'); + linkedList.addLast({ k: 4, v: 'd' }); + }); + + it('get string', () => { + expect(linkedList.toString()).toBe("'a' -> 2 -> 'c' -> { k: 4, v: 'd' }"); + }); + }); + + + describe('iterator', () => { + let a; + let b; + let c; + let d; + beforeEach(() => { + a = linkedList.addLast('a'); + b = linkedList.addLast('b'); + c = linkedList.addLast('c'); + d = linkedList.addLast('d'); + }); + + it('should convert to array of nodes', () => { + expect([...linkedList]).toEqual([a, b, c, d]); + expect(Array.from(linkedList)).toEqual([a, b, c, d]); + }); + }); + }); +}); diff --git a/docs/content/ds/data-structures/linked-lists/node-1.js b/docs/content/ds/data-structures/linked-lists/node-1.js new file mode 100644 index 0000000000..35a4624a19 --- /dev/null +++ b/docs/content/ds/data-structures/linked-lists/node-1.js @@ -0,0 +1,11 @@ +/** + * Simple node of a Linked List + */ +class Node { + constructor(value) { + this.value = value; + this.next = null; + } +} + +module.exports = Node; diff --git a/docs/content/ds/data-structures/linked-lists/node.js b/docs/content/ds/data-structures/linked-lists/node.js new file mode 100644 index 0000000000..0d3147f57b --- /dev/null +++ b/docs/content/ds/data-structures/linked-lists/node.js @@ -0,0 +1,18 @@ +// tag::snippet[] +/** + * Linked List Node + */ +// tag::singly[] +class Node { + constructor(value = null) { + this.value = value; + this.next = null; + // end::singly[] + this.previous = null; // if doubly linked list + // tag::singly[] + } +} +// end::singly[] +// end::snippet[] + +module.exports = Node; diff --git a/docs/content/ds/data-structures/maps/README.adoc b/docs/content/ds/data-structures/maps/README.adoc new file mode 100644 index 0000000000..b5eaa146e6 --- /dev/null +++ b/docs/content/ds/data-structures/maps/README.adoc @@ -0,0 +1,7 @@ +include::../../../book/content/part03/map.asc[] + +<<< +include::../../../book/content/part02/hash-map.asc[] + +<<< +include::../../../book/content/part03/tree-map.asc[] diff --git a/docs/content/ds/data-structures/maps/hash-maps/hash-map-1.js b/docs/content/ds/data-structures/maps/hash-maps/hash-map-1.js new file mode 100644 index 0000000000..4deec02c95 --- /dev/null +++ b/docs/content/ds/data-structures/maps/hash-maps/hash-map-1.js @@ -0,0 +1,76 @@ +/** + * Naïve HashMap implementation + * + * Hash function uses the length of the string (very bad) + * Just for illustration purposes + */ +class HashMap { + /** + * Initialize array that holds the values. Default is size 1,000 + * @param {number} initialCapacity + */ + constructor(initialCapacity = 2) { + this.buckets = new Array(initialCapacity); + } + + /** + * Insert a key/value pair into the hash map + * It doesn't handle collisions + * @param {any} key + * @param {any} value + */ + set(key, value) { + const index = this.getIndex(key); + this.buckets[index] = value; + } + + /** + * Gets the value out of the hash map + * @param {any} key + */ + get(key) { + const index = this.getIndex(key); + return this.buckets[index]; + } + + /** + * Very bad hash function that causes tons of collisions + * @param {any} key + */ + hash(key) { + return key.toString().length; + } + + /** + * Get the array index after applying the hash funtion to the given key + * @param {any} key + */ + getIndex(key) { + const indexHash = this.hash(key); + const index = indexHash % this.buckets.length; + return index; + } +} + +module.exports = HashMap; + +// // Usage: +// const assert = require('assert'); + +// const hashMap = new HashMap(); + +// hashMap.set('cat', 2); +// hashMap.set('rat', 7); +// hashMap.set('dog', 1); +// hashMap.set('art', 8); + +// console.log(hashMap.buckets); +// /* +// bucket #0: <1 empty item>, +// bucket #1: 8 +// */ + +// assert.equal(hashMap.get('art'), 8); // this one is ok +// assert.equal(hashMap.get('cat'), 8); // got overwritten by art 😱 +// assert.equal(hashMap.get('rat'), 8); // got overwritten by art 😱 +// assert.equal(hashMap.get('dog'), 8); // got overwritten by art 😱 diff --git a/docs/content/ds/data-structures/maps/hash-maps/hash-map-2.js b/docs/content/ds/data-structures/maps/hash-maps/hash-map-2.js new file mode 100644 index 0000000000..bd2b0254f0 --- /dev/null +++ b/docs/content/ds/data-structures/maps/hash-maps/hash-map-2.js @@ -0,0 +1,132 @@ +/** + * Hash Map data structure implementation + * Hash uses string code. Doesn't support overwrite. + */ +class HashMap { + /** + * Initialize array that holds the values. Default is size 1,000 + * @param {number} initialCapacity + */ + constructor(initialCapacity = 2) { + this.buckets = new Array(initialCapacity); + this.collisions = 0; + } + + /** + * insert a key/value pair into the hash map + * @param {any} key + * @param {any} value + */ + set(key, value) { + const bucketIndex = this.getIndex(key); + if (this.buckets[bucketIndex]) { + this.buckets[bucketIndex].push({ key, value }); + if (this.buckets[bucketIndex].length > 1) { this.collisions++; } + } else { + this.buckets[bucketIndex] = [{ key, value }]; + } + return this; + } + + /** + * Gets the value out of the hash map + * @param {any} key + */ + get(key) { + const bucketIndex = this.getIndex(key); + for (let arrayIndex = 0; arrayIndex < this.buckets[bucketIndex].length; arrayIndex++) { + const entry = this.buckets[bucketIndex][arrayIndex]; + if (entry.key === key) { + return entry.value; + } + } + } + + /** + * This hash function returns the sum of the char codes. + * Limitation same characters return the same code regardless of the order + * @param {any} key + */ + hash(key) { + let hashValue = 0; + const stringTypeKey = `${key}${typeof key}`; + + for (let index = 0; index < stringTypeKey.length; index++) { + const charCode = stringTypeKey.charCodeAt(index); + hashValue += charCode << (index * 8); + } + + return hashValue; + } + + /** + * Get the array index after applying the hash funtion to the given key + * @param {any} key + */ + getIndex(key) { + const indexHash = this.hash(key); + const index = indexHash % this.buckets.length; + return index; + } +} + +module.exports = HashMap; + + +// // Usage: +// const assert = require('assert'); + +// const hashMap = new HashMap(); + +// hashMap.set('cat', 2); +// hashMap.set('rat', 7); +// hashMap.set('dog', 1); +// hashMap.set('art', 8); + +// console.log('collisions: ', hashMap.collisions); +// console.log('hashMap.buckets\n', hashMap.buckets); +// /* +// bucket #0: [ { key: 'cat', value: 2 }, { key: 'dog', value: 1 } ] +// bucket #1: [ { key: 'rat', value: 7 }, { key: 'dog', value: 1 } ] +// */ + +// assert.equal(hashMap.get('art'), 8); // this one is ok +// assert.equal(hashMap.get('cat'), 2); // Good. Didn't got overwritten by art +// assert.equal(hashMap.get('rat'), 7); // Good. Didn't got overwritten by art +// assert.equal(hashMap.get('dog'), 1); // Good. Didn't got overwritten by art + + +// // + + +// const hashMapSize10 = new HashMap(10); + +// hashMapSize10.set('cat', 2); +// hashMapSize10.set('rat', 7); +// hashMapSize10.set('dog', 1); +// hashMapSize10.set('art', 8); + +// console.log('collisions: ', hashMapSize10.collisions); +// console.log('hashMapSize10\n', hashMapSize10.buckets); +// /* +// bucket#0: [ { key: 'cat', value: 2 }, { key: 'art', value: 8 } ], +// <4 empty items>, +// bucket#5: [ { key: 'rat', value: 7 } ], +// <1 empty item>, +// bucket#7: [ { key: 'dog', value: 1 } ], +// <2 empty items> ] +// */ + + +// // + + +// const hashMapSize100 = new HashMap(100); + +// hashMapSize100.set('cat', 2); +// hashMapSize100.set('rat', 7); +// hashMapSize100.set('dog', 1); +// hashMapSize100.set('art', 8); + +// console.log('collisions: ', hashMapSize100.collisions); +// console.log('hashMapSize100\n', hashMapSize100.buckets); diff --git a/docs/content/ds/data-structures/maps/hash-maps/hash-map-3.js b/docs/content/ds/data-structures/maps/hash-maps/hash-map-3.js new file mode 100644 index 0000000000..dcda027d5a --- /dev/null +++ b/docs/content/ds/data-structures/maps/hash-maps/hash-map-3.js @@ -0,0 +1,205 @@ +/** + * Hash Map data structure implementation + * + * Features: + * - HashMap offers 0(1) lookup and insertion. + * - Keys are ordered by their insertion order (like LinkedHashMap) + * - It contains only unique elements. + * - It may have one null key and multiple null values. + * + * @author Adrian Mejia + */ +class HashMap { + /** + * Initialize array that holds the values. Default is size 16 + * @param {number} initialCapacity initial size of the array + * @param {number} loadFactor if set, the Map will automatically rehash when the load factor threshold is met + */ + constructor(initialCapacity = 16, loadFactor = 0.75) { + this.buckets = new Array(initialCapacity); + this.loadFactor = loadFactor; + this.size = 0; + this.collisions = 0; + this.keysArrayWrapper = []; + } + + /** + * Decent hash function where each char ascii code is added with an offset depending on the possition + * @param {any} key + */ + static hashCode(key) { + let hashValue = 0; + const stringTypeKey = `${key}${typeof key}`; + + for (let index = 0; index < stringTypeKey.length; index++) { + const charCode = stringTypeKey.charCodeAt(index); + hashValue += charCode << (index * 8); + } + + return hashValue; + } + + /** + * A hash function converts keys into array indices + * @param {any} key + * @returns {Number} array index given the bucket size + */ + hashFunction(key) { + const hashValue = HashMap.hashCode(key); + const bucketIndex = hashValue % this.buckets.length; + return bucketIndex; + } + + /** + * Insert a key/value pair into the hash map. + * If the key is already there replaces its content. Return the Map object to allow chaining + * @param {any} key + * @param {any} value + */ + set(key, value) { + const { bucketIndex, entryIndex } = this._getIndexes(key); + + if (entryIndex === undefined) { + // initialize array and save key/value + const keyIndex = this.keysArrayWrapper.push({ content: key }) - 1; // keep track of the key index + this.buckets[bucketIndex] = this.buckets[bucketIndex] || []; + this.buckets[bucketIndex].push({ key, value, keyIndex }); + this.size++; + // Optional: keep count of collisions + if (this.buckets[bucketIndex].length > 1) { this.collisions++; } + } else { + // override existing value + this.buckets[bucketIndex][entryIndex].value = value; + } + + // check if a rehash is due + if (this.loadFactor > 0 && this.getLoadFactor() > this.loadFactor) { + this.rehash(this.buckets.length * 2); + } + + return this; + } + + /** + * Gets the value out of the hash map + * Returns the value associated to the key, or undefined if there is none. + * @param {any} key + */ + get(key) { + const { bucketIndex, entryIndex } = this._getIndexes(key); + + if (entryIndex === undefined) { + return; + } + + return this.buckets[bucketIndex][entryIndex].value; + } + + /** + * Search for key and return true if it was found + * @param {any} key + */ + has(key) { + return this._getIndexes(key).entryIndex !== undefined; + } + + /** + * Search for a key in the map. It returns it's internal array indexes. + * Returns bucketIndex and the internal array index + * @param {any} key + */ + _getIndexes(key) { + const bucketIndex = this.hashFunction(key); + const values = this.buckets[bucketIndex] || []; + + for (let entryIndex = 0; entryIndex < values.length; entryIndex++) { + const entry = values[entryIndex]; + if (entry.key === key) { + return { bucketIndex, entryIndex, keyIndex: entry.keyIndex }; + } + } + + return { bucketIndex }; + } + + /** + * Returns true if an element in the Map object existed and has been removed, or false if the element does not exist. + * @param {any} key + */ + delete(key) { + const { bucketIndex, entryIndex, keyIndex } = this._getIndexes(key); + + if (entryIndex === undefined) { + return false; + } + + this.buckets[bucketIndex].splice(entryIndex, 1); + delete this.keysArrayWrapper[keyIndex]; + this.size--; + + return true; + } + + /** + * Rehash means to create a new Map with a new (higher) capacity with the purpose of outgrow collisions. + * @param {Number} newCapacity + */ + rehash(newCapacity) { + const newMap = new HashMap(newCapacity); + + this.keysArrayWrapper.forEach((key) => { + newMap.set(key.content, this.get(key.content)); + }); + + // update bucket + this.buckets = newMap.buckets; + this.collisions = newMap.collisions; + // Optional: both `keys` has the same content except that the new one doesn't have empty spaces from deletions + this.keysArrayWrapper = newMap.keysArrayWrapper; + } + + /** + * Load factor - measure how full the Map is. + * It's ratio between items on the map and total size of buckets + */ + getLoadFactor() { + return this.size / this.buckets.length; + } + + /** + * Returns an array with valid keys + * If keys has been deleted they shouldn't be in the array of keys + */ + keys() { + return this.keysArrayWrapper.reduce((acc, key) => { + acc.push(key.content); + return acc; + }, []); + } + + /** + * The values() method returns a new Iterator object that + * contains the values for each element in the Map object + * in insertion order. + * + * @example + * const myMap = new HashMap(); + * myMap.set('0', 'foo'); + * myMap.set(1, 'bar'); + * myMap.set({}, 'baz'); + * + * var mapIter = myMap.values(); + * + * console.log(mapIter.next().value); // "foo" + * console.log(mapIter.next().value); // "bar" + * console.log(mapIter.next().value); // "baz" + */ + values() { + throw new Error('Not implemented'); + } +} + +// Aliases +HashMap.prototype.containsKey = HashMap.prototype.has; + +module.exports = HashMap; diff --git a/docs/content/ds/data-structures/maps/hash-maps/hash-map-4.js b/docs/content/ds/data-structures/maps/hash-maps/hash-map-4.js new file mode 100644 index 0000000000..e0a013dfdd --- /dev/null +++ b/docs/content/ds/data-structures/maps/hash-maps/hash-map-4.js @@ -0,0 +1,234 @@ +/* global BigInt */ + +/** + * Hash Map data structure implementation + * + * Polynomial hash + MAD + BigInt (very slow) + * + * Features: + * - HashMap offers 0(1) lookup and insertion. + * - Keys are ordered by their insertion order (like LinkedHashMap) + * - It contains only unique elements. + * - It may have one null key and multiple null values. + * + * @author Adrian Mejia + */ +class HashMap { + /** + * Initialize array that holds the values. Default is size 16 + * @param {number} initialCapacity initial size of the array + * @param {number} loadFactor if set, the Map will automatically rehash when the load factor threshold is met + */ + constructor(initialCapacity = 16, loadFactor = 0.75) { + this.buckets = new Array(initialCapacity); + this.loadFactor = loadFactor; + this.size = 0; + this.collisions = 0; + this.keysArrayWrapper = []; + } + + /** + * Use the binary represenation (IEEE 754) as a hashCode + * @param {any} key + */ + static hashCodeForNumber(number) { + const buffer = new ArrayBuffer(8); // 8 bytes for float64 + const dataView = new DataView(buffer); + dataView.setFloat64(0, number); // set as float64 + const longBits = dataView.getBigInt64(0); // read as long int (BigInt) + // gurantee only positive (big) integers + return longBits > 0 ? longBits : BigInt(2 ** 63) + (longBits * BigInt(-1)); + } + + /** + * Polynomial hash codes are used to hash String typed keys. + * A string is a sequence of characters encoded + * using Unicode's numerical value. + * @param {any} key converted to string + */ + hashFunctionForString(key) { + return Array + .from(key.toString()) + .reduce((hashIndex, char) => ((41 * hashIndex) + char.codePointAt(0)), 0); + } + + /** + * A hash function converts keys into array indices + * @param {any} key + * @returns {BigInt} array index given the bucket size + */ + hashFunction(key) { + if (typeof key === 'number') { + return this.hashCodeToIndex(HashMap.hashCodeForNumber(key)); + } + return this.hashCodeToIndex(this.hashFunctionForString(key)); + } + + /** + * Multiply-Add-Divide (MAD) compression + * @param {number} hash hashCode + * @param {number} size bucket array size + * @returns {number} array bucket index + */ + hashCodeToIndex(hash, size = this.buckets.length) { + return hash % size; + // const prime = BigInt(6700417); // prime number > size + // const a = prime - BigInt(2); // integer from range [1..(p-1)] + // const b = prime - BigInt(1); // integer from range [0..(p-1)] + // const hashIndex = (((a * BigInt(hash)) + b) % prime) % BigInt(size); + // return parseInt(hashIndex, 10); + } + + /** + * Insert a key/value pair into the hash map. + * If the key is already there replaces its content. Return the Map object to allow chaining + * @param {any} key + * @param {any} value + */ + set(key, value) { + const { bucketIndex, entryIndex } = this._getIndexes(key); + + if (entryIndex === undefined) { + // initialize array and save key/value + const keyIndex = this.keysArrayWrapper.push({ content: key }) - 1; // keep track of the key index + this.buckets[bucketIndex] = this.buckets[bucketIndex] || []; + this.buckets[bucketIndex].push({ key, value, keyIndex }); + this.size++; + // Optional: keep count of collisions + if (this.buckets[bucketIndex].length > 1) { this.collisions++; } + } else { + // override existing value + this.buckets[bucketIndex][entryIndex].value = value; + } + + // check if a rehash is due + if (this.loadFactor > 0 && this.getLoadFactor() > this.loadFactor) { + this.rehash(this.buckets.length * 2); + } + + return this; + } + + /** + * Gets the value out of the hash map + * Returns the value associated to the key, or undefined if there is none. + * @param {any} key + */ + get(key) { + const { bucketIndex, entryIndex } = this._getIndexes(key); + + if (entryIndex === undefined) { + return; + } + + return this.buckets[bucketIndex][entryIndex].value; + } + + /** + * Search for key and return true if it was found + * @param {any} key + */ + has(key) { + return this._getIndexes(key).entryIndex !== undefined; + } + + /** + * Search for a key in the map. It returns it's internal array indexes. + * Returns bucketIndex and the internal array index + * @param {any} key + */ + _getIndexes(key) { + const bucketIndex = this.hashFunction(key); + const values = this.buckets[bucketIndex] || []; + + for (let entryIndex = 0; entryIndex < values.length; entryIndex++) { + const entry = values[entryIndex]; + if (entry.key === key) { + return { bucketIndex, entryIndex, keyIndex: entry.keyIndex }; + } + } + + return { bucketIndex }; + } + + /** + * Returns true if an element in the Map object existed and has been removed, or false if the element does not exist. + * @param {any} key + */ + delete(key) { + const { bucketIndex, entryIndex, keyIndex } = this._getIndexes(key); + + if (entryIndex === undefined) { + return false; + } + + this.buckets[bucketIndex].splice(entryIndex, 1); + delete this.keysArrayWrapper[keyIndex]; + this.size--; + + return true; + } + + /** + * Rehash means to create a new Map with a new (higher) capacity with the purpose of outgrow collisions. + * @param {Number} newCapacity + */ + rehash(newCapacity) { + const newMap = new HashMap(newCapacity); + + this.keysArrayWrapper.forEach((key) => { + newMap.set(key.content, this.get(key.content)); + }); + + // update bucket + this.buckets = newMap.buckets; + this.collisions = newMap.collisions; + // Optional: both `keys` has the same content except that the new one doesn't have empty spaces from deletions + this.keysArrayWrapper = newMap.keysArrayWrapper; + } + + /** + * Load factor - measure how full the Map is. + * It's ratio between items on the map and total size of buckets + */ + getLoadFactor() { + return this.size / this.buckets.length; + } + + /** + * Returns an array with valid keys + * If keys has been deleted they shouldn't be in the array of keys + */ + keys() { + return this.keysArrayWrapper.reduce((acc, key) => { + acc.push(key.content); + return acc; + }, []); + } + + /** + * The values() method returns a new Iterator object that + * contains the values for each element in the Map object + * in insertion order. + * + * @example + * const myMap = new HashMap(); + * myMap.set('0', 'foo'); + * myMap.set(1, 'bar'); + * myMap.set({}, 'baz'); + * + * var mapIter = myMap.values(); + * + * console.log(mapIter.next().value); // "foo" + * console.log(mapIter.next().value); // "bar" + * console.log(mapIter.next().value); // "baz" + */ + values() { + throw new Error('Not implemented'); + } +} + +// Aliases +HashMap.prototype.containsKey = HashMap.prototype.has; + +module.exports = HashMap; diff --git a/docs/content/ds/data-structures/maps/hash-maps/hash-map.js b/docs/content/ds/data-structures/maps/hash-maps/hash-map.js new file mode 100644 index 0000000000..00a26beaa7 --- /dev/null +++ b/docs/content/ds/data-structures/maps/hash-maps/hash-map.js @@ -0,0 +1,310 @@ +/* eslint-disable no-bitwise, no-iterator, no-restricted-syntax */ +const { TextEncoder } = require('util'); +const LinkedList = require('../../linked-lists/linked-list'); +const { nextPrime } = require('./primes'); + +// Text encoding +const encoding = new TextEncoder(); + +/** + * The map holds key-value pairs. + * Any value (both objects and primitive values) may be used as either a key or a value. + * + * Features: + * - HashMap offers avg. 0(1) lookup, insertion and deletion. + * - Keys and values are ordered by their insertion order (like Java's LinkedHashMap) + * - It contains only unique key. + * - It may have one null key and multiple null values. + */ +class HashMap { + // tag::constructorPartial[] + /** + * Initialize array that holds the values. + * @param {number} initialCapacity initial size of the array (preferably a prime) + * @param {number} loadFactor rehash is called when this threshold is met. + */ + constructor(initialCapacity = 19, loadFactor = 0.75) { + this.initialCapacity = initialCapacity; + this.loadFactor = loadFactor; + // end::constructorPartial[] + this.reset(); + } + + /** + * Reset or reinitialize all values on the hashmap. + * + * Used for rehashing, clear and initializing the map. + * + * @param {array} buckets - New bucket. + * @param {number} size - The new size of the hashmap. + * @param {number} collisions - The number of collisions. + * @param {array} keysTrackerArray - The array of keys in insertion order + * @param {number} keysTrackerIndex - The last index of keysTrackerArray + */ + reset( + buckets = new Array(this.initialCapacity), + size = 0, + collisions = 0, + keysTrackerArray = [], + keysTrackerIndex = 0, + ) { + this.buckets = buckets; + this.size = size; + this.collisions = collisions; + // keyTracker* is used to keep track of the insertion order + this.keysTrackerArray = keysTrackerArray; + this.keysTrackerIndex = keysTrackerIndex; + } + + // tag::hashFunction[] + /** + * Polynomial hash codes are used to hash String typed keys. + * It uses FVN-1a hashing algorithm for 32 bits + * @see http://bit.ly/fvn-1a + * @param {any} key + * @return {integer} bucket index + */ + hashFunction(key) { + const bytes = encoding.encode(key); + const { length } = bytes; + + let hash = 2166136261; // FNV_offset_basis (32 bit) + + for (let i = 0; i < length; i++) { + hash ^= bytes[i]; // XOR + hash *= 16777619; // 32 bit FNV_prime + } + + return (hash >>> 0) % this.buckets.length; + } + // end::hashFunction[] + + // tag::getEntry[] + /** + * Find an entry inside a bucket. + * + * The bucket is an array of Linked Lists. + * Entries are the nodes in the linked list + * containing key/value objects. + * + * Avg. Runtime: O(1) + * Usually O(1) but if there are many collisions it could be O(n). + * + * @param {any} key + * @returns {object} object `{ bucket, entry }` containing the bucket + * and entry (LinkedList's node matching value) + */ + getEntry(key) { + const index = this.hashFunction(key); // <1> + this.buckets[index] = this.buckets[index] || new LinkedList(); // <2> + const bucket = this.buckets[index]; + + const entry = bucket.find(({ value: node }) => { // <3> + if (key === node.key) { + return node; // stop search + } + return undefined; // continue searching + }); + + return { bucket, entry }; // <4> + } + // end::getEntry[] + + + // tag::set[] + /** + * Insert a key/value pair into the hash map. + * If the key is already there replaces its content. + * Avg. Runtime: O(1). In the case a rehash is needed O(n). + * @param {any} key + * @param {any} value + * @returns {HashMap} Return the map to allow chaining + */ + set(key, value) { + const { entry: exists, bucket } = this.getEntry(key); + + if (!exists) { // key/value doesn't exist <1> + bucket.push({ key, value, order: this.keysTrackerIndex }); + this.keysTrackerArray[this.keysTrackerIndex] = key; // <4> + this.keysTrackerIndex += 1; + this.size += 1; + if (bucket.size > 1) { this.collisions += 1; } // <3> + if (this.isBeyondloadFactor()) { this.rehash(); } + } else { + // update value if key already exists + exists.value = value; // <2> + } + return this; + } + // end::set[] + + // tag::get[] + /** + * Gets the value out of the hash map + * Avg. Runtime: O(1) + * @param {any} key + * @returns {any} value associated to the key, or undefined if there is none. + */ + get(key) { + const { entry } = this.getEntry(key); + return entry && entry.value; + } + // end::get[] + + // tag::has[] + /** + * Search for key and return true if it was found + * Avg. Runtime: O(1) + * @param {any} key + * @returns {boolean} indicating whether an element + * with the specified key exists or not. + */ + has(key) { + const { entry } = this.getEntry(key); + return entry !== undefined; + } + // end::has[] + + // tag::delete[] + /** + * Removes the specified element from the map. + * Avg. Runtime: O(1) + * @param {*} key + * @returns {boolean} true if an element in the map existed + * and has been removed, or false if the element did not exist. + */ + delete(key) { + const { bucket, entry } = this.getEntry(key); + if (!entry) { return false; } + + return !!bucket.remove((node) => { + if (key === node.value.key) { + delete this.keysTrackerArray[node.value.order]; // O(1) deletion + this.size -= 1; + return true; + } + return undefined; + }); + } + // end::delete[] + + // tag::getLoadFactor[] + /** + * Load factor - measure how full the Map is. + * It's ratio between items on the map and total size of buckets + * @returns {number} load factor ratio + */ + getLoadFactor() { + return this.size / this.buckets.length; + } + + /** + * Check if a rehash is due + * @returns {boolean} true if is beyond load factor, false otherwise. + */ + isBeyondloadFactor() { + return this.getLoadFactor() > this.loadFactor; + } + // end::getLoadFactor[] + + // tag::rehash[] + /** + * Rehash means to create a new Map with a new (higher) + * capacity with the purpose of outgrowing collisions. + * @param {integer} newBucketSize new bucket size by default + * is the 2x the amount of data or bucket size. + */ + rehash(newBucketSize = Math.max(this.size, this.buckets.length) * 2) { + const newCapacity = nextPrime(newBucketSize); + const newMap = new HashMap(newCapacity); + + // copy all values to the new map + for (const key of this.keys()) { + newMap.set(key, this.get(key)); + } + + const newArrayKeys = Array.from(newMap.keys()); + + // override this map with the newMap + this.reset( + newMap.buckets, + newMap.size, + newMap.collisions, + newArrayKeys, + newArrayKeys.length, + ); + } + // end::rehash[] + + + /** + * Keys for each element in the map in insertion order. + * @returns {Iterator} keys without holes (empty spaces of deleted keys) + */ + * keys() { + for (let index = 0; index < this.keysTrackerArray.length; index++) { + const key = this.keysTrackerArray[index]; + if (key !== undefined) { + yield key; + } + } + } + + /** + * Values for each element in the map in insertion order. + * @returns {Iterator} values without holes (empty spaces of deleted values) + */ + * values() { + for (const key of this.keys()) { + yield this.get(key); + } + } + + /** + * Contains the [key, value] pairs for each element in the map in insertion order. + * @returns {Iterator} + */ + * entries() { + for (const key of this.keys()) { + yield [key, this.get(key)]; + } + } + + /** + * The same function object as the initial value of the `entries` method. + * Contains the [key, value] pairs for each element in the Map. + */ + * [Symbol.iterator]() { + yield* this.entries(); + } + + /** + * @returns {integer} number of elements in the hashmap + */ + get length() { + return this.size; + } + + /** + * Removes all key/value pairs from the Map object. + */ + clear() { + this.reset(); + } +} + +// Aliases +HashMap.prototype.containsKey = HashMap.prototype.has; + +module.exports = HashMap; + +/* HashMap usage example +// tag::snippet[] +const hashMap = new HashMap(); + +hashMap.set('cat', 2); +hashMap.set('art', 8); +hashMap.set('rat', 7); +hashMap.set('dog', 1); +// end::snippet[] +*/ diff --git a/docs/content/ds/data-structures/maps/hash-maps/hash-map.spec.js b/docs/content/ds/data-structures/maps/hash-maps/hash-map.spec.js new file mode 100644 index 0000000000..daaf93b78a --- /dev/null +++ b/docs/content/ds/data-structures/maps/hash-maps/hash-map.spec.js @@ -0,0 +1,298 @@ +const { HashMap } = require('../../../index'); + + +describe('HashMap Tests', () => { + let hashMap; + + beforeEach(() => { + hashMap = new HashMap(); + }); + + describe('set and get basics', () => { + it('should hold one null key', () => { + hashMap.set(null, 1); + hashMap.set(null, 2); + expect(hashMap.get(null)).toBe(2); + }); + + it('should hold multiple null values', () => { + hashMap.set(1, null); + hashMap.set(2, null); + expect(hashMap.get(1)).toBe(null); + expect(hashMap.get(2)).toBe(null); + }); + }); + + describe('#keys', () => { + it('should get keys', () => { + hashMap.set(0, 'foo'); + hashMap.set(null, 'fox'); + hashMap.set('a', 'bar'); + hashMap.set({}, 'baz'); + + const mapIter = hashMap.keys(); + + expect(mapIter.next().value).toBe(0); + expect(mapIter.next().value).toBe(null); + expect(mapIter.next().value).toBe('a'); + expect(mapIter.next().value).toEqual({}); + }); + + it('should not have holes', () => { + hashMap.set('0', 'foo'); + hashMap.set(1, 'bar'); + hashMap.set({}, 'baz'); + + hashMap.delete(1); + + expect([...hashMap.keys()]).toEqual(['0', {}]); + }); + }); + + describe('#values', () => { + it('should get values', () => { + hashMap.set('0', 'foo'); + hashMap.set(1, 'bar'); + hashMap.set({}, 'baz'); + + const mapIter = hashMap.values(); + + expect(mapIter.next().value).toBe('foo'); + expect(mapIter.next().value).toBe('bar'); + expect(mapIter.next().value).toBe('baz'); + }); + + it('should not have holes', () => { + hashMap.set('0', 'foo'); + hashMap.set(1, 'bar'); + hashMap.set({}, 'baz'); + + hashMap.delete(1); + + expect(Array.from(hashMap.values())).toEqual(['foo', 'baz']); + }); + }); + + describe('#entries', () => { + it('should get values', () => { + hashMap.set('0', 'foo'); + hashMap.set(1, 'bar'); + hashMap.set({}, 'baz'); + + const mapIter = hashMap.entries(); + + expect(mapIter.next().value).toEqual(['0', 'foo']); + expect(mapIter.next().value).toEqual([1, 'bar']); + expect(mapIter.next().value).toEqual([{}, 'baz']); + }); + + it('should not have holes', () => { + hashMap.set('0', 'foo'); + hashMap.set(1, 'bar'); + hashMap.set({}, 'baz'); + + hashMap.delete(1); + expect(hashMap.length).toBe(2); + expect(hashMap.size).toBe(2); + + expect(Array.from(hashMap.entries())).toEqual([ + ['0', 'foo'], + [{}, 'baz'], + ]); + + expect(Array.from(hashMap)).toEqual([ + ['0', 'foo'], + [{}, 'baz'], + ]); + }); + }); + + describe('without collisions', () => { + it('set and get values', () => { + hashMap.set('test', 'one'); + expect(hashMap.get('test')).toBe('one'); + }); + + it('should increase size', () => { + expect(hashMap.size).toBe(0); + hashMap.set('test', 'uno'); + expect(hashMap.size).toBe(1); + }); + + it('has without value', () => { + expect(hashMap.size).toBe(0); + hashMap.set('uno'); + expect(hashMap.size).toBe(1); + expect(hashMap.has('uno')).toBe(true); + expect(hashMap.has('dos')).toBe(false); + }); + + it('should overwrite values and keep same size', () => { + hashMap.set('test', 'uno'); + expect(hashMap.get('test')).toBe('uno'); + hashMap.set('test', 'dos'); + expect(hashMap.get('test')).toBe('dos'); + expect(hashMap.size).toBe(1); + }); + + it('should increase load factor and size', () => { + expect(hashMap.getLoadFactor()).toBe(0); + expect(hashMap.size).toBe(0); + hashMap.set('test', 'one'); + expect(hashMap.getLoadFactor()).toBeGreaterThan(0); + expect(hashMap.size).toBe(1); + }); + + it('should return with has', () => { + expect(hashMap.has('test')).toBe(false); + hashMap.set('test', 'uno'); + expect(hashMap.has('test')).toBe(true); + }); + + it('should delete', () => { + hashMap.set('Despacito', 'Luis Fonsi'); + hashMap.set('Bailando', 'Enrique Iglesias'); + hashMap.set('Dura', 'Daddy Yankee'); + + expect(hashMap.size).toBe(3); + expect(hashMap.delete('Bailando')).toBe(true); + expect(hashMap.size).toBe(2); + expect(hashMap.delete('Bailando')).toBe(false); + expect(hashMap.get('Bailando')).toBe(undefined); + }); + }); + + describe('with many values (and collisions)', () => { + beforeEach(() => { + hashMap = new HashMap(1, Number.MAX_SAFE_INTEGER); + + hashMap.set('Pineapple', 'Pen Pineapple Apple Pen'); + hashMap.set('Despacito', 'Luis Fonsi'); + hashMap.set('Bailando', 'Enrique Iglesias'); + hashMap.set('Dura', 'Daddy Yankee'); + hashMap.set('Lean On', 'Major Lazer'); + hashMap.set('Hello', 'Adele'); + hashMap.set('All About That Bass', 'Meghan Trainor'); + hashMap.set('This Is What You Came For', 'Calvin Harris '); + }); + + it('should count collisions', () => { + expect(hashMap.collisions).toBe(7); + }); + + it('gets values', () => { + hashMap.set('test', 'one'); + expect(hashMap.get('test')).toBe('one'); + expect(hashMap.get('Dura')).toBe('Daddy Yankee'); + expect(hashMap.get('Bailando')).toBe('Enrique Iglesias'); + }); + + it('should increase load factor and size', () => { + expect(hashMap.getLoadFactor()).toBe(8); + expect(hashMap.size).toBe(8); + hashMap.set('test', 'one'); + expect(hashMap.getLoadFactor()).toBe(9); + expect(hashMap.size).toBe(9); + }); + + it('should overwrite values and keep same size', () => { + hashMap.set('test', 'uno'); + expect(hashMap.get('test')).toBe('uno'); + hashMap.set('test', 'dos'); + expect(hashMap.get('test')).toBe('dos'); + expect(hashMap.size).toBe(9); + }); + + it('should return with has', () => { + expect(hashMap.has('test')).toBe(false); + hashMap.set('test', 'uno'); + expect(hashMap.has('test')).toBe(true); + }); + + it('should update keys on deletes', () => { + // expect(hashMap.size).toBe(8); + // expect(hashMap.has('Hello')).toBe(true); + expect(hashMap.delete('Hello')).toBe(true); + // expect(hashMap.has('Hello')).toBe(false); + // expect(hashMap.size).toBe(7); + expect(hashMap.delete('Lean On')).toBe(true); + // expect(hashMap.has('Lean On')).toBe(false); + // expect(hashMap.size).toBe(6); + expect(hashMap.delete('Pineapple')).toBe(true); + // expect(hashMap.has('Pineapple')).toBe(false); + // expect(hashMap.size).toBe(5); + expect(hashMap.delete('All About That Bass')).toBe(true); + expect(hashMap.has('All About That Bass')).toBe(false); + // expect(hashMap.size).toBe(4); + expect(hashMap.delete('This Is What You Came For')).toBe(true); + expect(hashMap.has('This Is What You Came For')).toBe(false); + expect(hashMap.size).toBe(3); + + expect(Array.from(hashMap.keys())).toEqual(['Despacito', 'Bailando', 'Dura']); + + expect(hashMap.delete('Bailando')).toBe(true); + expect(hashMap.delete('Bailando')).toBe(false); + expect(hashMap.get('Bailando')).toBe(undefined); + + expect(hashMap.size).toBe(2); + expect([...hashMap.keys()]).toEqual(['Despacito', 'Dura']); + }); + }); + + describe('#rehash', () => { + beforeEach(() => { + hashMap = new HashMap(1, 11); + + hashMap.set('Pineapple', 'Pen Pineapple Apple Pen'); + hashMap.set('Despacito', 'Luis Fonsi'); + hashMap.set('Bailando', 'Enrique Iglesias'); + hashMap.set('Dura', 'Daddy Yankee'); + hashMap.set('Lean On', 'Major Lazer'); + hashMap.set('Hello', 'Adele'); + hashMap.set('All About That Bass', 'Meghan Trainor'); + hashMap.set('Wake Me Up', 'Avicii'); + hashMap.set('Brother', 'Avicii'); + hashMap.set('Faded', 'Alan Walker'); + hashMap.set('The Spectre', 'Alan Walker'); + }); + + it('should rehash', () => { + expect(hashMap.collisions).toBe(10); + expect(hashMap.getLoadFactor()).toBe(11); + expect(hashMap.buckets.length).toBe(1); + + expect(hashMap.keysTrackerIndex).toBe(11); + hashMap.delete('All About That Bass'); + hashMap.set('All About That Bass', 'Meghan Trainor'); + expect(hashMap.keysTrackerIndex).toBe(12); + // should have a hole + expect(hashMap.keysTrackerArray).toEqual(['Pineapple', 'Despacito', 'Bailando', 'Dura', 'Lean On', 'Hello', + undefined, + 'Wake Me Up', 'Brother', 'Faded', 'The Spectre', 'All About That Bass']); + + hashMap.loadFactor = 0.75; + hashMap.set('Alone', 'Alan Walker'); + + // rehash + expect(hashMap.keysTrackerIndex).toBe(12); + expect(hashMap.collisions).toBeLessThan(10); + expect(hashMap.buckets.length).toBe(29); // <- next prime + expect(hashMap.getLoadFactor()).toBe(12 / 29); + + hashMap.set('Rolling in the Deep', 'Adele'); + + expect(hashMap.get('Dura')).toBe('Daddy Yankee'); + expect(hashMap.get('Bailando')).toBe('Enrique Iglesias'); + expect(hashMap.get('Alone')).toBe('Alan Walker'); + + expect(Array.from(hashMap.keys()).length).toBe(13); + expect(hashMap.size).toBe(13); + expect(hashMap.keysTrackerIndex).toBe(13); + // after the rehash the hole should have been removed + expect(hashMap.keysTrackerArray).toEqual(['Pineapple', 'Despacito', + 'Bailando', 'Dura', 'Lean On', 'Hello', 'Wake Me Up', 'Brother', + 'Faded', 'The Spectre', 'All About That Bass', 'Alone', + 'Rolling in the Deep']); + }); + }); +}); diff --git a/docs/content/ds/data-structures/maps/hash-maps/hashing-offset-test.js b/docs/content/ds/data-structures/maps/hash-maps/hashing-offset-test.js new file mode 100644 index 0000000000..e811b4a12b --- /dev/null +++ b/docs/content/ds/data-structures/maps/hash-maps/hashing-offset-test.js @@ -0,0 +1,70 @@ +const assert = require('assert'); + +/** + * Calculates polynomial hash code that maps a key (value) to an integer (unbounded). + * It uses a 20 bit offset to avoid Unicode value overlaps + * @param {any} key + * @returns {BigInt} returns big integer (unbounded) that maps to the key + */ +function hashCode(key) { + const array = Array.from(`${key}${typeof key}`); + return array.reduce((hashCode, char, position) => { + return hashCode + BigInt(char.codePointAt(0)) * (2n ** (BigInt(position) * 20n)); + }, 0n); +} + +/** + * Compression function: maps an arbitrary integer to integer in the range of [0… BUCKET_SIZE -1]. + * @param {BigInt} hashCode + * @param {Number} size bucket size + * @returns {Number} array index + */ +function compressToIndex(hashCode, size = 10) { + return parseInt(hashCode % BigInt(size), 10); +} + +/** + * + * @param {*} key + */ +function hashFunction(key, size = 10) { + return compressToIndex(hashCode(key), size); +} + + +function printHash(el) { + const code = hashCode(el); + return { s: el, v: code.toLocaleString(), hex: code.toString(16), hashFn: compressToIndex(code) }; +} + +// similar ending +console.table(['00', '10', '20', '30', '40', '50', '60', '70', '80', '90'].map(printHash)); +// similar start +console.table(['10', '11', '12', '13', '14', '15', '16', '17', '18', '19'].map(printHash)); + +console.table(['@', '#', '#!', 'stop', 'pots', 'Ca', 'DB'].map(printHash)); + +// all different +// console.table(['cat', 'dog', 'rat', 'art', 10, '10', {a:1}, '😸', '🐶', '😸🐶', '🐶😸'].map(printHash)); +// console.log(hashCode(Array(1500).fill('😁').join(''))); +// console.log(hashFunction(Array(1500).fill('😁').join(''))); + + +// function test(){ +// return 1n + 2n; +// } + +// test(); + + +// hashCode(10); //=> 97 +// hashCode('10'); //=> 97 + +assert.notEqual(hashCode(10), hashCode('10'), 'Hash code should be different with different types'); +assert.notEqual(hashCode('10string'), hashCode('10'), 'Hash code should be different with different types'); + +hashCode(10) === hashCode('10'); //=> false +hashCode('10') === hashCode('10string'); //=> false +hashCode('art') === hashCode('rat'); //=> false +hashCode('😄') === hashCode('😄'); //=> true +hashCode('😄') === hashCode('😸'); //=> false diff --git a/docs/content/ds/data-structures/maps/hash-maps/hashing-prime-test.js b/docs/content/ds/data-structures/maps/hash-maps/hashing-prime-test.js new file mode 100644 index 0000000000..1434d65ed4 --- /dev/null +++ b/docs/content/ds/data-structures/maps/hash-maps/hashing-prime-test.js @@ -0,0 +1,67 @@ +const assert = require('assert'); + +/** + * + * @param {*} key + */ +function hashFunction(key, size = 10) { + const primeNumber = 1327144003n; // 2 ** 77232917 - 1 + + const hashCode = Array.from(key.toString()).reduce((hash, char) => { + return (hash * primeNumber + BigInt(char.codePointAt(0))) % BigInt(size); + }, 0n); + + return parseInt(hashCode, 10); +} + +// function hashCodeJava(key) { +// let h = 0; +// const value = key.toString(); +// const length = value.length >> 1; + +// for (let i = 0; i < length; i++) { +// h = 31 * h + value.codePointAt(i); +// } +// return h; +// } + +function printHash(key) { + return { s: key, hashFn: hashFunction(key) }; +} + +// similar ending +// console.table(['00', '10', '20', '30', '40', '50', '60', '70', '80', '90'].map(printHash)); +// similar start +// console.table(['10', '11', '12', '13', '14', '15', '16', '17', '18', '19'].map(printHash)); + +// console.table(['@', '#', '#!', 'stop', 'pots', 'Ca', 'DB', 'polygenelubricants', 'Pneumonoultramicroscopicsilicovolcanoconiosis'].map(printHash)); + +const size = 5100; +console.log(printHash(Array(size).fill('😁').join('')).hashFn); +console.log(printHash(Array(size).fill('1').join('')).hashFn); +console.log(printHash(Array(size).fill('A').join('')).hashFn); + + +// all different +// console.table(['cat', 'dog', 'rat', 'art', 10, '10', {a:1}, '😸', '🐶', '😸🐶', '🐶😸'].map(printHash)); +// console.log(hashFunction(Array(1500).fill('😁').join(''))); + + +// function test(){ +// return 1n + 2n; +// } + +// test(); + + +// hashCode(10); //=> 97 +// hashCode('10'); //=> 97 + +// assert.notEqual(hashCode(10), hashCode('10'), 'Hash code should be different with different types'); +// assert.notEqual(hashCode('10string'), hashCode('10'), 'Hash code should be different with different types'); + +// hashCode(10) === hashCode('10'); //=> false +// hashCode('10') === hashCode('10string'); //=> false +// hashCode('art') === hashCode('rat'); //=> false +// hashCode('😄') === hashCode('😄'); //=> true +// hashCode('😄') === hashCode('😸'); //=> false diff --git a/docs/content/ds/data-structures/maps/hash-maps/hashing.js b/docs/content/ds/data-structures/maps/hash-maps/hashing.js new file mode 100644 index 0000000000..d2d104ea7e --- /dev/null +++ b/docs/content/ds/data-structures/maps/hash-maps/hashing.js @@ -0,0 +1,136 @@ +/* eslint-disable */ + +// tag::naiveHashCode[] +/** + * Naïve implementation of a non-cryptographic hashing function + * @param {any} key to be converted to a positive integer + * @returns {integer} hash code (numeric representation of the key) + */ +function hashCodeNaive(key) { + return Array.from(key.toString()).reduce((hashCode, char) => { + return hashCode + char.codePointAt(0); + }, 0); +} +// end::naiveHashCode[] + +/* Hash Code examples +// tag::naiveHashCodeExamples[] +hashCode('cat'); //=> 312 (c=99 + a=97 + t=116) +hashCode('dog'); //=> 314 (d=100 + o=111 + g=103) +hashCode('rat'); //=> 327 (r=114 + a=97 + t=116) +hashCode('art'); //=> 327 (a=97 + r=114 + t=116) +hashCode(10); //=> 97 ('1'=49 + '0'=48) +// end::naiveHashCodeExamples[] +*/ + +// tag::hashCodeOffset[] +/** + * Calculates hash code that maps a key (value) to an integer (unbounded). + * It uses a 20 bit offset to avoid Unicode value overlaps + * @param {any} key to be converted to a positive integer + * @returns {BigInt} returns big integer (unbounded) that maps to the key + */ +function hashCode(key) { + const array = Array.from(`${key}${typeof key}`); + return array.reduce((hashCode, char, position) => { + return hashCode + BigInt(char.codePointAt(0)) * (2n ** (BigInt(position) * 20n)); + }, 0n); +} +// end::hashCodeOffset[] + +/* +// tag::hashCodeOffsetExample[] +hashCode('art') //↪️ 150534821962845809557083360656040988391557528813665n +hashCode(10) === hashCode('10'); //↪️ false +hashCode('10') === hashCode('10string'); //↪️ false +hashCode('art') === hashCode('rat'); //↪️ false +hashCode('😄') === hashCode('😄'); //↪️ true +hashCode('😄') === hashCode('😸'); //↪️ false +// end::hashCodeOffsetExample[] +*/ + + +// ---- Experiments ----- + +const primes = [31n, 33n, 37n, 39n, 41n, 101n, 8191n, 131071n, 524287n, 6700417n, 1327144003n, 9007199254740881n]; + +function doubleToLongBits(number) { + const buffer = new ArrayBuffer(8); // 8 bytes for float64 + const dataView = new DataView(buffer); + dataView.setFloat64(0, number); // set as float64 + return dataView.getBigInt64(0); // read as long int (BigInt) +} + +function hashNumber(number) { + const bigInt = doubleToLongBits(number); + return bigInt > 0 ? bigInt : ((2n ** 63n) + (bigInt * -1n)); +} + +/** + * Polynomial hash codes + * @param {any} key + */ +function hashString(key) { + return Array.from(key.toString()).reduce((hash, char) => { + return (hash * 33n) + BigInt(char.codePointAt(0)); + }, 0n); +} + +function hashCode2(key) { + if (typeof(key) === 'number') { + return hashNumber(key); + } + return 2n ** 64n + hashString(key); +} + +function hashIndex({key, size = 16} = {}) { + // return hashCode(key) % BigInt(size); // modulo size + + // Multiply-Add-Divide (MAD) compression + const p = 524287n; // prime number larger than size. + const a = 8191n; // random [1..p-1] + const b = 0n; // random [0..p-1] + return ( (a * hashCode2(key) + b) % p ) % BigInt(size); +} + +module.exports = { + hashCode: hashCode2, + hashIndex +} + +/** + +function prepareToPrint(key){ + return { key: key.substring ? `${key.substring(0, 10)} (${key.length})` : key, hashCode: hashCode(key), hashIndex10: hashIndex({key, size: 10}) }; +} + +const res = [-2, -1, 0.5, 1, 2, 3, Math.PI, Number.MAX_VALUE, 2.7976931348623157e+308, 17.976931348623156e+400, + '😁', + 'hola', + '@', '#', '#!', 'stop', 'pots', 'Ca', 'DB', 'polygenelubricants', + 'Aa', + 'BB', + 'aoffckzdaoffckzdatafwjsh', + 'aoffckzdaoffckzdbhlijevx', + Array(50).fill('1').join(''), + // types + {a:1}, + 1n, + 1, + '1', + function a() {return;} + ] + .map(prepareToPrint); + +console.table(res); + +const res1 = [ + Array(1500).fill('1').join(''), + Array(1500).fill('😁').join(''), + // Array(1500).fill('z').join(''), + ] + .map(prepareToPrint); + + console.log(res1); + +// */ diff --git a/docs/content/ds/data-structures/maps/hash-maps/primes.js b/docs/content/ds/data-structures/maps/hash-maps/primes.js new file mode 100644 index 0000000000..c7b5feef80 --- /dev/null +++ b/docs/content/ds/data-structures/maps/hash-maps/primes.js @@ -0,0 +1,47 @@ +/** + * Odd number checker + * Runtime: O(1) + * @param {integer} number + * @returns {boolean} true if is odd, otherwise false. + */ +function isOdd(number) { + return number % 2 !== 0; +} + +/** + * Prime number tester + * Runtime: O(n ^ 0.5) + * @param {integer} number + * @returns {boolean} true if number is prime, otherwise false. + */ +function isPrime(number) { + if (number < 2) { return false; } + const max = Math.sqrt(number); + for (let divisor = 2; divisor <= max; divisor++) { + if (number % divisor === 0) { + return false; + } + } + return true; +} + +/** + * Find the next prime of a given number + * Runtime: ? // the gap between prime numbers is not deterministic. + * @param {integer} number + */ +function nextPrime(number) { + if (number < 2) { return 2; } + let possiblePrime = isOdd(number) ? number + 2 : number + 1; + + while (!isPrime(possiblePrime)) { + possiblePrime += 2; + } + return possiblePrime; +} + +module.exports = { + nextPrime, + isPrime, + isOdd, +}; diff --git a/docs/content/ds/data-structures/maps/hash-maps/primes.spec.js b/docs/content/ds/data-structures/maps/hash-maps/primes.spec.js new file mode 100644 index 0000000000..ecd37cad31 --- /dev/null +++ b/docs/content/ds/data-structures/maps/hash-maps/primes.spec.js @@ -0,0 +1,53 @@ +const { nextPrime, isPrime, isOdd } = require('./primes'); + +describe('Prime Util Tests', () => { + describe('#isPrime', () => { + test('2 is prime', () => { + expect(isPrime(2)).toBe(true); + }); + + test('1 is not prime', () => { + expect(isPrime(1)).toBe(false); + }); + + test('large prime number', () => { + expect(isPrime(914021)).toBe(true); + }); + + test('non prime number', () => { + expect(isPrime(99)).toBe(false); + }); + }); + + describe('#isOdd', () => { + it('odd number', () => { + expect(isOdd(1)).toBe(true); + }); + + it('even number', () => { + expect(isOdd(10)).toBe(false); + }); + + it('zero is an even number', () => { + expect(isOdd(0)).toBe(false); + }); + }); + + describe('#nextPrime', () => { + it('should find the next prime of 19', () => { + expect(nextPrime(38)).toBe(41); + }); + + it('should find the next prime of 11558', () => { + expect(nextPrime(11558)).toBe(11579); + }); + + it('should find the next prime of large number', () => { + expect(nextPrime(11579 * 2)).toBe(23159); + }); + + it('should find of negative number', () => { + expect(nextPrime(-1)).toBe(2); + }); + }); +}); diff --git a/docs/content/ds/data-structures/maps/hash-maps/readme.asc b/docs/content/ds/data-structures/maps/hash-maps/readme.asc new file mode 100644 index 0000000000..d06231ea04 --- /dev/null +++ b/docs/content/ds/data-structures/maps/hash-maps/readme.asc @@ -0,0 +1 @@ +include::../../../../book/content/part02/hash-map.asc[] diff --git a/docs/content/ds/data-structures/maps/map.js b/docs/content/ds/data-structures/maps/map.js new file mode 100644 index 0000000000..6c24842b0d --- /dev/null +++ b/docs/content/ds/data-structures/maps/map.js @@ -0,0 +1,18 @@ +/* JavaScript Built-in Map Usage +// tag::snippet[] +const myMap = new Map(); + +// mapping values to keys +myMap.set('string', 'foo'); // string as key +myMap.set(1, 'bar'); // number as key +myMap.set({}, 'baz'); // object as key +const obj1 = {}; +myMap.set(obj1, 'test'); + +// searching values by key +myMap.get(1); //↪️ bar +myMap.get('str'); //↪️ foo +myMap.get({}); //↪️ undefined +myMap.get(obj1); //↪️ test +// end::snippet[] +// */ diff --git a/docs/content/ds/data-structures/maps/map.spec.js b/docs/content/ds/data-structures/maps/map.spec.js new file mode 100644 index 0000000000..a69c70e3a6 --- /dev/null +++ b/docs/content/ds/data-structures/maps/map.spec.js @@ -0,0 +1,156 @@ +const { HashMap, TreeMap } = require('../../index'); + +const mapImplementations = [ + Map, + HashMap, + TreeMap, +]; + +mapImplementations.forEach((MapImplementation) => { + describe(`Map (common interface) with ${MapImplementation.name}`, () => { + let map; + + beforeEach(() => { + map = new MapImplementation(); + }); + + describe('#set', () => { + it('should save an object and increase size', () => { + expect(map.size).toBe(0); + map.set(1, 'test'); + map.set({}, 2); + expect(map.size).toBe(2); + }); + + it('should replace existing key its content', () => { + map.set(1, 'test1'); + map.set(1, 'test2'); + expect(map.size).toBe(1); + expect(map.get(1)).toBe('test2'); + }); + }); + + describe('#get', () => { + it('should save an object and increase size', () => { + const obj = {}; + map.set(1, 'test'); + map.set(obj, 2); + expect(map.get(1)).toBe('test'); + expect(map.get(obj)).toBe(2); + }); + + it('should handle when key is not present', () => { + expect(map.get(404)).toBe(undefined); + }); + }); + + describe('#has', () => { + it('should return true when key exists', () => { + map.set(1, 1); + expect(map.has(1)).toBe(true); + }); + + it('should return false when key is not present', () => { + expect(map.has(1)).toBe(false); + }); + }); + + describe('#delete', () => { + it('should return true if present', () => { + map.set(1, 1); + expect(map.delete(1)).toBe(true); + }); + + it('should return false if NOT present', () => { + expect(map.delete(1)).toBe(false); + }); + }); + + describe('#keys', () => { + beforeEach(() => { + map.set(1, 2); + map.set(2, 'dos'); + map.set(3, 3); + }); + + it('should return all keys', () => { + expect(Array.from(map.keys())).toEqual([1, 2, 3]); + }); + + it('should update on delete', () => { + expect(map.delete(1)).toBe(true); + expect(Array.from(map.keys())).toEqual([2, 3]); + }); + + it('should handle strings', () => { + map.set('uno', 1); + expect(Array.from(map.keys())).toEqual([1, 2, 3, 'uno']); + }); + + it('should handle objects', () => { + map.set({}, 1); + expect(Array.from(map.keys())).toEqual([1, 2, 3, {}]); + }); + + it('should handle null', () => { + map.set(null, 1); + expect([...map.keys()].sort()).toEqual([1, 2, 3, null].sort()); // ignoring order + }); + }); + + describe('#values', () => { + beforeEach(() => { + map.set(1, 2); + map.set(2, 'dos'); + map.set(3, 3); + }); + + it('should return all values', () => { + expect(Array.from(map.values())).toEqual([2, 'dos', 3]); + }); + + it('should update on delete', () => { + expect(map.delete(1)).toBe(true); + expect(Array.from(map.values())).toEqual(['dos', 3]); + }); + }); + + describe('#entries', () => { + beforeEach(() => { + map.set(1, 2); + map.set(2, 'dos'); + map.set(3, 3); + }); + + it('should return all entries', () => { + expect(Array.from(map.entries())).toEqual([ + [1, 2], + [2, 'dos'], + [3, 3], + ]); + }); + + it('should update on delete', () => { + expect(map.delete(1)).toBe(true); + expect(Array.from(map.entries())).toEqual([ + [2, 'dos'], + [3, 3], + ]); + }); + }); + + describe('#clear', () => { + beforeEach(() => { + map.set(1, 2); + map.set(2, 'dos'); + map.set(3, 3); + }); + + it('should work', () => { + expect(map.size).toBe(3); + expect(map.clear()).toEqual(); + expect(map.size).toBe(0); + }); + }); + }); +}); diff --git a/docs/content/ds/data-structures/maps/tree-maps/readme.asc b/docs/content/ds/data-structures/maps/tree-maps/readme.asc new file mode 100644 index 0000000000..15d5c2345b --- /dev/null +++ b/docs/content/ds/data-structures/maps/tree-maps/readme.asc @@ -0,0 +1 @@ +include::../../../../book/content/part03/tree-map.asc[] diff --git a/docs/content/ds/data-structures/maps/tree-maps/tree-map.js b/docs/content/ds/data-structures/maps/tree-maps/tree-map.js new file mode 100644 index 0000000000..10957aed53 --- /dev/null +++ b/docs/content/ds/data-structures/maps/tree-maps/tree-map.js @@ -0,0 +1,170 @@ +/* eslint-disable no-restricted-syntax */ +// const Tree = require('../../trees/binary-search-tree'); // unbalanced tree (slow everything) +// const Tree = require('../../trees/avl-tree'); // fast lookup +const Tree = require('../../trees/red-black-tree'); // fast insertion + +/** + * TreeMap is a Map implementation using a self-balanced tree + * such as AVL Tree or Red-Black tree to guarantee O(log n) operations. + * + * Implementing a Map with a tree, TreeMap, + * has a couple of advantages over a HashMap: + * • Keys are always sorted. + * • Statistical data can be easily obtained like median, + * highest, lowest key. + * • Collisions are not a concern so in the worst case is + * still O(log n). + * • Trees are more space efficient and doesn’t need to + * allocate memory beforehand (e.g. HashMap’s initial capacity) + * nor you have to rehash when is getting full. + * + * Implementations in other languages: + * Java: https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/TreeMap.html + * C++: https://en.cppreference.com/w/cpp/container/map + * Python: none + * + */ +class TreeMap { + // tag::constructor[] + /** + * Initialize tree + */ + constructor() { + this.tree = new Tree(); + } + // end::constructor[] + + // tag::set[] + /** + * Insert a key/value pair into the map. + * If the key is already there replaces its content. + * Runtime: O(log n) + * @param {any} key + * @param {any} value + * @returns {TreeNode} Return the Map object to allow chaining + */ + set(key, value) { + const node = this.tree.get(key); + if (node) { + node.data(value); + return node; + } + return this.tree.add(key).data(value); + } + + /** + * Size of the map + */ + get size() { + return this.tree.size; + } + // end::set[] + + // tag::get[] + /** + * Gets the value out of the map + * Runtime: O(log n) + * @param {any} key + * @returns {any} value associated to the key, or undefined if there is none. + */ + get(key) { + const node = this.tree.get(key) || undefined; + return node && node.data(); + } + + /** + * Search for key and return true if it was found + * Runtime: O(log n) + * @param {any} key + * @returns {boolean} indicating whether an element + * with the specified key exists or not. + */ + has(key) { + return !!this.get(key); + } + // end::get[] + + // tag::delete[] + /** + * Removes the specified element from the map. + * Runtime: O(log n) + * @param {*} key + * @returns {boolean} true if an element in the Map object existed + * and has been removed, or false if the element did not exist. + */ + delete(key) { + return this.tree.remove(key); + } + // end::delete[] + + /** + * Get the last key/value pair (node with largest key) + */ + lastEntry() { + const node = this.tree.getRightmost(); + return node ? [node.value, node.data()] : []; + } + + /** + * Get the first key/value pair (node with smallest key) + */ + firstEntry() { + const node = this.tree.getLeftmost(); + return node ? [node.value, node.data()] : []; + } + + // tag::iterators[] + /** + * Default iterator for this map + */ + * [Symbol.iterator]() { + yield* this.tree.inOrderTraversal(); // <1> + } + + /** + * Keys for each element in the Map object + * in order ascending order. + * @returns {Iterator} keys + */ + * keys() { + for (const node of this) { + yield node.value; + } + } + + /** + * Values for each element in the Map object + * in corresponding key ascending order. + * @returns {Iterator} values without holes (empty spaces of deleted values) + */ + * values() { + for (const node of this) { + yield node.data(); + } + } + // end::iterators[] + + /** + * Contains the [key, value] pairs for each element in the Map object + * in corresponding key ascending order. + * @returns {Iterator} + */ + * entries() { + for (const node of this) { + yield [node.value, node.data()]; + } + } + + /** + * Removes all key/value pairs from the Map object. + */ + clear() { + this.tree = new Tree(); + } +} + +// Aliases +TreeMap.prototype.containsKey = TreeMap.prototype.has; +TreeMap.prototype.put = TreeMap.prototype.set; + +module.exports = TreeMap; diff --git a/docs/content/ds/data-structures/maps/tree-maps/tree-map.spec.js b/docs/content/ds/data-structures/maps/tree-maps/tree-map.spec.js new file mode 100644 index 0000000000..8661207a65 --- /dev/null +++ b/docs/content/ds/data-structures/maps/tree-maps/tree-map.spec.js @@ -0,0 +1,43 @@ +// some parts tested on src/data-structures/maps/map.spec.js + +const TreeMap = require('./tree-map'); + +describe('TreeMap: keep values sorted', () => { + let map; + + beforeEach(() => { + map = new TreeMap(); + }); + + describe('when map is empty', () => { + describe('.lastEntry and .firstEntry', () => { + it('should get last/first entry', () => { + expect(map.lastEntry()).toEqual([]); + expect(map.firstEntry()).toEqual([]); + }); + }); + }); + + describe('when map has entries', () => { + beforeEach(() => { + map.set(20, { title: '3sum', passed: true }); + map.set(30, { title: '3sum', passed: false }); + map.set(10, { title: '2sum', passed: true }); + map.set(5, { title: '4sum', passed: false }); + }); + + describe('.lastEntry and .firstEntry', () => { + it('should get last/first entry', () => { + expect(map.lastEntry()).toEqual([ + 30, + { title: '3sum', passed: false }, + ]); + + expect(map.firstEntry()).toEqual([ + 5, + { title: '4sum', passed: false }, + ]); + }); + }); + }); +}); diff --git a/docs/content/ds/data-structures/queues/README.adoc b/docs/content/ds/data-structures/queues/README.adoc new file mode 100644 index 0000000000..68aa475ef4 --- /dev/null +++ b/docs/content/ds/data-structures/queues/README.adoc @@ -0,0 +1 @@ +include::../../../book/content/part02/queue.asc[] diff --git a/docs/content/ds/data-structures/queues/queue-1.js b/docs/content/ds/data-structures/queues/queue-1.js new file mode 100644 index 0000000000..b82c9721ee --- /dev/null +++ b/docs/content/ds/data-structures/queues/queue-1.js @@ -0,0 +1,37 @@ +/** + * Data structure where add and remove elements in a first-in, first-out (FIFO) + */ +class Queue { + constructor() { + this.input = []; + } + + /** + * Add element to the queue + * Insert to the end of the array + * Runtime: O(1) + * @param {any} element + */ + add(element) { + this.input.push(element); + } + + /** + * Add element to the queue + * Removing from the beginning of the array + * Runtime: O(n) + * @param {any} element + */ + remove() { + return this.input.shift(); + } + + /** + * Size of the queue + */ + get size() { + return this.input.length; + } +} + +module.exports = Queue; diff --git a/docs/content/ds/data-structures/queues/queue-2.js b/docs/content/ds/data-structures/queues/queue-2.js new file mode 100644 index 0000000000..9bf4bf9589 --- /dev/null +++ b/docs/content/ds/data-structures/queues/queue-2.js @@ -0,0 +1,45 @@ +/** + * Data structure where add and remove elements in a first-in, first-out (FIFO) + */ +class Queue { + constructor() { + this.input = []; + this.output = []; + } + + /** + * Add element to the queue + * Runtime: O(1) + * @param {any} element + */ + add(element) { + this.input.push(element); + } + + /** + * Remove element from the queue + * Runtime: O(n) + * Amortized runtime: O(1)* + */ + remove() { + if (!this.output.length) { + while (this.input.length) { + this.output.push(this.input.pop()); + } + } + return this.output.pop(); + } + + /** + * Size of the queue + */ + get size() { + return this.input.length + this.output.length; + } +} + +// Aliases +Queue.prototype.enqueue = Queue.prototype.add; +Queue.prototype.dequeue = Queue.prototype.remove; + +module.exports = Queue; diff --git a/docs/content/ds/data-structures/queues/queue.js b/docs/content/ds/data-structures/queues/queue.js new file mode 100644 index 0000000000..b62c57ae9c --- /dev/null +++ b/docs/content/ds/data-structures/queues/queue.js @@ -0,0 +1,89 @@ +const LinkedList = require('../linked-lists/linked-list'); + +/* Usage Example: +// tag::snippet[] +const queue = new Queue(); + +queue.enqueue('a'); +queue.enqueue('b'); +queue.dequeue(); //↪️ a +queue.enqueue('c'); +queue.dequeue(); //↪️ b +queue.dequeue(); //↪️ c +// end::snippet[] +// */ + +// tag::constructor[] +/** + * Data structure where we add and remove elements in a first-in, first-out (FIFO) fashion + */ +class Queue { + constructor(iterable = []) { + this.items = new LinkedList(iterable); + } + // end::constructor[] + + // tag::enqueue[] + /** + * Add element to the back of the queue. + * Runtime: O(1) + * @param {any} item + * @returns {queue} instance to allow chaining. + */ + enqueue(item) { + this.items.addLast(item); + return this; + } + // end::enqueue[] + + // tag::dequeue[] + /** + * Remove element from the front of the queue. + * Runtime: O(1) + * @returns {any} removed value. + */ + dequeue() { + return this.items.removeFirst(); + } + + // end::dequeue[] + /** + * Size of the queue + */ + get size() { + return this.items.size; + } + + /** + * Return true if is empty false otherwise true + */ + isEmpty() { + return !this.items.size; + } + + /** + * Return the most recent value or null if empty. + */ + back() { + if (this.isEmpty()) return null; + return this.items.last.value; + } + + /** + * Return oldest value from the queue or null if empty. + * (Peek at the next value to be dequeue) + */ + front() { + if (this.isEmpty()) return null; + return this.items.first.value; + } +} + +// Aliases +Queue.prototype.peek = Queue.prototype.front; +Queue.prototype.add = Queue.prototype.enqueue; +Queue.prototype.push = Queue.prototype.enqueue; +Queue.prototype.remove = Queue.prototype.dequeue; +Queue.prototype.pop = Queue.prototype.dequeue; + +module.exports = Queue; diff --git a/docs/content/ds/data-structures/queues/queue.spec.js b/docs/content/ds/data-structures/queues/queue.spec.js new file mode 100644 index 0000000000..289e0e39b1 --- /dev/null +++ b/docs/content/ds/data-structures/queues/queue.spec.js @@ -0,0 +1,73 @@ +const { Queue } = require('../../index'); + +describe('Queue', () => { + let queue; + + beforeEach(() => { + queue = new Queue(); + }); + + describe('#add', () => { + it('should push an element to the queue', () => { + expect(queue.size).toEqual(0); + queue.add(1); + expect(queue.size).toEqual(1); + }); + }); + + describe('#remove', () => { + beforeEach(() => { + queue.add('a'); + queue.add('b'); + }); + + it('should get last element entered', () => { + expect(queue.remove()).toEqual('a'); + expect(queue.remove()).toEqual('b'); + }); + + it('should keep the order after some addition', () => { + expect(queue.remove()).toEqual('a'); + queue.add('c'); + expect(queue.remove()).toEqual('b'); + expect(queue.remove()).toEqual('c'); + expect(queue.remove()).toBe(null); + }); + }); + + describe('#isEmpty', () => { + it('should return true when empty', () => { + expect(queue.isEmpty()).toBe(true); + }); + + it('should return false when not empty', () => { + queue.add('a'); + // expect(queue.size).toBe(1); + expect(queue.isEmpty()).toBe(false); + }); + }); + + describe('#back', () => { + it('should return null if empty', () => { + expect(queue.back()).toEqual(null); + }); + + it('should return newest element', () => { + queue.enqueue('oldest'); + queue.enqueue('newest'); + expect(queue.back()).toEqual('newest'); + }); + }); + + describe('#front', () => { + it('should return null if empty', () => { + expect(queue.front()).toEqual(null); + }); + + it('should return oldest element', () => { + queue.enqueue('oldest'); + queue.enqueue('newest'); + expect(queue.front()).toEqual('oldest'); + }); + }); +}); diff --git a/docs/content/ds/data-structures/sets/README.adoc b/docs/content/ds/data-structures/sets/README.adoc new file mode 100644 index 0000000000..ce08b5519b --- /dev/null +++ b/docs/content/ds/data-structures/sets/README.adoc @@ -0,0 +1 @@ +include::../../../book/content/part02/hash-set.asc[] diff --git a/docs/content/ds/data-structures/sets/array-set.js b/docs/content/ds/data-structures/sets/array-set.js new file mode 100644 index 0000000000..180c522376 --- /dev/null +++ b/docs/content/ds/data-structures/sets/array-set.js @@ -0,0 +1,87 @@ +/** + * Set implemented with our HashMap to have sublinear times on all operations + */ +class ArraySet { + /** + * Initialize Set using a HashMap + * + * @param {Array} iterable If passed, all iterable elements will be added to the new Set + */ + constructor(iterable = []) { + this.array = []; + Array.from(iterable).forEach((element) => this.add(element)); + } + + /** + * Add a new value + * + * Runtime: O(1) + * + * @param {any} value + */ + add(value) { + if (!this.has(value)) { + this.array.push(value); + } + } + + /** + * check if value is already on the set + * + * Runtime: O(n) + * + * @param {any} value + */ + has(value) { + return this.array.indexOf(value) > -1; + } + + /** + * Delete a value from the set + * + * Runtime: O(n) + * + * @param {any} value + */ + delete(value) { + const index = this.array.indexOf(value); + this.array.splice(index, 1); + return index > -1; + } + + /** + * Get size of the set + */ + get size() { + return this.array.length; + } + + /** + * Make this class iterable + */ + [Symbol.iterator]() { + return this.array[Symbol.iterator](); + } + + /** + * Get all the values on the Set + * @returns {iterator} values in insertion order + */ + * keys() { + yield* this; + } + + /** + * This is kept similar to the Map object, so that each entry has the + * same value for its key and value here. + * @returns {iterator} new Iterator object that contains[value, value] + * for each element in the Set object, in ascending order. + */ + * entries() { + for (const value of this) { + yield [value, value]; + } + } +} + +module.exports = ArraySet; diff --git a/docs/content/ds/data-structures/sets/hash-set.js b/docs/content/ds/data-structures/sets/hash-set.js new file mode 100644 index 0000000000..0817a56076 --- /dev/null +++ b/docs/content/ds/data-structures/sets/hash-set.js @@ -0,0 +1,89 @@ +const HashMap = require('../maps/hash-maps/hash-map'); +// tag::constructor[] +/** + * Set implemented with our HashMap + * Have an average of O(1) time on all operations + */ +class HashMapSet { + /** + * Initialize (Hash)map for the set + * + * @param {Array} iterable If passed, all iterable elements will be added to the new Set + */ + constructor(iterable = []) { + this.hashMap = new HashMap(); + Array.from(iterable).forEach((element) => this.add(element)); + } + + /** + * Get size of the set + */ + get size() { + return this.hashMap.size; + } + // end::constructor[] + + // tag::add[] + /** + * Add a new value (duplicates will be added only once) + * Avg. Runtime: O(1) + * @param {any} value + */ + add(value) { + this.hashMap.set(value); + } + // end::add[] + + // tag::has[] + /** + * Check if value is already on the set + * Avg. Runtime: O(1) + * @param {any} value + */ + has(value) { + return this.hashMap.has(value); + } + // end::has[] + + // tag::delete[] + /** + * Delete a value from the set + * Avg. Runtime: O(1) + * @param {any} value + */ + delete(value) { + return this.hashMap.delete(value); + } + // end::delete[] + + // tag::iterators[] + /** + * Make this class iterable + */ + * [Symbol.iterator]() { + yield* this.hashMap.keys(); + } + + /** + * Get all the values on the Set + * @returns {iterator} values in insertion order + */ + * keys() { + yield* this; + } + + /** + * This is kept similar to the Map object, so that each entry has the + * same value for its key and value here. + * @returns {iterator} new Iterator object that contains[value, value] + * for each element in the Set object, in ascending order. + */ + * entries() { + for (const value of this) { + yield [value, value]; + } + } +// end::iterators[] +} + +module.exports = HashMapSet; diff --git a/docs/content/ds/data-structures/sets/map-set.js b/docs/content/ds/data-structures/sets/map-set.js new file mode 100644 index 0000000000..fdb42391d0 --- /dev/null +++ b/docs/content/ds/data-structures/sets/map-set.js @@ -0,0 +1,74 @@ +/** + * Set implemented with Map (JS built-in) to have sublinear times on all operations + */ +class MapSet { + /** + * Initialize Set using the built-in Map + * + * @param {Array} iterable If passed, all iterable elements will be added to the new Set + */ + constructor(iterable = []) { + this.map = new Map(); + Array.from(iterable).forEach((element) => this.add(element)); + } + + /** + * Add a new value + * @param {any} value + */ + add(value) { + this.map.set(value); + } + + /** + * check if value is already on the set + * @param {any} value + */ + has(value) { + return this.map.has(value); + } + + /** + * Get size of the set + */ + get size() { + return this.map.size; + } + + /** + * Delete a value from the set + * @param {any} value + */ + delete(value) { + return this.map.delete(value); + } + + /** + * Make this class iterable + */ + * [Symbol.iterator]() { + yield* this.map.keys(); + } + + /** + * Get all the values on the Set + * @returns {iterator} values in insertion order + */ + * keys() { + yield* this; + } + + /** + * This is kept similar to the Map object, so that each entry has the + * same value for its key and value here. + * @returns {iterator} new Iterator object that contains[value, value] + * for each element in the Set object, in ascending order. + */ + * entries() { + for (const value of this) { + yield [value, value]; + } + } +} + +module.exports = MapSet; diff --git a/docs/content/ds/data-structures/sets/set.js b/docs/content/ds/data-structures/sets/set.js new file mode 100644 index 0000000000..304d955074 --- /dev/null +++ b/docs/content/ds/data-structures/sets/set.js @@ -0,0 +1,3 @@ +const HashSet = require('./hash-set'); + +module.exports = HashSet; diff --git a/docs/content/ds/data-structures/sets/set.spec.js b/docs/content/ds/data-structures/sets/set.spec.js new file mode 100644 index 0000000000..9a9e6200ba --- /dev/null +++ b/docs/content/ds/data-structures/sets/set.spec.js @@ -0,0 +1,77 @@ +const { + ArraySet, HashSet, MapSet, TreeSet, +} = require('../../index'); + +const setImplementations = [Set, HashSet, TreeSet, MapSet, ArraySet]; + +setImplementations.forEach((MySet) => { + describe(`Set (${MySet.name})`, () => { + let set; + + beforeEach(() => { + set = new MySet(); + }); + + it('should set size and has', () => { + expect(set.size).toBe(0); + expect(set.has('uno')).toBe(false); + + set.add('uno'); + + expect(set.size).toBe(1); + expect(set.has('uno')).toBe(true); + }); + + it('should not allow duplicates', () => { + set.add('uno'); + set.add('one'); + set.add('uno'); + + expect(set.size).toBe(2); + expect(set.has('uno')).toBe(true); + expect(set.has('one')).toBe(true); + }); + + it('should delete items', () => { + expect(set.delete('uno')).toBe(false); + + set.add('uno'); + + expect(set.delete('uno')).toBe(true); + expect(set.size).toBe(0); + }); + + it('should return entries', () => { + set.add(1); + set.add(2); + set.add(3); + expect([...set.entries()]).toEqual([[1, 1], [2, 2], [3, 3]]); + }); + + it('should return entries wihout holes', () => { + set.add(0); + set.add(1); + set.add(2); + set.add(3); + + expect(set.delete(2)).toBe(true); + expect(Array.from(set.entries())).toEqual([[0, 0], [1, 1], [3, 3]]); + expect(set.delete(0)).toBe(true); + expect(Array.from(set.entries())).toEqual([[1, 1], [3, 3]]); + + expect(Array.from(set)).toEqual([1, 3]); + expect(set.size).toBe(2); + }); + + it('should initialize with data provided', () => { + set = new MySet([1, 2, 3, 1]); + expect(set.size).toBe(3); + expect(Array.from(set.keys())).toEqual([1, 2, 3]); + }); + + it('should return an iterable', () => { + set = new MySet([1, 2, 3, 1]); + expect(Array.from(set)).toEqual([1, 2, 3]); + }); + }); +}); diff --git a/docs/content/ds/data-structures/sets/tree-set.js b/docs/content/ds/data-structures/sets/tree-set.js new file mode 100644 index 0000000000..1483e63f45 --- /dev/null +++ b/docs/content/ds/data-structures/sets/tree-set.js @@ -0,0 +1,99 @@ +// faster lookups +// const Tree = require('../trees/avl-tree'); + +// faster insertion +// tag::constructor[] +const Tree = require('../trees/red-black-tree'); + +/** + * TreeSet implements a Set (collection of unique values) + * using a balanced binary search tree to guarantee a O(log n) in all operations. + */ +class TreeSet { + /** + * Initialize tree and accept initial values. + * @param {array} iterable initial values (new set won't have duplicates) + */ + constructor(iterable = []) { + this.tree = new Tree(); + Array.from(iterable).forEach((value) => this.add(value)); // <1> + } + + /** + * Size of the set + */ + get size() { + return this.tree.size; + } + // end::constructor[] + + // tag::add[] + /** + * Add a new value (duplicates will be added only once) + * Runtime: O(log n) + * @param {any} value + */ + add(value) { + if (!this.has(value)) { + this.tree.add(value); + } + } + // end::add[] + + // tag::has[] + /** + * Check if value is already on the set + * Runtime: O(log n) + * @param {any} value + * @returns {boolean} true if exists or false otherwise + */ + has(value) { + return this.tree.has(value); + } + // end::has[] + + // tag::delete[] + /** + * Delete a value from the set + * Runtime: O(log n) + * @param {any} value + */ + delete(value) { + return this.tree.remove(value); + } + // end::delete[] + + // tag::iterator[] + /** + * Default iterator for this set + * @returns {iterator} values in ascending order + */ + * [Symbol.iterator]() { + for (const node of this.tree.inOrderTraversal()) { + yield node.value; + } + } + // end::iterator[] + + /** + * Get all the values on the Set + * @returns {iterator} values in ascending order + */ + * keys() { + yield* this; + } + + /** + * This is kept similar to the Map object, so that each entry has the + * same value for its key and value here. + * @returns {iterator} new Iterator object that contains[value, value] + * for each element in the Set object, in ascending order. + */ + * entries() { + for (const value of this) { + yield [value, value]; + } + } +} + +module.exports = TreeSet; diff --git a/docs/content/ds/data-structures/stacks/README.adoc b/docs/content/ds/data-structures/stacks/README.adoc new file mode 100644 index 0000000000..7cdcbe0e4b --- /dev/null +++ b/docs/content/ds/data-structures/stacks/README.adoc @@ -0,0 +1 @@ +include::../../../book/content/part02/stack.asc[] diff --git a/docs/content/ds/data-structures/stacks/stack-1.js b/docs/content/ds/data-structures/stacks/stack-1.js new file mode 100644 index 0000000000..5116909791 --- /dev/null +++ b/docs/content/ds/data-structures/stacks/stack-1.js @@ -0,0 +1,39 @@ +/** + * Data structure that adds and remove elements in a last-in, first-out (LIFO) fashion + */ +class Stack { + constructor() { + this.input = []; + } + + /** + * Add element into the stack + * Runtime: O(1) + * @param {any} element + */ + add(element) { + this.input.push(element); + return this; + } + + /** + * Remove element from the stack + * Runtime: O(1) + */ + remove() { + return this.input.pop(); + } + + /** + * Size of the queue + */ + get size() { + return this.input.length; + } +} + +// aliases +Stack.prototype.push = Stack.prototype.add; +Stack.prototype.pop = Stack.prototype.remove; + +module.exports = Stack; diff --git a/docs/content/ds/data-structures/stacks/stack.js b/docs/content/ds/data-structures/stacks/stack.js new file mode 100644 index 0000000000..e96a6807bb --- /dev/null +++ b/docs/content/ds/data-structures/stacks/stack.js @@ -0,0 +1,69 @@ +const LinkedList = require('../linked-lists/linked-list'); +// tag::constructor[] +/** + * Data structure that adds and remove elements in a first-in, first-out (FIFO) fashion + */ +class Stack { + constructor() { + this.items = new LinkedList(); + } + // end::constructor[] + + // tag::add[] + /** + * Add element into the stack. Similar to Array.push + * Runtime: O(1) + * @param {any} item + * @returns {stack} instance to allow chaining. + */ + add(item) { + this.items.addLast(item); + return this; + } + // end::add[] + + // tag::remove[] + /** + * Remove element from the stack. + * Similar to Array.pop + * Runtime: O(1) + * @returns {any} removed value. + */ + remove() { + return this.items.removeLast(); + } + // end::remove[] + + /** + * Size of the queue + */ + get size() { + return this.items.size; + } + + /** + * Return true if is empty false otherwise true + */ + isEmpty() { + return !this.items.size; + } +} + +// aliases +Stack.prototype.push = Stack.prototype.add; +Stack.prototype.pop = Stack.prototype.remove; + +module.exports = Stack; + +/* Usage Example: +// tag::snippet[] +const stack = new Stack(); + +stack.add('a'); +stack.add('b'); +stack.remove(); //↪️ b +stack.add('c'); +stack.remove(); //↪️ c +stack.remove(); //↪️ a +// end::snippet[] +// */ diff --git a/docs/content/ds/data-structures/stacks/stack.spec.js b/docs/content/ds/data-structures/stacks/stack.spec.js new file mode 100644 index 0000000000..6fa472f328 --- /dev/null +++ b/docs/content/ds/data-structures/stacks/stack.spec.js @@ -0,0 +1,43 @@ +const { Stack } = require('../../index'); + +describe('Stack', () => { + let stack; + + beforeEach(() => { + stack = new Stack(); + }); + + describe('#push', () => { + it('should push an element to the stack', () => { + expect(stack.size).toEqual(0); + stack.push(1); + expect(stack.size).toEqual(1); + }); + }); + + describe('#pop', () => { + beforeEach(() => { + stack.push('a'); + stack.push('b'); + }); + + it('should get last element entered', () => { + expect(stack.pop()).toEqual('b'); + expect(stack.pop()).toEqual('a'); + expect(stack.pop()).toEqual(null); + }); + }); + + describe('#isEmpty', () => { + it('should return true when empty', () => { + // expect(stack.size).toBe(0); + expect(stack.isEmpty()).toBe(true); + }); + + it('should return false when not empty', () => { + stack.add('a'); + // expect(stack.size).toBe(1); + expect(stack.isEmpty()).toBe(false); + }); + }); +}); diff --git a/docs/content/ds/data-structures/trees/README.adoc b/docs/content/ds/data-structures/trees/README.adoc new file mode 100644 index 0000000000..2a6527380a --- /dev/null +++ b/docs/content/ds/data-structures/trees/README.adoc @@ -0,0 +1,16 @@ +include::../../../book/content/part03/tree-intro.asc[] + +<<< +include::../../../book/content/part03/binary-search-tree.asc[] + +<<< +include::../../../book/content/part03/tree-search-traversal.asc[] + +<<< +include::../../../book/content/part03/binary-search-tree-traversal.asc[] + +<<< +include::../../../book/B-self-balancing-binary-search-trees.asc[] + +<<< +include::../../../book/C-AVL-tree.asc[] diff --git a/docs/content/ds/data-structures/trees/avl-tree.js b/docs/content/ds/data-structures/trees/avl-tree.js new file mode 100644 index 0000000000..272a16e1bb --- /dev/null +++ b/docs/content/ds/data-structures/trees/avl-tree.js @@ -0,0 +1,90 @@ +const BinarySearchTree = require('./binary-search-tree'); +const { + leftRotation, + rightRotation, + leftRightRotation, + rightLeftRotation, +} = require('./tree-rotations'); + +// tag::balance[] +/** + * Balance tree doing rotations based on balance factor. + * + * Depending on the `node` balance factor and child's factor + * one of this rotation is performed: + * - LL rotations: single left rotation + * - RR rotations: single right rotation + * - LR rotations: double rotation left-right + * - RL rotations: double rotation right-left + * + * @param {BinaryTreeNode} node + */ +function balance(node) { + if (node.balanceFactor > 1) { + // left subtree is higher than right subtree + if (node.left.balanceFactor < 0) { + return leftRightRotation(node); + } + return rightRotation(node); + } if (node.balanceFactor < -1) { + // right subtree is higher than left subtree + if (node.right.balanceFactor > 0) { + return rightLeftRotation(node); + } + return leftRotation(node); + } + return node; +} +// end::balance[] + +// tag::balanceUpstream[] +/** + * Bubbles up balancing nodes a their parents + * + * @param {BinaryTreeNode} node + */ +function balanceUpstream(node) { + let current = node; + let newParent; + while (current) { + newParent = balance(current); + current = current.parent; + } + return newParent; +} +// end::balanceUpstream[] + +// tag::AvlTree[] +/** + * AVL Tree + * It's a self-balanced binary search tree optimized for fast lookups. + */ +class AvlTree extends BinarySearchTree { + /** + * Add node to tree. It self-balance itself. + * @param {any} value node's value + */ + add(value) { + const node = super.add(value); + this.root = balanceUpstream(node); + return node; + } + + /** + * Remove node if it exists and re-balance tree + * @param {any} value + */ + remove(value) { + const node = super.find(value); + if (node) { + const found = super.remove(value); + this.root = balanceUpstream(node.parent); + return found; + } + + return false; + } +} +// end::AvlTree[] + +module.exports = AvlTree; diff --git a/docs/content/ds/data-structures/trees/avl-tree.spec.js b/docs/content/ds/data-structures/trees/avl-tree.spec.js new file mode 100644 index 0000000000..c45ff94ec2 --- /dev/null +++ b/docs/content/ds/data-structures/trees/avl-tree.spec.js @@ -0,0 +1,229 @@ +const AvlTree = require('./avl-tree.js'); + +describe('AvlTree', () => { + let tree; + + beforeEach(() => { + tree = new AvlTree(); + }); + + describe('#contructor', () => { + it('should initialize', () => { + expect(tree).not.toBe(null); + }); + }); + + describe('#add', () => { + it('should add a node', () => { + expect(tree.size).toBe(0); + const n1 = tree.add(1); + expect(tree.size).toBe(1); + expect(n1.height).toBe(0); + }); + + it('should rebalance asc inserted nodes (LL Rotation)', () => { + tree.add(1); + const n = tree.add(2); + tree.add(3); + + expect(n.toValues()).toMatchObject({ + value: 2, left: 1, right: 3, parent: null, + }); + + expect(tree.toArray()).toEqual([2, 1, 3, null, null, null, null]); + expect(tree.root).toBe(n); + }); + + it('should rebalance desc inserted nodes (RR Rotation)', () => { + tree.add(3); + const n = tree.add(2); + tree.add(1); + + expect(n.toValues()).toMatchObject({ + value: 2, left: 1, right: 3, parent: null, + }); + expect(tree.root).toBe(n); + }); + + it('should rebalance with LR Rotation', () => { + tree.add(3); + tree.add(1); + const n = tree.add(2); + + expect(n.toValues()).toMatchObject({ + value: 2, left: 1, right: 3, parent: null, + }); + expect(tree.root).toBe(n); + }); + + + it('should rebalance with RL Rotation', () => { + tree.add(1); + tree.add(3); + const n = tree.add(2); + + expect(n.toValues()).toMatchObject({ + value: 2, left: 1, right: 3, parent: null, + }); + expect(tree.root).toBe(n); + }); + + it('should not balance', () => { + [30, 20, 40, 10, 50].forEach((v) => tree.add(v)); + + expect(tree.toArray()).toEqual([ + 30, + 20, 40, + 10, null, null, 50, + null, null, null, null, + ]); + + expect(tree.find(40).balanceFactor).toBe(-1); // l: 0, r: 1, bf: -1 + expect(tree.find(20).balanceFactor).toBe(1); // l: 1, r: 0, bf: 1 + expect(tree.find(30).balanceFactor).toBe(0); // l: 2, r: 2, bf: 0 + }); + }); + + describe('#remove', () => { + it('should rebalance asc inserted nodes (LL Rotation)', () => { + tree.add(1); + tree.add(-1); + const n = tree.add(2); + tree.add(3); + + tree.remove(-1); + + expect(n.toValues()).toMatchObject({ + value: 2, left: 1, right: 3, parent: null, + }); + + expect(tree.root).toBe(n); + expect(tree.toArray()).toEqual([2, 1, 3, null, null, null, null]); + }); + + it('should rebalance desc inserted nodes (RR Rotation)', () => { + tree.add(3); + tree.add(4); + const n = tree.add(2); + tree.add(1); + + tree.remove(4); + + expect(n.toValues()).toMatchObject({ + value: 2, left: 1, right: 3, parent: null, + }); + expect(tree.root).toBe(n); + }); + + it('should rebalance with LR Rotation', () => { + tree.add(3); + tree.add(4); + tree.add(1); + const n = tree.add(2); + + tree.remove(4); + + expect(n.toValues()).toMatchObject({ + value: 2, left: 1, right: 3, parent: null, + }); + expect(tree.root).toBe(n); + }); + + + it('should rebalance with RL Rotation', () => { + tree.add(1); + tree.add(-1); + tree.add(3); + const n = tree.add(2); + + tree.remove(-1); + + expect(n.toValues()).toMatchObject({ + value: 2, left: 1, right: 3, parent: null, + }); + expect(tree.root).toBe(n); + }); + }); + + describe('balance without loosing nodes', () => { + beforeEach(() => { + tree.add(16); + tree.add(4); + tree.add(32); + tree.add(8); + tree.add(2); + }); + + it('should have all nodes', () => { + expect(tree.toArray()).toEqual([16, 4, 32, 2, 8, + null, null, null, null, null, null]); + }); + + it('should rebalance and keep all nodes', () => { + tree.add(1); + expect(tree.toArray()).toEqual([4, 2, 16, 1, null, 8, 32, + null, null, null, null, null, null]); + }); + }); + + describe('balancing to the left', () => { + let n32; + beforeEach(() => { + n32 = tree.add(32); + tree.add(8); + tree.add(64); + tree.add(4); + tree.add(16); + tree.add(48); + tree.add(128); + tree.add(2); + tree.add(6); + tree.add(10); + tree.add(20); + }); + + it('should have all nodes', () => { + expect(tree.toArray()).toEqual([32, 8, 64, 4, 16, 48, 128, 2, 6, 10, 20, + null, null, null, null, null, null, null, null, null, null, null, null]); + }); + + it('should rebalance when removing', () => { + tree.remove(64); + expect(tree.toArray()).toEqual([32, 8, 128, 4, 16, 48, null, 2, 6, 10, 20, + null, null, null, null, null, null, null, null, null, null]); + expect(n32.balanceFactor).toBe(1); + expect(n32.right.balanceFactor).toBe(1); + expect(n32.left.balanceFactor).toBe(0); + + tree.remove(48); + expect(tree.toArray()).toEqual([8, 4, 32, 2, 6, 16, 128, null, null, null, null, 10, 20, + null, null, null, null, null, null]); + }); + }); + + describe('balancing to the right', () => { + beforeEach(() => { + tree.add(8); + tree.add(4); + tree.add(32); + tree.add(2); + tree.add(16); + tree.add(64); + tree.add(10); + tree.add(20); + tree.add(60); + tree.add(70); + }); + + it('should build the tree', () => { + expect(tree.toArray()).toEqual([8, 4, 32, 2, null, 16, 64, null, null, 10, 20, 60, 70, + null, null, null, null, null, null, null, null]); + }); + + it('should rebalance right side', () => { + tree.remove(2); + expect(tree.toArray()).toEqual([32, 8, 64, 4, 16, 60, 70, null, null, 10, 20, + null, null, null, null, null, null, null, null]); + }); + }); +}); diff --git a/docs/content/ds/data-structures/trees/binary-search-tree.js b/docs/content/ds/data-structures/trees/binary-search-tree.js new file mode 100644 index 0000000000..e7a5cc49dd --- /dev/null +++ b/docs/content/ds/data-structures/trees/binary-search-tree.js @@ -0,0 +1,308 @@ +const BinaryTreeNode = require('./binary-tree-node'); +const Queue = require('../queues/queue'); +const Stack = require('../stacks/stack'); +// tag::snippet[] +class BinarySearchTree { + constructor() { + this.root = null; + this.size = 0; + } + // end::snippet[] + + // tag::add[] + /** + * Insert value on the BST. + * + * If the value is already in the tree, + * then it increases the multiplicity value + * @param {any} value node's value to insert in the tree + * @returns {BinaryTreeNode} newly added node + */ + add(value) { + let node = new BinaryTreeNode(value); + + if (this.root) { + const { found, parent } = this.findNodeAndParent(value); // <1> + if (found) { // duplicated: value already exist on the tree + found.meta.multiplicity = (found.meta.multiplicity || 1) + 1; // <2> + node = found; + } else if (value < parent.value) { + parent.setLeftAndUpdateParent(node); + } else { + parent.setRightAndUpdateParent(node); + } + } else { + this.root = node; + } + + this.size += 1; + return node; + } + // end::add[] + + /** + * Find if a node is present or not + * @param {any} value node to find + * @returns {boolean} true if is present, false otherwise + */ + has(value) { + return !!this.find(value); + } + + // tag::find[] + /** + * @param {any} value value to find + * @returns {BinaryTreeNode|null} node if it found it or null if not + */ + find(value) { + return this.findNodeAndParent(value).found; + } + + + /** + * Recursively finds the node matching the value. + * If it doesn't find, it returns the leaf `parent` where the new value should be appended. + * @param {any} value Node's value to find + * @param {BinaryTreeNode} node first element to start the search (root is default) + * @param {BinaryTreeNode} parent keep track of parent (usually filled by recursion) + * @returns {object} node and its parent like {node, parent} + */ + findNodeAndParent(value, node = this.root, parent = null) { + if (!node || node.value === value) { + return { found: node, parent }; + } if (value < node.value) { + return this.findNodeAndParent(value, node.left, node); + } + return this.findNodeAndParent(value, node.right, node); + } + // end::find[] + + /** + * Get the node with the max value of subtree: the right-most value. + * @param {BinaryTreeNode} node subtree's root + * @returns {BinaryTreeNode} right-most node (max value) + */ + getRightmost(node = this.root) { + if (!node || !node.right) { + return node; + } + return this.getMax(node.right); + } + + // tag::leftMost[] + /** + * Get the node with the min value of subtree: the left-most value. + * @param {BinaryTreeNode} node subtree's root + * @returns {BinaryTreeNode} left-most node (min value) + */ + getLeftmost(node = this.root) { + if (!node || !node.left) { + return node; + } + return this.getMin(node.left); + } + // end::leftMost[] + + + // tag::remove[] + /** + * Remove a node from the tree + * @returns {boolean} false if not found and true if it was deleted + */ + remove(value) { + const { found: nodeToRemove, parent } = this.findNodeAndParent(value); // <1> + + if (!nodeToRemove) return false; // <2> + + // Combine left and right children into one subtree without nodeToRemove + const removedNodeChildren = this.combineLeftIntoRightSubtree(nodeToRemove); // <3> + + if (nodeToRemove.meta.multiplicity && nodeToRemove.meta.multiplicity > 1) { // <4> + nodeToRemove.meta.multiplicity -= 1; // handles duplicated + } else if (nodeToRemove === this.root) { // <5> + // Replace (root) node to delete with the combined subtree. + this.root = removedNodeChildren; + if (this.root) { this.root.parent = null; } // clearing up old parent + } else if (nodeToRemove.isParentLeftChild) { // <6> + // Replace node to delete with the combined subtree. + parent.setLeftAndUpdateParent(removedNodeChildren); + } else { + parent.setRightAndUpdateParent(removedNodeChildren); + } + + this.size -= 1; + return true; + } + // end::remove[] + + // tag::combine[] + /** + * Combine left into right children into one subtree without given parent node. + * + * @example combineLeftIntoRightSubtree(30) + * + * 30* 40 + * / \ / \ + * 10 40 combined 35 50 + * \ / \ ----------> / + * 15 35 50 10 + * \ + * 15 + * + * It takes node 30 left subtree (10 and 15) and put it in the + * leftmost node of the right subtree (40, 35, 50). + * + * @param {BinaryTreeNode} node + * @returns {BinaryTreeNode} combined subtree + */ + combineLeftIntoRightSubtree(node) { + if (node.right) { + const leftmost = this.getLeftmost(node.right); + leftmost.setLeftAndUpdateParent(node.left); + return node.right; + } + return node.left; + } + // end::combine[] + + // tag::bfs[] + /** + * Breath-first search for a tree (always starting from the root element). + * @yields {BinaryTreeNode} + */ + * bfs() { + const queue = new Queue(); + + queue.add(this.root); + + while (!queue.isEmpty()) { + const node = queue.remove(); + yield node; + + if (node.left) { queue.add(node.left); } + if (node.right) { queue.add(node.right); } + } + } + // end::bfs[] + + // tag::dfs[] + /** + * Depth-first search for a tree (always starting from the root element) + * @see preOrderTraversal Similar results to the pre-order transversal. + * @yields {BinaryTreeNode} + */ + * dfs() { + const stack = new Stack(); + + stack.add(this.root); + + while (!stack.isEmpty()) { + const node = stack.remove(); + yield node; + + if (node.right) { stack.add(node.right); } + if (node.left) { stack.add(node.left); } + } + } + // end::dfs[] + + // tag::inOrderTraversal[] + /** + * In-order traversal on a tree: left-root-right. + * If the tree is a BST, then the values will be sorted in ascendent order + * @param {BinaryTreeNode} node first node to start the traversal + * @yields {BinaryTreeNode} + */ + * inOrderTraversal(node = this.root) { + if (node && node.left) { yield* this.inOrderTraversal(node.left); } + yield node; + if (node && node.right) { yield* this.inOrderTraversal(node.right); } + } + // end::inOrderTraversal[] + + // tag::preOrderTraversal[] + /** + * Pre-order traversal on a tree: root-left-right. + * Similar results to DFS + * @param {BinaryTreeNode} node first node to start the traversal + * @yields {BinaryTreeNode} + */ + * preOrderTraversal(node = this.root) { + yield node; + if (node.left) { yield* this.preOrderTraversal(node.left); } + if (node.right) { yield* this.preOrderTraversal(node.right); } + } + // end::preOrderTraversal[] + + // tag::postOrderTraversal[] + /** + * Post-order traversal on a tree: left-right-root. + * @param {BinaryTreeNode} node first node to start the traversal + * @yields {BinaryTreeNode} + */ + * postOrderTraversal(node = this.root) { + if (node.left) { yield* this.postOrderTraversal(node.left); } + if (node.right) { yield* this.postOrderTraversal(node.right); } + yield node; + } + // end::postOrderTraversal[] + + /** + * Represent Binary Tree as an array. + * + * Leaf nodes will have two `undefined` descendants. + * + * The array representation of the binary tree is as follows: + * + * First element (index=0) is the root. + * The following two elements (index=1,2) are descendants of the root: left (a) and right (b). + * The next two elements (index=3,4) are the descendants of a + * The next two elements (index=5,6) are the descendants of b and so on. + * + * 0 1 2 3 4 5 6 n + * [root, a=root.left, b=root.right, a.left, a.right, b.left, b.right, ...] + * + * You can also find the parents as follows + * + * e.g. + * Parent 0: children 1,2 + * Parent 1: children 3,4 + * Parent 2: children 5,6 + * Parent 3: children 7,8 + * + * Given any index you can find the parent index with the following formula: + * + * parent = (index) => Math.floor((index-1)/2) + */ + toArray() { + const array = []; + const queue = new Queue(); + const visited = new Map(); + + if (this.root) { queue.add(this.root); } + + while (!queue.isEmpty()) { + const current = queue.remove(); + array.push(current && current.value); + + if (current) { visited.set(current); } + + if (current && !visited.has(current.left)) { queue.add(current.left); } + if (current && !visited.has(current.right)) { queue.add(current.right); } + } + + return array; + } +} + +// aliases +BinarySearchTree.prototype.insert = BinarySearchTree.prototype.add; +BinarySearchTree.prototype.set = BinarySearchTree.prototype.add; +BinarySearchTree.prototype.delete = BinarySearchTree.prototype.remove; +BinarySearchTree.prototype.getMin = BinarySearchTree.prototype.getLeftmost; +BinarySearchTree.prototype.minimum = BinarySearchTree.prototype.getMin; +BinarySearchTree.prototype.getMax = BinarySearchTree.prototype.getRightmost; +BinarySearchTree.prototype.maximum = BinarySearchTree.prototype.getMax; +BinarySearchTree.prototype.get = BinarySearchTree.prototype.find; + +module.exports = BinarySearchTree; diff --git a/docs/content/ds/data-structures/trees/binary-search-tree.spec.js b/docs/content/ds/data-structures/trees/binary-search-tree.spec.js new file mode 100644 index 0000000000..22508eb286 --- /dev/null +++ b/docs/content/ds/data-structures/trees/binary-search-tree.spec.js @@ -0,0 +1,379 @@ +const { BinarySearchTree } = require('../../index'); + +describe('Binary Search Tree', () => { + let bst; + const getValues = (treeGenerator) => Array.from(treeGenerator).map((node) => node.value); + + beforeEach(() => { + bst = new BinarySearchTree(); + }); + + describe('when is empty', () => { + describe('#add', () => { + it('should insert an item', () => { + expect(bst.size).toBe(0); + bst.add(1); + expect(bst.size).toBe(1); + }); + + it('should insert left and right child', () => { + const root = bst.add(5); + const n = bst.add(1); + expect(n.toValues()).toMatchObject({ + value: 1, parent: 5, left: null, right: null, + }); + expect(root.toValues()).toMatchObject({ + value: 5, parent: null, left: 1, right: null, + }); + bst.add(10); + expect(root.toValues()).toMatchObject({ + value: 5, parent: null, left: 1, right: 10, + }); + }); + + it('should insert low values to the left and higher to the right', () => { + const node5 = bst.add(5); + bst.add(1); + bst.add(10); + expect(bst.size).toBe(3); + // expect(bst.root).toBe(node5); + expect(node5.left.value).toBe(1); + expect(node5.right.value).toBe(10); + }); + + it('should insert nested values', () => { + const root = bst.add(10); + const n2 = bst.add(2); + const n30 = bst.add(30); + const n40 = bst.add(40); + + expect(root.left).toBe(n2); + expect(root.right).toBe(n30); + + expect(n30.left).toBe(null); + expect(n30.right).toBe(n40); + }); + + it('should keep parent reference', () => { + bst.add(1); + bst.add(2); + const n3 = bst.add(3); + + expect(n3.parent.value).toBe(2); + expect(bst.toArray()).toEqual([1, null, 2, null, 3, null, null]); + }); + + it('should deal with duplicates', () => { + const root = bst.add(1); + expect(root.meta.multiplicity).toBe(undefined); + expect(bst.add(1)).toBe(root); // should return existing + expect(bst.size).toBe(2); + expect(root.toValues()).toMatchObject({ + value: 1, parent: null, left: null, right: null, + }); + expect(root.meta.multiplicity).toBe(2); + }); + }); + + describe('#findNodeAndParent', () => { + it('should return falsy for empty tree', () => { + const { found, parent } = bst.findNodeAndParent(5); + expect(found).toBe(null); + expect(parent).toBe(null); + }); + + it('should return with a single element', () => { + bst.add(5); + const { found, parent } = bst.findNodeAndParent(5); + expect(found).toMatchObject({ value: 5 }); + expect(parent).toBe(null); + }); + + it('should return with an element and its parent', () => { + bst.add(5); + bst.add(1); + const { found, parent } = bst.findNodeAndParent(1); + expect(found).toMatchObject({ value: 1 }); + expect(parent).toMatchObject({ value: 5 }); + }); + + it('should find future parent of a node that doesnt exist yet', () => { + bst.add(5); + bst.add(1); + const { found, parent } = bst.findNodeAndParent(10); + expect(found).toBe(null); + expect(parent).toMatchObject({ value: 5 }); + }); + + it('should find future parent of a node that doesnt exist yet with -1', () => { + bst.add(5); + bst.add(1); + const { found, parent } = bst.findNodeAndParent(-1); + expect(found).toBe(null); + expect(parent).toMatchObject({ value: 1 }); + }); + }); + + describe('#remove', () => { + it('should remove root', () => { + bst.add(1); + expect(bst.remove(1)).toBe(true); + expect(bst.has(1)).toBe(false); + bst.add(1); + expect(bst.has(1)).toBe(true); + }); + }); + + describe('#inOrderTraversal', () => { + it('should get all keys', () => { + const fn = () => {}; + bst.set(1).data(1); + bst.set('dos').data(2); + bst.set({}).data(fn); + + // get keys + expect(getValues(bst.inOrderTraversal())).toEqual([1, {}, 'dos']); + // get data + expect(Array.from(bst.inOrderTraversal()).map((n) => n.data())).toEqual([ + 1, + fn, + 2, + ]); + }); + }); + }); + + describe('when has items', () => { + let root; + let n3; + let n4; + let n5; + let n30; + let n40; + let n15; + + beforeEach(() => { + // 10 + // / \ + // 5 30 + // / / \ + // 4 15 40 + // / \ + // 3 (4.5) + root = bst.add(10); + n5 = bst.add(5); + n30 = bst.add(30); + n40 = bst.add(40); + n15 = bst.add(15); + n4 = bst.add(4); + n3 = bst.add(3); + }); + + describe('#find', () => { + it('should find the value 2', () => { + expect(bst.find(5)).toBe(n5); + expect(bst.size).toBe(7); + }); + + it('should NOT find the value 20', () => { + expect(bst.find(20)).toBe(null); + }); + }); + + describe('#remove', () => { + it('should remove a left leaf node', () => { + expect(n4.left).toBe(n3); + bst.remove(3); + expect(n4.left).toBe(null); + expect(bst.size).toBe(6); + }); + + it('should remove a right leaf node', () => { + expect(n30.right).toBe(n40); + bst.remove(40); + expect(n30.right).toBe(null); + expect(bst.size).toBe(6); + }); + + it('should remove a child with one descent on the left', () => { + expect(n3.toValues()).toMatchObject({ + value: 3, left: null, right: null, parent: 4, + }); + bst.remove(4); + expect(n3.toValues()).toMatchObject({ + value: 3, left: null, right: null, parent: 5, + }); + }); + + it('should remove a child with one descent on the right', () => { + bst.remove(40); + expect(n15.toValues()).toMatchObject({ + value: 15, left: null, right: null, parent: 30, + }); + bst.remove(30); + expect(n15.toValues()).toMatchObject({ + value: 15, left: null, right: null, parent: 10, + }); + }); + + it('should remove a parent with two descents on the right', () => { + expect(root.left.value).toBe(5); + expect(root.right.value).toBe(30); + + bst.remove(30); + + expect(root.left.value).toBe(5); + expect(root.right.value).toBe(40); + expect(bst.size).toBe(6); + }); + + it('should remove a parent with two descents on the left', () => { + bst.add(4.5); + expect(n5.left.value).toBe(4); + expect(n5.left.right.value).toBe(4.5); + + bst.remove(4); + + expect(n5.left.value).toBe(4.5); + expect(bst.size).toBe(7); + }); + + it('should return false when it does not exist', () => { + expect(bst.remove(4.5)).toBe(false); + expect(bst.size).toBe(7); + }); + + it('should remove the root', () => { + expect(n30.parent).toBe(root); + expect(n5.parent).toBe(root); + bst.remove(10); + expect(n30.parent).toBe(null); + expect(n5.parent.value).toBe(15); + + expect(bst.toArray()).toEqual([ + 30, + 15, 40, + 5, null, null, null, + 4, null, + 3, null, + null, null]); + + expect(bst.size).toBe(6); + }); + + it('should remove duplicates', () => { + expect(bst.add(40)).toBe(n40); // add duplicate + expect(n40.meta.multiplicity).toBe(2); + + expect(bst.remove(40)).toBe(true); + expect(bst.size).toBe(7); + expect(n40.meta.multiplicity).toBe(1); + expect(bst.find(40)).toBe(n40); + + expect(bst.remove(40)).toBe(true); + expect(bst.size).toBe(6); + expect(bst.find(40)).toBeFalsy(); + + expect(bst.remove(40)).toBe(false); + expect(bst.size).toBe(6); + }); + }); + + describe('#bfs', () => { + it('should visit nodes on BFS order using iterator', () => { + const bfs = bst.bfs(); + expect(bfs.next().value).toBe(root); // 10 + expect(bfs.next().value).toBe(n5); + expect(bfs.next().value.value).toBe(30); + expect(bfs.next().value.value).toBe(4); + expect(bfs.next().value.value).toBe(15); + expect(bfs.next()).toMatchObject({ value: { value: 40 }, done: false }); + expect(bfs.next()).toMatchObject({ value: { value: 3 }, done: false }); + expect(bfs.next().done).toBe(true); + }); + + it('should generate an array from bfs', () => { + expect(getValues(bst.bfs())).toEqual([10, 5, 30, 4, 15, 40, 3]); + }); + }); + + describe('#dfs', () => { + it('should visit nodes on dfs order using iterator', () => { + const dfs = bst.dfs(); + expect(dfs.next().value).toBe(root); // 10 + expect(dfs.next().value).toBe(n5); + expect(dfs.next().value.value).toBe(4); + expect(dfs.next().value.value).toBe(3); + expect(dfs.next().value.value).toBe(30); + expect(dfs.next().value.value).toBe(15); + expect(dfs.next()).toMatchObject({ value: { value: 40 }, done: false }); + expect(dfs.next().done).toBe(true); + }); + + it('should generate an array from dfs', () => { + const nodes = Array.from(bst.dfs()); + const values = nodes.map((node) => node.value); + expect(values).toEqual([10, 5, 4, 3, 30, 15, 40]); + }); + }); + + describe('#inOrderTraversal', () => { + it('should generate an array', () => { + expect(getValues(bst.inOrderTraversal())).toEqual([3, 4, 5, 10, 15, 30, 40]); + }); + }); + + describe('#preOrderTraversal', () => { + it('should generate an array from preOrderTraversal', () => { + const nodes = Array.from(bst.preOrderTraversal()); + const values = nodes.map((node) => node.value); + expect(values).toEqual([10, 5, 4, 3, 30, 15, 40]); + }); + }); + + describe('#postOrderTraversal', () => { + it('should generate an array from postOrderTraversal', () => { + const nodes = Array.from(bst.postOrderTraversal()); + const values = nodes.map((node) => node.value); + expect(values).toEqual([3, 4, 5, 15, 40, 30, 10]); + }); + }); + + describe('#toArray', () => { + it('should serialize the tree as an array', () => { + expect(bst.toArray()).toEqual([10, 5, 30, 4, null, 15, 40, 3, + null, null, null, null, null, null, null]); + }); + }); + + describe('#getMax', () => { + it('should get the maximun value', () => { + expect(bst.getMax().value).toBe(40); + }); + + it('should get the maximun value of a subtree', () => { + expect(bst.getMax(n4).value).toBe(4); + }); + + it('should work with empty BST', () => { + bst = new BinarySearchTree(); + expect(bst.getMax()).toBe(null); + }); + }); + + describe('#getMin', () => { + it('should get the maximun value', () => { + expect(bst.getMin().value).toBe(3); + }); + + it('should get the maximun value of a subtree', () => { + expect(bst.getMin(n30).value).toBe(15); + }); + + it('should work with empty BST', () => { + bst = new BinarySearchTree(); + expect(bst.getMin()).toBe(null); + }); + }); + }); +}); diff --git a/docs/content/ds/data-structures/trees/binary-tree-node.js b/docs/content/ds/data-structures/trees/binary-tree-node.js new file mode 100644 index 0000000000..781b080db6 --- /dev/null +++ b/docs/content/ds/data-structures/trees/binary-tree-node.js @@ -0,0 +1,195 @@ +const LEFT = Symbol('left'); +const RIGHT = Symbol('right'); + +// tag::snippet[] +/** + * Binary Tree Node + * + */ +class BinaryTreeNode { + constructor(value) { + this.value = value; + this.left = null; + this.right = null; + this.meta = {}; + // end::snippet[] + this.parent = null; + this.parentSide = null; + } + + // tag::setAndUpdateParent[] + /** + * Set a left node descendants. + * Also, children get associated to parent. + */ + setLeftAndUpdateParent(node) { + this.left = node; + if (node) { + node.parent = this; + node.parentSide = LEFT; + } + } + + /** + * Set a right node descendants. + * Also, children get associated to parent. + */ + setRightAndUpdateParent(node) { + this.right = node; + if (node) { + node.parent = this; + node.parentSide = RIGHT; + } + } + // end::setAndUpdateParent[] + + /** + * Tell if is parent's left or right child + * + * @returns {string} side (left or right) this node is of its parent + */ + get parentChildSide() { + if (this.parent) { + return this.isParentLeftChild ? 'left' : 'right'; + } + + return 'root'; + } + + /** + * Return true if this node is its parent left child + */ + get isParentLeftChild() { + return this.parentSide === LEFT; + } + + /** + * Return true if this node is its parent right child + */ + get isParentRightChild() { + return this.parentSide === RIGHT; + } + + /** + * Node is leaf is it has no descendants + */ + get isLeaf() { + return !this.left && !this.right; + } + + /** + * Get sibling of current node + */ + get sibling() { + const { parent } = this; + if (!parent) return null; + return parent.right === this ? parent.left : parent.right; + } + + /** + * Get parent sibling = uncle (duh) + */ + get uncle() { + const { parent } = this; + if (!parent) return null; + return parent.sibling; + } + + get grandparent() { + const { parent } = this; + return parent && parent.parent; + } + + /** + * Get color + */ + get color() { + return this.meta.color; + } + + /** + * Set Color + */ + set color(value) { + this.meta.color = value; + } + + // tag::avl[] + /** + * @returns {Number} left subtree height or 0 if no left child + */ + get leftSubtreeHeight() { + return this.left ? this.left.height + 1 : 0; + } + + /** + * @returns {Number} right subtree height or 0 if no right child + */ + get rightSubtreeHeight() { + return this.right ? this.right.height + 1 : 0; + } + + /** + * Get the max height of the subtrees. + * + * It recursively goes into each children calculating the height + * + * Height: distance from the deepest leaf to this node + */ + get height() { + return Math.max(this.leftSubtreeHeight, this.rightSubtreeHeight); + } + + /** + * Returns the difference the heights on the left and right subtrees + */ + get balanceFactor() { + return this.leftSubtreeHeight - this.rightSubtreeHeight; + } + // end::avl[] + + /** + * Serialize node's values + */ + toValues() { + return { + value: this.value, + left: this.left && this.left.value, + right: this.right && this.right.value, + parent: this.parent && this.parent.value, + parentSide: this.parentSide, + }; + } + + /** + * Get and Set data value + * @param {any} value (optional) if not provided is a getter, otherwise a setter. + */ + data(value) { + if (value === undefined) { + return this.meta.data; + } + this.meta.data = value; + return this; + } + + /** + * Convert Binary tree from an iterable (e.g. array) + * @param {array|string} iterable - The iterable + */ + static from(iterable = []) { + const toBinaryTree = (array, index = 0) => { + if (index >= array.length) return null; + const node = new BinaryTreeNode(array[index]); + node.setLeftAndUpdateParent(toBinaryTree(array, index * 2 + 1)); + node.setRightAndUpdateParent(toBinaryTree(array, index * 2 + 2)); + return node; + }; + return toBinaryTree(Array.from(iterable)); + } +} + +BinaryTreeNode.RIGHT = RIGHT; +BinaryTreeNode.LEFT = LEFT; + +module.exports = BinaryTreeNode; diff --git a/docs/content/ds/data-structures/trees/binary-tree-node.spec.js b/docs/content/ds/data-structures/trees/binary-tree-node.spec.js new file mode 100644 index 0000000000..675e6a0198 --- /dev/null +++ b/docs/content/ds/data-structures/trees/binary-tree-node.spec.js @@ -0,0 +1,167 @@ +const { BinaryTreeNode } = require('../../index'); + +const { LEFT, RIGHT } = BinaryTreeNode; + +describe('Binary Tree Node', () => { + let treeNode; + + describe('with instance', () => { + beforeEach(() => { + treeNode = new BinaryTreeNode('hola'); + }); + + it('should start with null parent', () => { + expect(treeNode.parent).toBe(null); + }); + + it('should start with empty metadata', () => { + expect(treeNode.meta).toEqual({}); + }); + + it('should hold a value', () => { + expect(treeNode.value).toBe('hola'); + }); + + it('should have a height 0', () => { + expect(treeNode.height).toBe(0); + }); + + it('should set/get left node', () => { + expect(treeNode.left).toBe(null); + const newNode = new BinaryTreeNode(1); + treeNode.setLeftAndUpdateParent(newNode); + expect(treeNode.left.value).toBe(1); + + expect(newNode.parent).toBe(treeNode); + expect(treeNode.height).toBe(1); + expect(treeNode.balanceFactor).toBe(1); + }); + + it('should set/get right node', () => { + expect(treeNode.right).toBe(null); + const newNode = new BinaryTreeNode(1); + treeNode.setRightAndUpdateParent(newNode); + + expect(treeNode.right.value).toBe(1); + expect(newNode.parent).toBe(treeNode); + expect(treeNode.height).toBe(1); + expect(treeNode.balanceFactor).toBe(-1); + }); + + describe('Family operations', () => { + let g; + let p; + let u; + let c; + let s; + + beforeEach(() => { + g = new BinaryTreeNode('grandparent'); + p = new BinaryTreeNode('parent'); + u = new BinaryTreeNode('uncle'); + c = new BinaryTreeNode('child'); + s = new BinaryTreeNode('sibling'); + + g.setRightAndUpdateParent(p); + g.setLeftAndUpdateParent(u); + p.setRightAndUpdateParent(c); + p.setLeftAndUpdateParent(s); + }); + + it('should set heights', () => { + expect(g.height).toBe(2); + expect(g.balanceFactor).toBe(-1); + + expect(p.height).toBe(1); + expect(p.balanceFactor).toBe(0); + + expect(u.height).toBe(0); + expect(u.balanceFactor).toBe(0); + }); + + it('should get the sibling', () => { + expect(c.sibling).toBe(s); + expect(p.sibling).toBe(u); + }); + + it('should set leaf correctly', () => { + expect(c.isLeaf).toBe(true); + expect(u.isLeaf).toBe(true); + expect(p.isLeaf).toBe(false); + expect(g.isLeaf).toBe(false); + }); + + it('should get null if no sibling', () => { + expect(g.sibling).toBe(null); + }); + + it('should get the uncle', () => { + expect(c.uncle).toBe(u); + }); + + it('should get null if no uncle', () => { + expect(g.uncle).toBe(null); + expect(p.uncle).toBe(null); + }); + + it('true if is parent left child for sibling', () => { + expect(s.isParentLeftChild).toBe(true); + expect(s.isParentRightChild).toBe(false); + }); + + it('true if is parent left child for child', () => { + expect(c.isParentLeftChild).toBe(false); + expect(c.isParentRightChild).toBe(true); + }); + }); + }); + + describe('with static methods', () => { + it('should work with null', () => { + const tree = BinaryTreeNode.from(); + expect(tree).toEqual(null); + }); + + it('should build from array', () => { + /* + 0 + / \ + 1 2 + / \ \ + 3 5 4 + */ + const tree = BinaryTreeNode.from([0, 1, 2, 3, 5, null, 4]); + expect(tree.toValues()).toEqual({ + value: 0, + left: 1, + right: 2, + parent: null, + parentSide: null, + }); + + expect(tree.left.toValues()).toEqual({ + value: 1, + left: 3, + right: 5, + parent: 0, + parentSide: LEFT, + }); + + expect(tree.right.toValues()).toEqual({ + value: 2, + left: null, + right: 4, + parent: 0, + parentSide: RIGHT, + }); + + expect(tree.right.right.toValues()).toEqual({ + value: 4, + left: null, + right: null, + parent: 2, + parentSide: RIGHT, + }); + }); + }); +}); diff --git a/docs/content/ds/data-structures/trees/red-black-tree.js b/docs/content/ds/data-structures/trees/red-black-tree.js new file mode 100644 index 0000000000..28c7e4b43a --- /dev/null +++ b/docs/content/ds/data-structures/trees/red-black-tree.js @@ -0,0 +1,141 @@ +const BinarySearchTree = require('./binary-search-tree'); + +const RED = Symbol('red'); +const BLACK = Symbol('black'); + +/** + * Red-Black Tree + * It's a self-balanced binary search tree optimized for fast insertion. + * + * Properties: + * + * 1. Every node is either BLACK or RED. + * 2. The root is BLACK. + * 3. The null leaves are considered BLACK. + * 4. Every RED node has only BLACK children. + * A BLACK node can have BLACK children, however a RED node cannot have RED children. + * 5. Every path from the leaves to the root has the same number of BLACK nodes. + * + */ +class RedBlackTree extends BinarySearchTree { + /** + * Insert node in the tree. + * + * The root is always BLACK. + * New nodes are always RED. + * + * @param {any} value new nodes' value + */ + add(value) { + // add node using the regular BST add + const node = super.add(value); + + if (node === this.root) { + node.meta.color = BLACK; + } else { + node.meta.color = RED; + this.balance(node); + } + + return node; + } + + /** + * Balance tree by doing rotations + * + * Fix RED/BLACK violations + * - RED violation: a RED node has a RED child or root is RED. + * - BLACK violation: one path has more BLACK nodes than other. + * + * + * @param {TreeNode} node + */ + balance(node) { + // check uncle + if (node.uncle && node.uncle.color === RED) { + // if uncle is RED, change the color of uncle, parent and grandparent to BLACK + node.parent.color = BLACK; + node.uncle.color = BLACK; + node.grandparent.color = BLACK; + } else if (node.uncle && node.uncle.color === BLACK) { + // if uncle is BLACK + + // case: Right Right Case + + } else if (node.parent && node.color === RED && node.parent.color === RED) { + // Solve RED violation doing rotations and re-color + if (node.isParentLeftChild) { + this.rightRotation(node.parent); + } else { + this.leftRotation(node.parent); + } + } + } + + /** + * Left rotation in-place + * + * E.g. left-rotate node 2 + * + * 1 [2] + * \ / \ + * [2] => 1 3 + * \ + * 3 + * + * @param {TreeNode} node + */ + leftRotation(node) { + const oldParent = node.parent; + const grandParent = oldParent.parent; + + if (grandParent) { + // do nothing + } else { + this.root = node; + node.parent = null; + node.setLeftAndUpdateParent(oldParent); + oldParent.setRightAndUpdateParent(null); + // re-color + node.color = BLACK; + node.right.color = RED; + node.left.color = RED; + } + } + + /** + * Right rotation in-place + * + * E.g. Right-rotate node 2 + * + * 3 [2] + * / / \ + * [2] => 1 3 + * / + * 1 + * + * @param {TreeNode} node + */ + rightRotation(node) { + const oldParent = node.parent; + const grandParent = oldParent.parent; + + if (grandParent) { + // do something + } else { + this.root = node; + node.parent = null; + node.setRightAndUpdateParent(oldParent); + oldParent.setLeftAndUpdateParent(null); + // re-color + node.color = BLACK; + node.right.color = RED; + node.left.color = RED; + } + } +} + +RedBlackTree.RED = RED; +RedBlackTree.BLACK = BLACK; + +module.exports = RedBlackTree; diff --git a/docs/content/ds/data-structures/trees/red-black-tree.spec.js b/docs/content/ds/data-structures/trees/red-black-tree.spec.js new file mode 100644 index 0000000000..52d7f843af --- /dev/null +++ b/docs/content/ds/data-structures/trees/red-black-tree.spec.js @@ -0,0 +1,109 @@ +const { RedBlackTree } = require('../../index'); + +const { RED, BLACK } = RedBlackTree; + +describe('RedBlackTree', () => { + let tree; + + beforeEach(() => { + tree = new RedBlackTree(); + }); + + describe('#add', () => { + it('should add and self-balance the tree', () => { + expect(tree).not.toBe(null); + }); + + it('should make root black', () => { + const root = tree.add(1); + expect(root.meta.color).toBe(BLACK); + expect(tree.size).toBe(1); + }); + + it('should add a new node as red', () => { + tree.add(1); + const n2 = tree.add(2); + expect(n2.meta.color).toBe(RED); + }); + + it('should balance tree by rotating left', () => { + tree.add(1); + tree.add(2); + tree.add(3); + expect(tree.size).toBe(3); + + expect(tree.toArray()).toEqual([ + 2, + 1, 3, + null, null, null, null, + ]); + + // verify colors + expect(tree.root.color).toBe(BLACK); + expect(tree.root.right.color).toBe(RED); + expect(tree.root.left.color).toBe(RED); + }); + + it('should balance tree by rotating right', () => { + tree.add(3); + tree.add(2); + tree.add(1); + + expect(tree.toArray()).toEqual([ + 2, + 1, 3, + null, null, null, null, + ]); + + // verify colors + expect(tree.root.color).toBe(BLACK); + expect(tree.root.right.color).toBe(RED); + expect(tree.root.left.color).toBe(RED); + }); + + it('should change colors', () => { + const n1 = tree.add(1); + const n2 = tree.add(2); + const n3 = tree.add(3); + const n4 = tree.add(4); + + expect(tree.toArray()).toEqual([ + 2, + 1, 3, + null, null, null, 4, + null, null, + ]); + + expect(tree.root.color).toBe(BLACK); + expect(tree.root.right.color).toBe(BLACK); + expect(tree.root.left.color).toBe(BLACK); + expect(tree.root.right.right.color).toBe(RED); + + expect(n1.color).toBe(BLACK); + expect(n2.color).toBe(BLACK); + expect(n3.color).toBe(BLACK); + expect(n4.color).toBe(RED); + }); + + xtest('letf roation with grandparent', () => { + const n1 = tree.add(1); + const n2 = tree.add(2); + const n3 = tree.add(3); + const n4 = tree.add(4); + const n5 = tree.add(5); + + expect(tree.toArray()).toEqual([ + 2, + 1, 4, + null, null, 3, 5, + null, null, null, null, + ]); + + expect(n1.color).toBe(BLACK); + expect(n2.color).toBe(BLACK); + expect(n4.color).toBe(BLACK); + expect(n3.color).toBe(RED); + expect(n5.color).toBe(RED); + }); + }); +}); diff --git a/docs/content/ds/data-structures/trees/tree-node.js b/docs/content/ds/data-structures/trees/tree-node.js new file mode 100644 index 0000000000..c78624a1c4 --- /dev/null +++ b/docs/content/ds/data-structures/trees/tree-node.js @@ -0,0 +1,13 @@ +// tag::snippet[] +/** + * TreeNode - each node can have zero or more children + */ +class TreeNode { + constructor(value) { + this.value = value; + this.descendants = []; + } +} +// end::snippet[] + +module.exports = TreeNode; diff --git a/docs/content/ds/data-structures/trees/tree-rotations.js b/docs/content/ds/data-structures/trees/tree-rotations.js new file mode 100644 index 0000000000..235c369113 --- /dev/null +++ b/docs/content/ds/data-structures/trees/tree-rotations.js @@ -0,0 +1,173 @@ +// tag::swapParentChild[] +/** + * Swap parent's child + * + * @example Child on the left side (it also work for the right side) + * + * p = parent + * o = old child + * n = new child + * + * p p + * \ => \ + * o n + * + * @param {TreeNode} oldChild current child's parent + * @param {TreeNode} newChild new child's parent + * @param {TreeNode} parent parent + */ +function swapParentChild(oldChild, newChild, parent) { + if (parent) { + // this set parent child + const side = oldChild.isParentRightChild ? 'Right' : 'Left'; + parent[`set${side}AndUpdateParent`](newChild); + } else { + // no parent? so set it to null + newChild.parent = null; + } +} +// end::swapParentChild[] + + +// tag::leftRotation[] +/** + * Single Left Rotation (LL Rotation) + * + * @example: #1 tree with values 1-2-3-4 + * + * 1 1 + * \ \ + * 2* 3 + * \ --left-rotation(2)-> / \ + * 3 2* 4 + * \ + * 4 + * + * @example: #2 left rotation + * + * 1 1 + * \ \ + * 4* 16 + * / \ / \ + * 2 16 -- left-rotation(4) -> 4 32 + * / \ / \ \ + * 8 32 2 8 64 + * \ + * 64 + * @param {TreeNode} node current node to rotate (e.g. 4) + * @returns {TreeNode} new parent after the rotation (e.g. 16) + */ +function leftRotation(node) { + const newParent = node.right; // E.g., node 16 + const grandparent = node.parent; // E.g., node 1 + const previousLeft = newParent.left; // E.g., node 8 + + // swap parent of node 4 from node 1 to node 16 + swapParentChild(node, newParent, grandparent); + + // Update node 16 left child to be 4, and + // updates node 4 parent to be node 16 (instead of 1). + newParent.setLeftAndUpdateParent(node); + // set node4 right child to be previousLeft (node 8) + node.setRightAndUpdateParent(previousLeft); + + return newParent; +} +// end::leftRotation[] + +// tag::rightRotation[] +/** + * Single Right Rotation (RR Rotation) +* @example: #1 rotate node 3 to the right + * + * 4 4 + * / / + * 3* 2 + * / / \ + * 2 ---| right-rotation(3) |--> 1 3* + * / + * 1 + * + * @example: #2 rotate 16 to the right and preserve nodes + * 64 64 + * / / + * 16* 4 + * / \ / \ + * 4 32 -- right-rotation(16) --> 2 16 + * / \ / / \ + * 2 8 1 8 32 + * / + * 1 + * + * @param {TreeNode} node + * this is the node we want to rotate to the right. (E.g., node 16) + * @returns {TreeNode} new parent after the rotation (E.g., node 4) + */ +function rightRotation(node) { + const newParent = node.left; // E.g., node 4 + const grandparent = node.parent; // E.g., node 64 + const previousRight = newParent.right; // E.g., node 8 + + // swap node 64's left children (node 16) with node 4 (newParent). + swapParentChild(node, newParent, grandparent); + + // update node 4's right child to be node 16, + // also make node 4 the new parent of node 16. + newParent.setRightAndUpdateParent(node); + // Update 16's left child to be the `previousRight` node. + node.setLeftAndUpdateParent(previousRight); + + return newParent; +} +// end::rightRotation[] + +// tag::leftRightRotation[] +/** + * Left Right Rotation (LR Rotation) + * + * @example LR rotation on node 3 + * 4 4 + * / / 4 + * 3 3* / + * / / 2 + * 1* --left-rotation(1)-> 2 --right-rotation(3)-> / \ + * \ / 1 3* + * 2 1 + * + * @param {TreeNode} node + * this is the node we want to rotate to the right. E.g., node 3 + * @returns {TreeNode} new parent after the rotation + */ +function leftRightRotation(node) { + leftRotation(node.left); + return rightRotation(node); +} +// end::leftRightRotation[] + +// tag::rightLeftRotation[] +/** + * Right Left Rotation (RL Rotation) + * + * @example RL rotation on 1 + * + * 1* 1* + * \ \ 2 + * 3 -right-rotation(3)-> 2 -left-rotation(1)-> / \ + * / \ 1* 3 + * 2 3 + * + * @param {TreeNode} node + * @returns {TreeNode} new parent after the rotation + */ +function rightLeftRotation(node) { + rightRotation(node.right); + return leftRotation(node); +} +// end::rightLeftRotation[] + +module.exports = { + leftRotation, + rightRotation, + leftRightRotation, + rightLeftRotation, +}; diff --git a/docs/content/ds/data-structures/trees/tree-rotations.spec.js b/docs/content/ds/data-structures/trees/tree-rotations.spec.js new file mode 100644 index 0000000000..3a82653229 --- /dev/null +++ b/docs/content/ds/data-structures/trees/tree-rotations.spec.js @@ -0,0 +1,284 @@ +const BinaryTreeNode = require('./binary-tree-node'); + +const { + leftRotation, + rightRotation, + leftRightRotation, + rightLeftRotation, +} = require('./tree-rotations'); + +describe('Tree rotations', () => { + let n1; + let n2; + let n3; + let n4; + + beforeEach(() => { + n1 = new BinaryTreeNode(1); + n2 = new BinaryTreeNode(2); + n3 = new BinaryTreeNode(3); + n4 = new BinaryTreeNode(4); + }); + + describe('#leftRotation (LL Rotation)', () => { + it('basic LL rotation', () => { + /* + * 1* 2* + * \ / \ + * 2 ---| left-rotation(1) |--> 1 3 + * \ + * 3 + */ + n1.setRightAndUpdateParent(n2); + n2.setRightAndUpdateParent(n3); + + const newParent = leftRotation(n1); + expect(newParent.value).toBe(2); + expect(n1.toValues()).toMatchObject({ + value: 1, left: null, right: null, parent: 2, + }); + expect(n2.toValues()).toMatchObject({ + value: 2, left: 1, right: 3, parent: null, + }); + expect(n3.toValues()).toMatchObject({ + value: 3, left: null, right: null, parent: 2, + }); + }); + + it('should rotate left with root (LL Rotation)', () => { + // 1 + // \ + // 2* + // \ + // 3 + // \ + // 4 + n1.setRightAndUpdateParent(n2); + n2.setRightAndUpdateParent(n3); + n3.setRightAndUpdateParent(n4); + + const newParent = leftRotation(n2); + + expect(newParent).toBe(n3); + expect(n1.toValues()).toMatchObject({ + value: 1, left: null, right: 3, parent: null, + }); + expect(n2.toValues()).toMatchObject({ + value: 2, left: null, right: null, parent: 3, + }); + expect(n3.toValues()).toMatchObject({ + value: 3, left: 2, right: 4, parent: 1, + }); + }); + + it('should rotate last two', () => { + // 4 + // / + // 3 + // / + // 1* + // \ + // 2 + n4.setLeftAndUpdateParent(n3); + n3.setLeftAndUpdateParent(n1); + n1.setRightAndUpdateParent(n2); + + const newParent = leftRotation(n1); + expect(newParent).toBe(n2); + + expect(n1.toValues()).toMatchObject({ + value: 1, left: null, right: null, parent: 2, + }); + expect(n2.toValues()).toMatchObject({ + value: 2, left: 1, right: null, parent: 3, + }); + expect(n3.toValues()).toMatchObject({ + value: 3, left: 2, right: null, parent: 4, + }); + }); + + it('should not lose nodes on LL', () => { + // 1 1 + // \ \ + // 4* 16 + // / \ / \ + // 2 16 -- left-rotation(4) -> 4 32 + // / \ / \ \ + // 8 32 2 8 64 + // \ + // 64 + const n8 = new BinaryTreeNode(8); + const n16 = new BinaryTreeNode(16); + const n32 = new BinaryTreeNode(32); + const n64 = new BinaryTreeNode(64); + + n1.setRightAndUpdateParent(n4); + n4.setLeftAndUpdateParent(n2); + n4.setRightAndUpdateParent(n16); + n16.setLeftAndUpdateParent(n8); + n16.setRightAndUpdateParent(n32); + n32.setLeftAndUpdateParent(n64); + + const newParent = leftRotation(n4); + + expect(newParent).toBe(n16); + expect(n8.toValues()).toMatchObject({ + value: 8, left: null, right: null, parent: 4, + }); + expect(n4.toValues()).toMatchObject({ + value: 4, left: 2, right: 8, parent: 16, + }); + }); + }); + + describe('#rightRotation (RR Rotation)', () => { + it('should rotate single trees', () => { + // 4 + // / + // 3* + // / + // 2 + // / + // 1 + n4.setLeftAndUpdateParent(n3); + n3.setLeftAndUpdateParent(n2); + n2.setLeftAndUpdateParent(n1); + + const newParent = rightRotation(n3); + + expect(newParent).toBe(n2); + expect(n1.toValues()).toMatchObject({ + value: 1, left: null, right: null, parent: 2, + }); + expect(n2.toValues()).toMatchObject({ + value: 2, left: 1, right: 3, parent: 4, + }); + expect(n3.toValues()).toMatchObject({ + value: 3, left: null, right: null, parent: 2, + }); + expect(n4.toValues()).toMatchObject({ + value: 4, left: 2, right: null, parent: null, + }); + }); + + it('should right rotate at the top', () => { + // 3* + // / + // 2 + // / + // 1 + n3.setLeftAndUpdateParent(n2); + n2.setLeftAndUpdateParent(n1); + + const newParent = rightRotation(n3); + + expect(newParent).toBe(n2); + expect(n2.toValues()).toMatchObject({ + value: 2, left: 1, right: 3, parent: null, + }); + }); + + it('should RR last two', () => { + // 1 + // \ + // 3* + // / + // 2 + n1.setRightAndUpdateParent(n3); + n3.setLeftAndUpdateParent(n2); + + const newParent = rightRotation(n3); + + expect(newParent).toBe(n2); + expect(n1.toValues()).toMatchObject({ + value: 1, left: null, right: 2, parent: null, + }); + expect(n2.toValues()).toMatchObject({ + value: 2, left: null, right: 3, parent: 1, + }); + expect(n3.toValues()).toMatchObject({ + value: 3, left: null, right: null, parent: 2, + }); + }); + + it('should not lose nodes on RR', () => { + // 16* 4 + // / \ / \ + // 4 32 -- right-rotation(16) --> 2 16 + // / \ / / \ + // 2 8 1 8 32 + // / + // 1 + const n8 = new BinaryTreeNode(8); + const n16 = new BinaryTreeNode(16); + const n32 = new BinaryTreeNode(32); + n16.setLeftAndUpdateParent(n4); + n16.setRightAndUpdateParent(n32); + n4.setLeftAndUpdateParent(n2); + n4.setRightAndUpdateParent(n8); + n2.setLeftAndUpdateParent(n1); + + const newParent = rightRotation(n16); + + expect(newParent).toBe(n4); + expect(n8.toValues()).toMatchObject({ + value: 8, left: null, right: null, parent: 16, + }); + expect(n16.toValues()).toMatchObject({ + value: 16, left: 8, right: 32, parent: 4, + }); + }); + }); + + describe('#leftRightRotation (LR Rotation)', () => { + it('should do LL rotation and RR rotation', () => { + // 4 + // / + // 3* + // / + // 1 + // \ + // 2 + n4.setLeftAndUpdateParent(n3); + n3.setLeftAndUpdateParent(n1); + n1.setRightAndUpdateParent(n2); + + const newParent = leftRightRotation(n3); + + expect(newParent).toBe(n2); + expect(n1.toValues()).toMatchObject({ + value: 1, left: null, right: null, parent: 2, + }); + expect(n2.toValues()).toMatchObject({ + value: 2, left: 1, right: 3, parent: 4, + }); + expect(n3.toValues()).toMatchObject({ + value: 3, left: null, right: null, parent: 2, + }); + }); + }); + + describe('#rightLeftRotation (RL Rotation)', () => { + it('should do a RR rotation and then a LL rotation', () => { + // 1* + // \ + // 3 + // / + // 2 + n1.setRightAndUpdateParent(n3); + n3.setLeftAndUpdateParent(n2); + + const newParent = rightLeftRotation(n1); + expect(newParent).toBe(n2); + expect(n1.toValues()).toMatchObject({ + value: 1, left: null, right: null, parent: 2, + }); + expect(n2.toValues()).toMatchObject({ + value: 2, left: 1, right: 3, parent: null, + }); + expect(n3.toValues()).toMatchObject({ + value: 3, left: null, right: null, parent: 2, + }); + }); + }); +}); diff --git a/docs/content/ds/data-structures/trees/trie-1.js b/docs/content/ds/data-structures/trees/trie-1.js new file mode 100644 index 0000000000..fb6ed1bf95 --- /dev/null +++ b/docs/content/ds/data-structures/trees/trie-1.js @@ -0,0 +1,123 @@ +class Trie { + constructor(val) { + this.val = val; + this.children = {}; + this.isWord = false; + } + + /** + * Insert word into trie and mark last element as such. + * @param {string} word + * @return {undefined} + */ + insert(word) { + let curr = this; + + for (const char of word) { + curr.children[char] = curr.children[char] || new Trie(char); + curr = curr.children[char]; + } + + curr.isWord = true; + } + + /** + * Search for complete word (by default) or partial if flag is set. + * @param {string} word - Word to search. + * @param {boolean} options.partial - Whether or not match partial matches. + * @return {boolean} + */ + search(word, { partial } = {}) { + let curr = this; + + for (const char of word) { + if (!curr.children[char]) { return false; } + curr = curr.children[char]; + } + + return partial ? true : curr.isWord; + } + + /** + * Return true if any word on the trie starts with the given prefix + * @param {string} prefix - Partial word to search. + * @return {boolean} + */ + startsWith(prefix) { + return this.search(prefix, { partial: true }); + } + + /** + * Returns all the words from the current `node`. + * Uses backtracking. + * + * @param {string} prefix - The prefix to append to each word. + * @param {string} node - Current node to start backtracking. + * @param {string[]} words - Accumulated words. + * @param {string} string - Current string. + */ + getAllWords(prefix = '', node = this, words = [], string = '') { + if (node.isWord) { + words.push(`${prefix}${string}`); + } + + for (const char of Object.keys(node.children)) { + this.getAllWords(prefix, node.children[char], words, `${string}${char}`); + } + + return words; + } + + /** + * Return true if found the word to be removed, otherwise false. + * Iterative approach + * @param {string} word - The word to remove + * @returns {boolean} + */ + remove(word) { + const stack = []; + let curr = this; + + for (const char of word) { + if (!curr.children[char]) { return false; } + stack.push(curr); + curr = curr.children[char]; + } + + if (!curr.isWord) { return false; } + let node = stack.pop(); + + do { + node.children = {}; + node = stack.pop(); + } while (node && !node.isWord); + + return true; + } + + /** + * Return true if found the word to be removed, otherwise false. + * recursive approach + * @param {string} word - The word to remove + * @returns {boolean} + */ + remove2(word, i = 0, parent = this) { + if (i === word.length - 1) { + return true; + } + const child = parent.children[word.charAt(i)]; + if (!child) return false; + + const found = this.remove(word, i + 1, child); + + if (found) { + delete parent.children[word.charAt(i)]; + } + return true; + } +} + +// Aliases +Trie.prototype.add = Trie.prototype.insert; + +module.exports = Trie; diff --git a/docs/content/ds/data-structures/trees/trie-2.js b/docs/content/ds/data-structures/trees/trie-2.js new file mode 100644 index 0000000000..1a03c24115 --- /dev/null +++ b/docs/content/ds/data-structures/trees/trie-2.js @@ -0,0 +1,133 @@ +class Trie { + constructor(val) { + this.val = val; + this.children = {}; + this.isWord = false; + } + + /** + * Insert word into trie and mark last element as such. + * @param {string} word + * @return {undefined} + */ + insert(word) { + let curr = this; + + for (const char of word) { + curr.children[char] = curr.children[char] || new Trie(char); + curr = curr.children[char]; + } + + curr.isWord = true; + } + + /** + * Return true if found the word to be removed, otherwise false. + * @param {string} word - The word to remove + * @returns {boolean} + */ + remove(word) { + return this.removeHelper(word); + } + + /** + * Remove word from trie, return true if found, otherwise false. + * @param {string} word - The word to remove. + * @param {Trie} parent - The parent node. + * @param {number} index - The index. + * @param {number} meta.stop - Keeps track of the last letter that won't be removed. + * @returns {boolean} + */ + removeHelper(word, parent = this, index = 0, meta = { stop: 0 }) { + if (index === word.length) { + parent.isWord = false; + if (Object.keys(parent.children)) { meta.stop = index; } + return true; + } + const child = parent.children[word.charAt(index)]; + if (!child) { return false; } + if (parent.isWord) { meta.stop = index; } + const found = this.removeHelper(word, child, index + 1, meta); + // deletes all the nodes beyond `meta.stop`. + if (found && index >= meta.stop) { + delete parent.children[word.charAt(index)]; + } + return found; + } + + /** + * Retun last node that matches word or prefix or false if not found. + * @param {string} word - Word to search. + * @param {boolean} options.partial - Whether or not match partial matches. + * @return {Trie|false} + */ + searchNode(word) { + let curr = this; + + for (const char of word) { + if (!curr.children[char]) { return false; } + curr = curr.children[char]; + } + + return curr; + } + + /** + * Search for complete word (by default) or partial if flag is set. + * @param {string} word - Word to search. + * @param {boolean} options.partial - Whether or not match partial matches. + * @return {boolean} + */ + search(word, { partial } = {}) { + const curr = this.searchNode(word); + if (!curr) { return false; } + return partial ? true : curr.isWord; + } + + /** + * Return true if any word on the trie starts with the given prefix + * @param {string} prefix - Partial word to search. + * @return {boolean} + */ + startsWith(prefix) { + return this.search(prefix, { partial: true }); + } + + /** + * Returns all the words from the current `node`. + * Uses backtracking. + * + * @param {string} prefix - The prefix to append to each word. + * @param {string} node - Current node to start backtracking. + */ + getAllWords(prefix = '', node = this) { + let words = []; + + if (!node) { return words; } + if (node.isWord) { + words.push(prefix); + } + + for (const char of Object.keys(node.children)) { + const newWords = this.getAllWords(`${prefix}${char}`, node.children[char]); + words = words.concat(newWords); + } + + return words; + } + + /** + * Return a list of words matching the prefix + * @param {*} prefix - The prefix to match. + * @returns {string[]} + */ + autocomplete(prefix = '') { + const curr = this.searchNode(prefix); + return this.getAllWords(prefix, curr); + } +} + +// Aliases +Trie.prototype.add = Trie.prototype.insert; + +module.exports = Trie; diff --git a/docs/content/ds/data-structures/trees/trie.js b/docs/content/ds/data-structures/trees/trie.js new file mode 100644 index 0000000000..77fa5b57ab --- /dev/null +++ b/docs/content/ds/data-structures/trees/trie.js @@ -0,0 +1,132 @@ +class Trie { + constructor(val) { + this.val = val; + this.children = {}; + this.isWord = false; + } + + /** + * Insert word into trie and mark last element as such. + * @param {string} word + * @return {undefined} + */ + insert(word) { + let curr = this; + + for (const char of word) { + curr.children[char] = curr.children[char] || new Trie(char); + curr = curr.children[char]; + } + + curr.isWord = true; + } + + /** + * Return true if found the word to be removed, otherwise false. + * @param {string} word - The word to remove + * @returns {boolean} + */ + remove(word) { + let curr = this; + // let lastWordToKeep = 0; + const stack = [curr]; + + // find word and stack path + for (const char of word) { + if (!curr.children[char]) { return false; } + // lastWordToKeep += 1; + curr = curr.children[char]; + stack.push(curr); + } + + let child = stack.pop(); + child.isWord = false; + + // remove non words without children + while (stack.length) { + const parent = stack.pop(); + if (!child.isWord && !Object.keys(child.children).length) { + delete parent.children[child.val]; + } + child = parent; + } + + return true; + } + + /** + * Retun last node that matches word or prefix or false if not found. + * @param {string} word - Word to search. + * @param {boolean} options.partial - Whether or not match partial matches. + * @return {Trie|false} + */ + searchNode(word) { + let curr = this; + + for (const char of word) { + if (!curr.children[char]) { return false; } + curr = curr.children[char]; + } + + return curr; + } + + /** + * Search for complete word (by default) or partial if flag is set. + * @param {string} word - Word to search. + * @param {boolean} options.partial - Whether or not match partial matches. + * @return {boolean} + */ + search(word, { partial } = {}) { + const curr = this.searchNode(word); + if (!curr) { return false; } + return partial ? true : curr.isWord; + } + + /** + * Return true if any word on the trie starts with the given prefix + * @param {string} prefix - Partial word to search. + * @return {boolean} + */ + startsWith(prefix) { + return this.search(prefix, { partial: true }); + } + + /** + * Returns all the words from the current `node`. + * Uses backtracking. + * + * @param {string} prefix - The prefix to append to each word. + * @param {string} node - Current node to start backtracking. + */ + getAllWords(prefix = '', node = this) { + let words = []; + + if (!node) { return words; } + if (node.isWord) { + words.push(prefix); + } + + for (const char of Object.keys(node.children)) { + const newWords = this.getAllWords(`${prefix}${char}`, node.children[char]); + words = words.concat(newWords); + } + + return words; + } + + /** + * Return a list of words matching the prefix + * @param {*} prefix - The prefix to match. + * @returns {string[]} + */ + autocomplete(prefix = '') { + const curr = this.searchNode(prefix); + return this.getAllWords(prefix, curr); + } +} + +// Aliases +Trie.prototype.add = Trie.prototype.insert; + +module.exports = Trie; diff --git a/docs/content/ds/data-structures/trees/trie.spec.js b/docs/content/ds/data-structures/trees/trie.spec.js new file mode 100644 index 0000000000..fca6588e7b --- /dev/null +++ b/docs/content/ds/data-structures/trees/trie.spec.js @@ -0,0 +1,188 @@ +const Trie = require('./trie'); + +describe('Trie', () => { + let trie; + + beforeEach(() => { + trie = new Trie(); + }); + + describe('construtor', () => { + it('should initialize trie', () => { + expect(trie).toBeDefined(); + }); + + it('should set default value to undefined', () => { + expect(trie.val).toEqual(undefined); + }); + + it('should initialization value', () => { + trie = new Trie(1); + expect(trie.val).toEqual(1); + }); + + it('should initialize children as empty map', () => { + expect(trie.children).toEqual({}); + }); + + it('should not be a word by default', () => { + expect(trie.isWord).toEqual(false); + }); + }); + + describe('insert', () => { + it('should insert a word', () => { + trie.insert('ab'); + expect(trie.children.a).toBeDefined(); + expect(trie.children.a.children.b).toBeDefined(); + expect(trie.children.a.isWord).toEqual(false); + expect(trie.children.a.children.b.isWord).toEqual(true); + }); + + it('should insert multiple words with the same root', () => { + trie.insert('a'); + trie.insert('ab'); + expect(trie.children.a.isWord).toEqual(true); + expect(trie.children.a.children.b.isWord).toEqual(true); + }); + }); + + describe('search & startsWith', () => { + beforeEach(() => { + trie.insert('dog'); + trie.insert('dogs'); + trie.insert('door'); + }); + + it('should search for words', () => { + expect(trie.search('dog')).toEqual(true); + }); + + it('should not match incomplete words by default', () => { + expect(trie.search('do')).toEqual(false); + }); + + it('should match partial words if partial is set', () => { + expect(trie.search('do', { + partial: true, + })).toEqual(true); + expect(trie.startsWith('do')).toEqual(true); + }); + + it('should match full words if partial is set', () => { + expect(trie.search('dogs', { + partial: true, + })).toEqual(true); + expect(trie.startsWith('dogs')).toEqual(true); + }); + + it('should not match non existing words', () => { + expect(trie.search('doors')).toEqual(false); + }); + + it('should not match non existing words with partials', () => { + expect(trie.search('doors', { + partial: true, + })).toEqual(false); + expect(trie.startsWith('doors')).toEqual(false); + }); + }); + + describe('when multiple words are inserted', () => { + beforeEach(() => { + trie.insert('dog'); + trie.insert('dogs'); + trie.insert('door'); + trie.insert('day'); + trie.insert('cat'); + }); + + describe('getAllWords', () => { + it('should get all words', () => { + const words = trie.getAllWords(); + expect(words.length).toEqual(5); + expect(words).toEqual(['dog', 'dogs', 'door', 'day', 'cat']); + }); + + it('should use prefix', () => { + const words = trie.getAllWords("Adrian's "); + expect(words.length).toEqual(5); + expect(words).toEqual([ + "Adrian's dog", + "Adrian's dogs", + "Adrian's door", + "Adrian's day", + "Adrian's cat", + ]); + }); + }); + + describe('autocomplete', () => { + it('should return all words if not prefix is given', () => { + const words = trie.autocomplete(); + expect(words.length).toBe(5); + expect(words).toEqual(['dog', 'dogs', 'door', 'day', 'cat']); + }); + + it('should auto complete words given a prefix', () => { + const words = trie.autocomplete('do'); + expect(words.length).toBe(3); + expect(words).toEqual(['dog', 'dogs', 'door']); + }); + + it('should handle non-existing words prefixes', () => { + const words = trie.autocomplete('co'); + expect(words.length).toBe(0); + expect(words).toEqual([]); + }); + }); + + describe('remove', () => { + it('should remove a word', () => { + trie = new Trie(); + trie.insert('a'); + expect(trie.remove('a')).toEqual(true); + expect(trie.getAllWords()).toEqual([]); + }); + + it('should remove word and keep other words', () => { + trie = new Trie(); + trie.insert('a'); + trie.insert('ab'); + expect(trie.remove('a')).toEqual(true); + expect(trie.getAllWords()).toEqual(['ab']); + }); + + it('should remove surrounding word', () => { + trie = new Trie(); + trie.insert('a'); + trie.insert('ab'); + expect(trie.remove('ab')).toEqual(true); + expect(trie.getAllWords()).toEqual(['a']); + }); + + it('should return false when word is not found', () => { + expect(trie.remove('not there')).toBe(false); + }); + + it('should remove words in between and still match', () => { + expect(trie.remove('dog')).toBe(true); + expect(trie.search('dogs')).toBe(true); + expect(trie.startsWith('dog')).toBe(true); + expect(trie.getAllWords()).toEqual([ + 'dogs', 'door', 'day', 'cat', + ]); + }); + + it('should remove word and no longer match partials', () => { + expect(trie.remove('dogs')).toBe(true); + expect(trie.search('dogs')).toBe(false); + expect(trie.search('dog')).toBe(true); + expect(trie.startsWith('dog')).toBe(true); + expect(trie.getAllWords()).toEqual([ + 'dog', 'door', 'day', 'cat', + ]); + }); + }); + }); +}); diff --git a/docs/content/html-docs/docs/node-docs-complete/images/http_/callbackhell.com/index.html b/docs/content/html-docs/docs/node-docs-complete/images/http_/callbackhell.com/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/http_/callbackhell.com/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/http_/koajs.com/index.html b/docs/content/html-docs/docs/node-docs-complete/images/http_/koajs.com/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/http_/koajs.com/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/adonisjs.com/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/adonisjs.com/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/adonisjs.com/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/adonisjs.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/adonisjs.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/adonisjs.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/angular.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/angular.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/angular.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/caniuse.com/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/caniuse.com/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/caniuse.com/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/caniuse.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/caniuse.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/caniuse.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/developer.apple.com/documentation/javascriptcore.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/developer.apple.com/documentation/javascriptcore.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/developer.apple.com/documentation/javascriptcore.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/developer.mozilla.org/en-US/docs/WebAssembly.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/developer.mozilla.org/en-US/docs/WebAssembly.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/developer.mozilla.org/en-US/docs/WebAssembly.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/docs.npmjs.com/cli/v7/configuring-npm/package-json.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/docs.npmjs.com/cli/v7/configuring-npm/package-json.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/docs.npmjs.com/cli/v7/configuring-npm/package-json.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/docs.wasmtime.dev/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/docs.wasmtime.dev/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/docs.wasmtime.dev/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/eggjs.org/en/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/eggjs.org/en/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/eggjs.org/en/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/emscripten.org/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/emscripten.org/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/emscripten.org/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Application_layer.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Application_layer.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Application_layer.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Computer_network.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Computer_network.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Computer_network.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Endianness.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Endianness.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Endianness.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Hypertext_Transfer_Protocol.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Hypertext_Transfer_Protocol.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Hypertext_Transfer_Protocol.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/OSI_model.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/OSI_model.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/OSI_model.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Transmission_Control_Protocol.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Transmission_Control_Protocol.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Transmission_Control_Protocol.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Transport_layer.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Transport_layer.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/Transport_layer.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/User_Datagram_Protocol.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/User_Datagram_Protocol.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/en.wikipedia.org/wiki/User_Datagram_Protocol.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/expressjs.com/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/expressjs.com/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/expressjs.com/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/fastify.io/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/fastify.io/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/fastify.io/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/feathersjs.com/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/feathersjs.com/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/feathersjs.com/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/fetch.spec.whatwg.org/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/fetch.spec.whatwg.org/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/fetch.spec.whatwg.org/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/gist.github.com/iamnewton/8754917.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/gist.github.com/iamnewton/8754917.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/gist.github.com/iamnewton/8754917.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/Microsoft/ChakraCore.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/Microsoft/ChakraCore.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/Microsoft/ChakraCore.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/SBoudrias/Inquirer.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/SBoudrias/Inquirer.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/SBoudrias/Inquirer.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/axios/axios.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/axios/axios.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/axios/axios.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/chalk/chalk.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/chalk/chalk.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/chalk/chalk.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/webassembly/wabt.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/webassembly/wabt.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/webassembly/wabt.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/zeit/micro.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/zeit/micro.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/github.com/zeit/micro.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/graphql.org/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/graphql.org/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/graphql.org/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/hapijs.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/hapijs.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/hapijs.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/loopback.io/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/loopback.io/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/loopback.io/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/meteor.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/meteor.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/meteor.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nestjs.com/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nestjs.com/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nestjs.com/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nextjs.org/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nextjs.org/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nextjs.org/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/learn/discover-javascript-timers.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/learn/discover-javascript-timers.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/learn/discover-javascript-timers.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/learn/semantic-versioning-using-npm/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/learn/semantic-versioning-using-npm/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/learn/semantic-versioning-using-npm/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/learn/the-nodejs-event-loop.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/learn/the-nodejs-event-loop.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/learn/the-nodejs-event-loop.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/learn/understanding-javascript-promises.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/learn/understanding-javascript-promises.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/learn/understanding-javascript-promises.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/the-nodejs-http-module.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/the-nodejs-http-module.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.dev/the-nodejs-http-module.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/buffer.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/buffer.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/buffer.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/console.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/console.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/console.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/dgram.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/dgram.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/dgram.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/errors.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/errors.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/errors.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/events.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/events.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/events.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/fs.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/fs.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/fs.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/http.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/http.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/http.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/modules.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/modules.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/modules.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/net.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/net.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/net.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/readline.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/readline.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/readline.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/stream.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/stream.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/stream.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/timers.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/timers.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/api/timers.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/docs/latest-v11.x/api/util.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/docs/latest-v11.x/api/util.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nodejs.org/docs/latest-v11.x/api/util.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/nx.dev/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/nx.dev/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/nx.dev/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/pnpm.js.org/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/pnpm.js.org/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/pnpm.js.org/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/prisma.io/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/prisma.io/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/prisma.io/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/reactjs.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/reactjs.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/reactjs.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/reactjs.org/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/reactjs.org/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/reactjs.org/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/rustwasm.github.io/wasm-pack/book/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/rustwasm.github.io/wasm-pack/book/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/rustwasm.github.io/wasm-pack/book/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/rxjs.dev/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/rxjs.dev/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/rxjs.dev/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/sapper.svelte.dev/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/sapper.svelte.dev/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/sapper.svelte.dev/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/socket.io/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/socket.io/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/socket.io/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/spidermonkey.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/spidermonkey.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/spidermonkey.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/strapi.io/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/strapi.io/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/strapi.io/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/typeorm.io/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/typeorm.io/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/typeorm.io/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/v8.dev/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/v8.dev/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/v8.dev/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/vuejs.org/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/vuejs.org/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/vuejs.org/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/wasi.dev/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/wasi.dev/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/wasi.dev/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/webassembly.github.io/spec/core/text/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/webassembly.github.io/spec/core/text/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/webassembly.github.io/spec/core/text/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/webassembly.org/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/webassembly.org/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/webassembly.org/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/www.assemblyscript.org/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.assemblyscript.org/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.assemblyscript.org/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/www.gatsbyjs.com/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.gatsbyjs.com/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.gatsbyjs.com/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/browserslist.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/browserslist.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/browserslist.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/cowsay.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/cowsay.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/cowsay.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/dotenv.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/dotenv.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/dotenv.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/fs-extra.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/fs-extra.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/fs-extra.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/minimist.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/minimist.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/minimist.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/node.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/node.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/node.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/npx.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/npx.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/npx.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/progress.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/progress.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/progress.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/readline-sync.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/readline-sync.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.npmjs.com/package/readline-sync.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/www.typescriptlang.org/docs.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.typescriptlang.org/docs.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/www.typescriptlang.org/docs.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-complete/images/https_/yarnpkg.com/en/index.html b/docs/content/html-docs/docs/node-docs-complete/images/https_/yarnpkg.com/en/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-complete/images/https_/yarnpkg.com/en/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/node-docs-full/index.html b/docs/content/html-docs/docs/node-docs-full/index.html new file mode 100644 index 0000000000..458b39f9b9 --- /dev/null +++ b/docs/content/html-docs/docs/node-docs-full/index.html @@ -0,0 +1,1101 @@ + +Node Docs | webdevhub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Node Docs

Cheat Sheet:

/* *******************************************************************************************
+ * SYNOPSIS
+ * http://nodejs.org/api/synopsis.html
+ * ******************************************************************************************* */
+
+
+var http = require('http');
+
+// An example of a web server written with Node which responds with 'Hello World'.
+// To run the server, put the code into a file called example.js and execute it with the node program.
+http.createServer(function (request, response) {
+  response.writeHead(200, {'Content-Type': 'text/plain'});
+  response.end('Hello World\n');
+}).listen(8124);
+
+console.log('Server running at http://127.0.0.1:8124/');
+
+
+/* *******************************************************************************************
+ * GLOBAL OBJECTS
+ * http://nodejs.org/api/globals.html
+ * ******************************************************************************************* */
+
+
+// In browsers, the top-level scope is the global scope.
+// That means that in browsers if you're in the global scope var something will define a global variable.
+// In Node this is different. The top-level scope is not the global scope; var something inside a Node module will be local to that module.
+
+__filename;  // The filename of the code being executed. (absolute path)
+__dirname;   // The name of the directory that the currently executing script resides in. (absolute path)
+module;      // A reference to the current module. In particular module.exports is used for defining what a module exports and makes available through require().
+exports;     // A reference to the module.exports that is shorter to type.
+process;     // The process object is a global object and can be accessed from anywhere. It is an instance of EventEmitter.
+Buffer;      // The Buffer class is a global type for dealing with binary data directly.
+
+
+/* *******************************************************************************************
+ * CONSOLE
+ * http://nodejs.org/api/console.html
+ * ******************************************************************************************* */
+
+
+console.log([data], [...]);             // Prints to stdout with newline.
+console.info([data], [...]);            // Same as console.log.
+console.error([data], [...]);           // Same as console.log but prints to stderr.
+console.warn([data], [...]);            // Same as console.error.
+console.dir(obj);                       // Uses util.inspect on obj and prints resulting string to stdout.
+console.time(label);                    // Mark a time.
+console.timeEnd(label);                 // Finish timer, record output.
+console.trace(label);                   // Print a stack trace to stderr of the current position.
+console.assert(expression, [message]);  // Same as assert.ok() where if the expression evaluates as false throw an AssertionError with message.
+
+
+/* *******************************************************************************************
+ * TIMERS
+ * http://nodejs.org/api/timers.html
+ * ******************************************************************************************* */
+
+
+setTimeout(callback, delay, [arg], [...]);   // To schedule execution of a one-time callback after delay milliseconds. Optionally you can also pass arguments to the callback.
+clearTimeout(t);                             // Stop a timer that was previously created with setTimeout().
+setInterval(callback, delay, [arg], [...]);  // To schedule the repeated execution of callback every delay milliseconds. Optionally you can also pass arguments to the callback.
+clearInterval(t);                            // Stop a timer that was previously created with setInterval().
+setImmediate(callback, [arg], [...]);        // To schedule the "immediate" execution of callback after I/O events callbacks and before setTimeout and setInterval.
+clearImmediate(immediateObject);             // Stop a timer that was previously created with setImmediate().
+
+unref();  // Allow you to create a timer that is active but if it is the only item left in the event loop, node won't keep the program running.
+ref();    // If you had previously unref()d a timer you can call ref() to explicitly request the timer hold the program open.
+
+
+/* *******************************************************************************************
+ * MODULES
+ * http://nodejs.org/api/modules.html
+ * ******************************************************************************************* */
+
+
+var module = require('./module.js');    // Loads the module module.js in the same directory.
+module.require('./another_module.js');  // load another_module as if require() was called from the module itself.
+
+module.id;        // The identifier for the module. Typically this is the fully resolved filename.
+module.filename;  // The fully resolved filename to the module.
+module.loaded;    // Whether or not the module is done loading, or is in the process of loading.
+module.parent;    // The module that required this one.
+module.children;  // The module objects required by this one.
+
+exports.area = function (r) {
+  return Math.PI * r * r;
+};
+
+// If you want the root of your module's export to be a function (such as a constructor)
+// or if you want to export a complete object in one assignment instead of building it one property at a time,
+// assign it to module.exports instead of exports.
+module.exports = function(width) {
+  return {
+    area: function() {
+      return width * width;
+    }
+  };
+}
+
+
+/* *******************************************************************************************
+ * PROCESS
+ * http://nodejs.org/api/process.html
+ * ******************************************************************************************* */
+
+
+process.on('exit', function(code) {});              // Emitted when the process is about to exit
+process.on('uncaughtException', function(err) {});  // Emitted when an exception bubbles all the way back to the event loop. (should not be used)
+
+process.stdout;           // A writable stream to stdout.
+process.stderr;           // A writable stream to stderr.
+process.stdin;            // A readable stream for stdin.
+
+process.argv;             // An array containing the command line arguments.
+process.env;              // An object containing the user environment.
+process.execPath;         // This is the absolute pathname of the executable that started the process.
+process.execArgv;         // This is the set of node-specific command line options from the executable that started the process.
+
+process.arch;             // What processor architecture you're running on: 'arm', 'ia32', or 'x64'.
+process.config;           // An Object containing the JavaScript representation of the configure options that were used to compile the current node executable.
+process.pid;              // The PID of the process.
+process.platform;         // What platform you're running on: 'darwin', 'freebsd', 'linux', 'sunos' or 'win32'.
+process.title;            // Getter/setter to set what is displayed in 'ps'.
+process.version;          // A compiled-in property that exposes NODE_VERSION.
+process.versions;         // A property exposing version strings of node and its dependencies.
+
+process.abort();          // This causes node to emit an abort. This will cause node to exit and generate a core file.
+process.chdir(dir);       // Changes the current working directory of the process or throws an exception if that fails.
+process.cwd();            // Returns the current working directory of the process.
+process.exit([code]);     // Ends the process with the specified code. If omitted, exit uses the 'success' code 0.
+process.getgid();         // Gets the group identity of the process.
+process.setgid(id);       // Sets the group identity of the process.
+process.getuid();         // Gets the user identity of the process.
+process.setuid(id);       // Sets the user identity of the process.
+process.getgroups();      // Returns an array with the supplementary group IDs.
+process.setgroups(grps);  // Sets the supplementary group IDs.
+
+process.initgroups(user, extra_grp);  // Reads /etc/group and initializes the group access list, using all groups of which the user is a member.
+process.kill(pid, [signal]);          // Send a signal to a process. pid is the process id and signal is the string describing the signal to send.
+process.memoryUsage();                // Returns an object describing the memory usage of the Node process measured in bytes.
+process.nextTick(callback);           // On the next loop around the event loop call this callback.
+process.maxTickDepth;                 // Callbacks passed to process.nextTick will usually be called at the end of the current flow of execution, and are thus approximately as fast as calling a function synchronously.
+process.umask([mask]);                // Sets or reads the process's file mode creation mask.
+process.uptime();                     // Number of seconds Node has been running.
+process.hrtime();                     // Returns the current high-resolution real time in a [seconds, nanoseconds] tuple Array.
+
+
+/* *******************************************************************************************
+ * CHILD PROCESS
+ * http://nodejs.org/api/child_process.html
+ * ******************************************************************************************* */
+
+
+// Node provides a tri-directional popen facility through the child_process module.
+// It is possible to stream data through a child's stdin, stdout, and stderr in a fully non-blocking way.
+
+ChildProcess;                                                 // Class. ChildProcess is an EventEmitter.
+
+child.stdin;                                                  // A Writable Stream that represents the child process's stdin
+child.stdout;                                                 // A Readable Stream that represents the child process's stdout
+child.stderr;                                                 // A Readable Stream that represents the child process's stderr.
+child.pid;                                                    // The PID of the child process
+child.connected;                                              // If .connected is false, it is no longer possible to send messages
+child.kill([signal]);                                         // Send a signal to the child process
+child.send(message, [sendHandle]);                            // When using child_process.fork() you can write to the child using child.send(message, [sendHandle]) and messages are received by a 'message' event on the child.
+child.disconnect();                                           // Close the IPC channel between parent and child, allowing the child to exit gracefully once there are no other connections keeping it alive.
+child_process.spawn(command, [args], [options]);              // Launches a new process with the given command, with command line arguments in args. If omitted, args defaults to an empty Array.
+child_process.exec(command, [options], callback);             // Runs a command in a shell and buffers the output.
+child_process.execFile(file, [args], [options], [callback]);  // Runs a command in a shell and buffers the output.
+child_process.fork(modulePath, [args], [options]);            // This is a special case of the spawn() functionality for spawning Node processes. In addition to having all the methods in a normal ChildProcess instance, the returned object has a communication channel built-in. 
+
+
+/* *******************************************************************************************
+ * UTIL
+ * http://nodejs.org/api/util.html
+ * ******************************************************************************************* */
+
+
+// These functions are in the module 'util'. Use require('util') to access them.
+
+util.format(format, [...]);    // Returns a formatted string using the first argument as a printf-like format. (%s, %d, %j)
+util.debug(string);            // A synchronous output function. Will block the process and output string immediately to stderr.
+util.error([...]);             // Same as util.debug() except this will output all arguments immediately to stderr.
+util.puts([...]);              // A synchronous output function. Will block the process and output all arguments to stdout with newlines after each argument.
+util.print([...]);             // A synchronous output function. Will block the process, cast each argument to a string then output to stdout. (no newlines)
+util.log(string);              // Output with timestamp on stdout.
+util.inspect(object, [opts]);  // Return a string representation of object, which is useful for debugging. (options: showHidden, depth, colors, customInspect)
+util.isArray(object);          // Returns true if the given "object" is an Array. false otherwise.
+util.isRegExp(object);         // Returns true if the given "object" is a RegExp. false otherwise.
+util.isDate(object);           // Returns true if the given "object" is a Date. false otherwise.
+util.isError(object);          // Returns true if the given "object" is an Error. false otherwise.
+util.promisify(fn)             // Takes a function whose last argument is a callback and returns a version that returns promises.
+
+util.inherits(constructor, superConstructor);  // Inherit the prototype methods from one constructor into another.
+
+
+/* *******************************************************************************************
+ * EVENTS
+ * http://nodejs.org/api/events.html
+ * ******************************************************************************************* */
+
+
+// All objects which emit events are instances of events.EventEmitter. You can access this module by doing: require("events");
+// To access the EventEmitter class, require('events').EventEmitter.
+// All EventEmitters emit the event 'newListener' when new listeners are added and 'removeListener' when a listener is removed.
+
+emitter.addListener(event, listener);        // Adds a listener to the end of the listeners array for the specified event.
+emitter.on(event, listener);                 // Same as emitter.addListener().
+emitter.once(event, listener);               // Adds a one time listener for the event. This listener is invoked only the next time the event is fired, after which it is removed.
+emitter.removeListener(event, listener);     // Remove a listener from the listener array for the specified event.
+emitter.removeAllListeners([event]);         // Removes all listeners, or those of the specified event.
+emitter.setMaxListeners(n);                  // By default EventEmitters will print a warning if more than 10 listeners are added for a particular event.
+emitter.listeners(event);                    // Returns an array of listeners for the specified event.
+emitter.emit(event, [arg1], [arg2], [...]);  // Execute each of the listeners in order with the supplied arguments. Returns true if event had listeners, false otherwise.
+
+EventEmitter.listenerCount(emitter, event);  // Return the number of listeners for a given event.
+
+
+/* *******************************************************************************************
+ * STREAM
+ * http://nodejs.org/api/stream.html
+ * ******************************************************************************************* */
+
+
+// A stream is an abstract interface implemented by various objects in Node. For example a request to an HTTP server is a stream, as is stdout.
+// Streams are readable, writable, or both. All streams are instances of EventEmitter.
+
+// The Readable stream interface is the abstraction for a source of data that you are reading from.
+// In other words, data comes out of a Readable stream.
+// A Readable stream will not start emitting data until you indicate that you are ready to receive it.
+// Examples of readable streams include: http responses on the client, http requests on the server, fs read streams
+// zlib streams, crypto streams, tcp sockets, child process stdout and stderr, process.stdin.
+
+var readable = getReadableStreamSomehow();
+
+readable.on('readable', function() {});   // When a chunk of data can be read from the stream, it will emit a 'readable' event.
+readable.on('data', function(chunk) {});  // If you attach a data event listener, then it will switch the stream into flowing mode, and data will be passed to your handler as soon as it is available.
+readable.on('end', function() {});        // This event fires when there will be no more data to read.
+readable.on('close', function() {});      // Emitted when the underlying resource (for example, the backing file descriptor) has been closed. Not all streams will emit this.
+readable.on('error', function() {});      // Emitted if there was an error receiving data.
+
+// The read() method pulls some data out of the internal buffer and returns it. If there is no data available, then it will return null.
+// This method should only be called in non-flowing mode. In flowing-mode, this method is called automatically until the internal buffer is drained.
+readable.read([size]);
+
+readable.setEncoding(encoding);           // Call this function to cause the stream to return strings of the specified encoding instead of Buffer objects.
+readable.resume();                        // This method will cause the readable stream to resume emitting data events.
+readable.pause();                         // This method will cause a stream in flowing-mode to stop emitting data events.
+readable.pipe(destination, [options]);    // This method pulls all the data out of a readable stream, and writes it to the supplied destination, automatically managing the flow so that the destination is not overwhelmed by a fast readable stream.
+readable.unpipe([destination]);           // This method will remove the hooks set up for a previous pipe() call. If the destination is not specified, then all pipes are removed.
+readable.unshift(chunk);                  // This is useful in certain cases where a stream is being consumed by a parser, which needs to "un-consume" some data that it has optimistically pulled out of the source, so that the stream can be passed on to some other party.
+
+
+// The Writable stream interface is an abstraction for a destination that you are writing data to.
+// Examples of writable streams include: http requests on the client, http responses on the server, fs write streams,
+// zlib streams, crypto streams, tcp sockets, child process stdin, process.stdout, process.stderr.
+
+var writer = getWritableStreamSomehow();
+
+writable.write(chunk, [encoding], [callback]);  // This method writes some data to the underlying system, and calls the supplied callback once the data has been fully handled.
+writer.once('drain', write);                    // If a writable.write(chunk) call returns false, then the drain event will indicate when it is appropriate to begin writing more data to the stream.
+
+writable.end([chunk], [encoding], [callback]);  // Call this method when no more data will be written to the stream.
+writer.on('finish', function() {});             // When the end() method has been called, and all data has been flushed to the underlying system, this event is emitted.
+writer.on('pipe', function(src) {});            // This is emitted whenever the pipe() method is called on a readable stream, adding this writable to its set of destinations.
+writer.on('unpipe', function(src) {});          // This is emitted whenever the unpipe() method is called on a readable stream, removing this writable from its set of destinations.
+writer.on('error', function(src) {});           // Emitted if there was an error when writing or piping data.
+
+
+// Duplex streams are streams that implement both the Readable and Writable interfaces. See above for usage.
+// Examples of Duplex streams include: tcp sockets, zlib streams, crypto streams.
+
+// Transform streams are Duplex streams where the output is in some way computed from the input. They implement both the Readable and Writable interfaces. See above for usage.
+// Examples of Transform streams include: zlib streams, crypto streams.
+
+
+/* *******************************************************************************************
+ * FILE SYSTEM
+ * http://nodejs.org/api/fs.html
+ * ******************************************************************************************* */
+
+
+// To use this module do require('fs').
+// All the methods have asynchronous and synchronous forms.
+
+fs.rename(oldPath, newPath, callback);  // Asynchronous rename. No arguments other than a possible exception are given to the completion callback.Asynchronous ftruncate. No arguments other than a possible exception are given to the completion callback.
+fs.renameSync(oldPath, newPath);        // Synchronous rename.
+
+fs.ftruncate(fd, len, callback);        // Asynchronous ftruncate. No arguments other than a possible exception are given to the completion callback.
+fs.ftruncateSync(fd, len);              // Synchronous ftruncate.
+fs.truncate(path, len, callback);       // Asynchronous truncate. No arguments other than a possible exception are given to the completion callback.
+fs.truncateSync(path, len);             // Synchronous truncate.
+
+fs.chown(path, uid, gid, callback);     // Asynchronous chown. No arguments other than a possible exception are given to the completion callback.
+fs.chownSync(path, uid, gid);           // Synchronous chown.
+fs.fchown(fd, uid, gid, callback);      // Asynchronous fchown. No arguments other than a possible exception are given to the completion callback.
+fs.fchownSync(fd, uid, gid);            // Synchronous fchown.
+fs.lchown(path, uid, gid, callback);    // Asynchronous lchown. No arguments other than a possible exception are given to the completion callback.
+fs.lchownSync(path, uid, gid);          // Synchronous lchown.
+
+fs.chmod(path, mode, callback);         // Asynchronous chmod. No arguments other than a possible exception are given to the completion callback.
+fs.chmodSync(path, mode);               // Synchronous chmod.
+fs.fchmod(fd, mode, callback);          // Asynchronous fchmod. No arguments other than a possible exception are given to the completion callback.
+fs.fchmodSync(fd, mode);                // Synchronous fchmod.
+fs.lchmod(path, mode, callback);        // Asynchronous lchmod. No arguments other than a possible exception are given to the completion callback.
+fs.lchmodSync(path, mode);              // Synchronous lchmod.
+
+fs.stat(path, callback);                // Asynchronous stat. The callback gets two arguments (err, stats) where stats is a fs.Stats object. 
+fs.statSync(path);                      // Synchronous stat. Returns an instance of fs.Stats.
+fs.lstat(path, callback);               // Asynchronous lstat. The callback gets two arguments (err, stats) where stats is a fs.Stats object. lstat() is identical to stat(), except that if path is a symbolic link, then the link itself is stat-ed, not the file that it refers to.
+fs.lstatSync(path);                     // Synchronous lstat. Returns an instance of fs.Stats.
+fs.fstat(fd, callback);                 // Asynchronous fstat. The callback gets two arguments (err, stats) where stats is a fs.Stats object. fstat() is identical to stat(), except that the file to be stat-ed is specified by the file descriptor fd.
+fs.fstatSync(fd);                       // Synchronous fstat. Returns an instance of fs.Stats.
+
+fs.link(srcpath, dstpath, callback);             // Asynchronous link. No arguments other than a possible exception are given to the completion callback.
+fs.linkSync(srcpath, dstpath);                   // Synchronous link.
+fs.symlink(srcpath, dstpath, [type], callback);  // Asynchronous symlink. No arguments other than a possible exception are given to the completion callback. The type argument can be set to 'dir', 'file', or 'junction' (default is 'file') and is only available on Windows (ignored on other platforms)
+fs.symlinkSync(srcpath, dstpath, [type]);        // Synchronous symlink.
+fs.readlink(path, callback);                     // Asynchronous readlink. The callback gets two arguments (err, linkString).
+fs.readlinkSync(path);                           // Synchronous readlink. Returns the symbolic link's string value.
+fs.unlink(path, callback);                       // Asynchronous unlink. No arguments other than a possible exception are given to the completion callback.
+fs.unlinkSync(path);                             // Synchronous unlink.
+
+fs.realpath(path, [cache], callback);     // Asynchronous realpath. The callback gets two arguments (err, resolvedPath).
+fs.realpathSync(path, [cache]);           // Synchronous realpath. Returns the resolved path.
+
+fs.rmdir(path, callback);                 // Asynchronous rmdir. No arguments other than a possible exception are given to the completion callback.
+fs.rmdirSync(path);                       // Synchronous rmdir.
+fs.mkdir(path, [mode], callback);         // Asynchronous mkdir. No arguments other than a possible exception are given to the completion callback. mode defaults to 0777.
+fs.mkdirSync(path, [mode]);               // Synchronous mkdir.
+fs.readdir(path, callback);               // Asynchronous readdir. Reads the contents of a directory. The callback gets two arguments (err, files) where files is an array of the names of the files in the directory excluding '.' and '..'.
+fs.readdirSync(path);                     // Synchronous readdir. Returns an array of filenames excluding '.' and '..'.
+fs.close(fd, callback);                   // Asynchronous close. No arguments other than a possible exception are given to the completion callback.
+fs.closeSync(fd);                         // Synchronous close.
+fs.open(path, flags, [mode], callback);   // Asynchronous file open.
+fs.openSync(path, flags, [mode]);         // Synchronous version of fs.open().
+fs.utimes(path, atime, mtime, callback);  // Change file timestamps of the file referenced by the supplied path.
+fs.utimesSync(path, atime, mtime);        // Synchronous version of fs.utimes().
+fs.futimes(fd, atime, mtime, callback);   // Change the file timestamps of a file referenced by the supplied file descriptor.
+fs.futimesSync(fd, atime, mtime);         // Synchronous version of fs.futimes().
+fs.fsync(fd, callback);                   // Asynchronous fsync. No arguments other than a possible exception are given to the completion callback.
+fs.fsyncSync(fd);                         // Synchronous fsync.
+
+fs.write(fd, buffer, offset, length, position, callback);  // Write buffer to the file specified by fd.
+fs.writeSync(fd, buffer, offset, length, position);        // Synchronous version of fs.write(). Returns the number of bytes written.
+fs.read(fd, buffer, offset, length, position, callback);   // Read data from the file specified by fd.
+fs.readSync(fd, buffer, offset, length, position);         // Synchronous version of fs.read. Returns the number of bytesRead.
+fs.readFile(filename, [options], callback);                // Asynchronously reads the entire contents of a file.
+fs.readFileSync(filename, [options]);                      // Synchronous version of fs.readFile. Returns the contents of the filename. If the encoding option is specified then this function returns a string. Otherwise it returns a buffer.
+
+fs.writeFile(filename, data, [options], callback);   // Asynchronously writes data to a file, replacing the file if it already exists. data can be a string or a buffer.
+fs.writeFileSync(filename, data, [options]);         // The synchronous version of fs.writeFile.
+fs.appendFile(filename, data, [options], callback);  // Asynchronously append data to a file, creating the file if it not yet exists. data can be a string or a buffer.
+fs.appendFileSync(filename, data, [options]);        // The synchronous version of fs.appendFile.
+fs.watch(filename, [options], [listener]);           // Watch for changes on filename, where filename is either a file or a directory. The returned object is a fs.FSWatcher. The listener callback gets two arguments (event, filename). event is either 'rename' or 'change', and filename is the name of the file which triggered the event.
+fs.exists(path, callback);                           // Test whether or not the given path exists by checking with the file system. Then call the callback argument with either true or false. (should not be used)
+fs.existsSync(path);                                 // Synchronous version of fs.exists. (should not be used)
+
+// fs.Stats: objects returned from fs.stat(), fs.lstat() and fs.fstat() and their synchronous counterparts are of this type.
+stats.isFile();
+stats.isDirectory()
+stats.isBlockDevice()
+stats.isCharacterDevice()
+stats.isSymbolicLink()  // (only valid with fs.lstat())
+stats.isFIFO()
+stats.isSocket()
+
+fs.createReadStream(path, [options]);   // Returns a new ReadStream object.
+fs.createWriteStream(path, [options]);  // Returns a new WriteStream object.
+
+
+/* *******************************************************************************************
+ * PATH
+ * http://nodejs.org/api/fs.html
+ * ******************************************************************************************* */
+
+
+// Use require('path') to use this module.
+// This module contains utilities for handling and transforming file paths.
+// Almost all these methods perform only string transformations.
+// The file system is not consulted to check whether paths are valid.
+
+path.normalize(p);                    // Normalize a string path, taking care of '..' and '.' parts.
+path.join([path1], [path2], [...]);   // Join all arguments together and normalize the resulting path.
+path.resolve([from ...], to);         // Resolves 'to' to an absolute path.
+path.relative(from, to);              // Solve the relative path from 'from' to 'to'.
+path.dirname(p);                      // Return the directory name of a path. Similar to the Unix dirname command.
+path.basename(p, [ext]);              // Return the last portion of a path. Similar to the Unix basename command.
+path.extname(p);                      // Return the extension of the path, from the last '.' to end of string in the last portion of the path.
+
+path.sep;                             // The platform-specific file separator. '\\' or '/'.
+path.delimiter;                       // The platform-specific path delimiter, ';' or ':'.
+
+
+/* *******************************************************************************************
+ * HTTP
+ * http://nodejs.org/api/http.html
+ * ******************************************************************************************* */
+
+
+// To use the HTTP server and client one must require('http').
+
+http.STATUS_CODES;                                             // A collection of all the standard HTTP response status codes, and the short description of each.
+http.request(options, [callback]);                             // This function allows one to transparently issue requests.
+http.get(options, [callback]);                                 // Set the method to GET and calls req.end() automatically.
+
+server = http.createServer([requestListener]);                 // Returns a new web server object. The requestListener is a function which is automatically added to the 'request' event.
+server.listen(port, [hostname], [backlog], [callback]);        // Begin accepting connections on the specified port and hostname.
+server.listen(path, [callback]);                               // Start a UNIX socket server listening for connections on the given path.
+server.listen(handle, [callback]);                             // The handle object can be set to either a server or socket (anything with an underlying _handle member), or a {fd: <n>} object.
+server.close([callback]);                                      // Stops the server from accepting new connections. 
+server.setTimeout(msecs, callback);                            // Sets the timeout value for sockets, and emits a 'timeout' event on the Server object, passing the socket as an argument, if a timeout occurs.
+
+server.maxHeadersCount;  // Limits maximum incoming headers count, equal to 1000 by default. If set to 0 - no limit will be applied.
+server.timeout;          // The number of milliseconds of inactivity before a socket is presumed to have timed out.
+
+server.on('request', function (request, response) { });        // Emitted each time there is a request.
+server.on('connection', function (socket) { });                // When a new TCP stream is established.
+server.on('close', function () { });                           // Emitted when the server closes.
+server.on('checkContinue', function (request, response) { });  // Emitted each time a request with an http Expect: 100-continue is received.
+server.on('connect', function (request, socket, head) { });    // Emitted each time a client requests a http CONNECT method.
+server.on('upgrade', function (request, socket, head) { });    // Emitted each time a client requests a http upgrade.
+server.on('clientError', function (exception, socket) { });    // If a client connection emits an 'error' event - it will forwarded here.
+
+request.write(chunk, [encoding]);                              // Sends a chunk of the body.
+request.end([data], [encoding]);                               // Finishes sending the request. If any parts of the body are unsent, it will flush them to the stream.
+request.abort();                                               // Aborts a request.
+request.setTimeout(timeout, [callback]);                       // Once a socket is assigned to this request and is connected socket.setTimeout() will be called.
+request.setNoDelay([noDelay]);                                 // Once a socket is assigned to this request and is connected socket.setNoDelay() will be called.
+request.setSocketKeepAlive([enable], [initialDelay]);          // Once a socket is assigned to this request and is connected socket.setKeepAlive() will be called.
+
+request.on('response', function(response) { });                // Emitted when a response is received to this request. This event is emitted only once.
+request.on('socket', function(socket) { });                    // Emitted after a socket is assigned to this request.
+request.on('connect', function(response, socket, head) { });   // Emitted each time a server responds to a request with a CONNECT method. If this event isn't being listened for, clients receiving a CONNECT method will have their connections closed.
+request.on('upgrade', function(response, socket, head) { });   // Emitted each time a server responds to a request with an upgrade. If this event isn't being listened for, clients receiving an upgrade header will have their connections closed.
+request.on('continue', function() { });                        // Emitted when the server sends a '100 Continue' HTTP response, usually because the request contained 'Expect: 100-continue'. This is an instruction that the client should send the request body.
+
+response.write(chunk, [encoding]);                             // This sends a chunk of the response body. If this merthod is called and response.writeHead() has not been called, it will switch to implicit header mode and flush the implicit headers.
+response.writeContinue();                                      // Sends a HTTP/1.1 100 Continue message to the client, indicating that the request body should be sent.
+response.writeHead(statusCode, [reasonPhrase], [headers]);     // Sends a response header to the request.
+response.setTimeout(msecs, callback);                          // Sets the Socket's timeout value to msecs. If a callback is provided, then it is added as a listener on the 'timeout' event on the response object.
+response.setHeader(name, value);                               // Sets a single header value for implicit headers. If this header already exists in the to-be-sent headers, its value will be replaced. Use an array of strings here if you need to send multiple headers with the same name.
+response.getHeader(name);                                      // Reads out a header that's already been queued but not sent to the client. Note that the name is case insensitive.
+response.removeHeader(name);                                   // Removes a header that's queued for implicit sending.
+response.addTrailers(headers);                                 // This method adds HTTP trailing headers (a header but at the end of the message) to the response.
+response.end([data], [encoding]);                              // This method signals to the server that all of the response headers and body have been sent; that server should consider this message complete. The method, response.end(), MUST be called on each response.
+
+response.statusCode;                                           // When using implicit headers (not calling response.writeHead() explicitly), this property controls the status code that will be sent to the client when the headers get flushed.
+response.headersSent;                                          // Boolean (read-only). True if headers were sent, false otherwise.
+response.sendDate;                                             // When true, the Date header will be automatically generated and sent in the response if it is not already present in the headers. Defaults to true.
+
+response.on('close', function () { });  // Indicates that the underlying connection was terminated before response.end() was called or able to flush.
+response.on('finish', function() { });  // Emitted when the response has been sent. 
+
+message.httpVersion;                    // In case of server request, the HTTP version sent by the client. In the case of client response, the HTTP version of the connected-to server.
+message.headers;                        // The request/response headers object.
+message.trailers;                       // The request/response trailers object. Only populated after the 'end' event.
+message.method;                         // The request method as a string. Read only. Example: 'GET', 'DELETE'.
+message.url;                            // Request URL string. This contains only the URL that is present in the actual HTTP request.
+message.statusCode;                     // The 3-digit HTTP response status code. E.G. 404.
+message.socket;                         // The net.Socket object associated with the connection.
+
+message.setTimeout(msecs, callback);    // Calls message.connection.setTimeout(msecs, callback).
+
+
+/* *******************************************************************************************
+ * URL
+ * http://nodejs.org/api/url.html
+ * ******************************************************************************************* */
+
+
+// This module has utilities for URL resolution and parsing. Call require('url') to use it.
+
+url.parse(urlStr, [parseQueryString], [slashesDenoteHost]);  // Take a URL string, and return an object.
+url.format(urlObj);                                          // Take a parsed URL object, and return a formatted URL string.
+url.resolve(from, to);                                       // Take a base URL, and a href URL, and resolve them as a browser would for an anchor tag.
+
+
+/* *******************************************************************************************
+ * QUERY STRING
+ * http://nodejs.org/api/querystring.html
+ * ******************************************************************************************* */
+
+
+// This module provides utilities for dealing with query strings. Call require('querystring') to use it.
+
+querystring.stringify(obj, [sep], [eq]);         // Serialize an object to a query string. Optionally override the default separator ('&') and assignment ('=') characters.
+querystring.parse(str, [sep], [eq], [options]);  // Deserialize a query string to an object. Optionally override the default separator ('&') and assignment ('=') characters.
+
+
+/* *******************************************************************************************
+ * ASSERT
+ * http://nodejs.org/api/assert.html
+ * ******************************************************************************************* */
+
+
+// This module is used for writing unit tests for your applications, you can access it with require('assert').
+
+assert.fail(actual, expected, message, operator);     // Throws an exception that displays the values for actual and expected separated by the provided operator.
+assert(value, message); assert.ok(value, [message]);  // Tests if value is truthy, it is equivalent to assert.equal(true, !!value, message);
+assert.equal(actual, expected, [message]);            // Tests shallow, coercive equality with the equal comparison operator ( == ).
+assert.notEqual(actual, expected, [message]);         // Tests shallow, coercive non-equality with the not equal comparison operator ( != ).
+assert.deepEqual(actual, expected, [message]);        // Tests for deep equality.
+assert.notDeepEqual(actual, expected, [message]);     // Tests for any deep inequality.
+assert.strictEqual(actual, expected, [message]);      // Tests strict equality, as determined by the strict equality operator ( === )
+assert.notStrictEqual(actual, expected, [message]);   // Tests strict non-equality, as determined by the strict not equal operator ( !== )
+assert.throws(block, [error], [message]);             // Expects block to throw an error. error can be constructor, RegExp or validation function.
+assert.doesNotThrow(block, [message]);                // Expects block not to throw an error, see assert.throws for details.
+assert.ifError(value);                                // Tests if value is not a false value, throws if it is a true value. Useful when testing the first argument, error in callbacks.
+
+
+/* *******************************************************************************************
+ * OS
+ * http://nodejs.org/api/os.html
+ * ******************************************************************************************* */
+
+
+// Provides a few basic operating-system related utility functions.
+// Use require('os') to access this module.
+
+os.tmpdir();             // Returns the operating system's default directory for temp files.
+os.endianness();         // Returns the endianness of the CPU. Possible values are "BE" or "LE".
+os.hostname();           // Returns the hostname of the operating system.
+os.type();               // Returns the operating system name.
+os.platform();           // Returns the operating system platform.
+os.arch();               // Returns the operating system CPU architecture.
+os.release();            // Returns the operating system release.
+os.uptime();             // Returns the system uptime in seconds.
+os.loadavg();            // Returns an array containing the 1, 5, and 15 minute load averages.
+os.totalmem();           // Returns the total amount of system memory in bytes.
+os.freemem();            // Returns the amount of free system memory in bytes.
+os.cpus();               // Returns an array of objects containing information about each CPU/core installed: model, speed (in MHz), and times (an object containing the number of milliseconds the CPU/core spent in: user, nice, sys, idle, and irq).
+os.networkInterfaces();  // Get a list of network interfaces.
+os.EOL;                  // A constant defining the appropriate End-of-line marker for the operating system.
+
+
+/* *******************************************************************************************
+ * BUFFER
+ * http://nodejs.org/api/buffer.html
+ * ******************************************************************************************* */
+
+
+// Buffer is used to dealing with binary data
+// Buffer is similar to an array of integers but corresponds to a raw memory allocation outside the V8 heap
+
+Buffer.from(size);                                                  // Allocates a new buffer of size octets.
+Buffer.from(array);                                                 // Allocates a new buffer using an array of octets.
+Buffer.from(str, [encoding]);                                       // Allocates a new buffer containing the given str. encoding defaults to 'utf8'.
+
+Buffer.isEncoding(encoding);                                        // Returns true if the encoding is a valid encoding argument, or false otherwise.
+Buffer.isBuffer(obj);                                               // Tests if obj is a Buffer
+Buffer.concat(list, [totalLength]);                                 // Returns a buffer which is the result of concatenating all the buffers in the list together.
+Buffer.byteLength(string, [encoding]);                              // Gives the actual byte length of a string.
+
+buf.write(string, [offset], [length], [encoding]);                  // Writes string to the buffer at offset using the given encoding
+buf.toString([encoding], [start], [end]);                           // Decodes and returns a string from buffer data encoded with encoding (defaults to 'utf8') beginning at start (defaults to 0) and ending at end (defaults to buffer.length).
+buf.toJSON();                                                       // Returns a JSON-representation of the Buffer instance, which is identical to the output for JSON Arrays
+buf.copy(targetBuffer, [targetStart], [sourceStart], [sourceEnd]);  // Does copy between buffers. The source and target regions can be overlapped
+buf.slice([start], [end]);                                          // Returns a new buffer which references the same memory as the old, but offset and cropped by the start (defaults to 0) and end (defaults to buffer.length) indexes. Negative indexes start from the end of the buffer.   
+buf.fill(value, [offset], [end]);                                   // Fills the buffer with the specified value
+buf[index];                                                         // Get and set the octet at index
+buf.length;                                                         // The size of the buffer in bytes, Note that this is not necessarily the size of the contents
+
+buffer.INSPECT_MAX_BYTES;                                           // How many bytes will be returned when buffer.inspect() is called. This can be overridden by user modules.
+

Node.js is an open-source and cross-platform JavaScript runtime environment. It is a popular tool for almost any kind of project!

Node.js runs the V8 JavaScript engine, the core of Google Chrome, outside of the browser. This allows Node.js to be very performant.

A Node.js app runs in a single process, without creating a new thread for every request. Node.js provides a set of asynchronous I/O primitives in its standard library that prevent JavaScript code from blocking and generally, libraries in Node.js are written using non-blocking paradigms, making blocking behavior the exception rather than the norm.

When Node.js performs an I/O operation, like reading from the network, accessing a database or the filesystem, instead of blocking the thread and wasting CPU cycles waiting, Node.js will resume the operations when the response comes back.

This allows Node.js to handle thousands of concurrent connections with a single server without introducing the burden of managing thread concurrency, which could be a significant source of bugs.

Node.js has a unique advantage because millions of frontend developers that write JavaScript for the browser are now able to write the server-side code in addition to the client-side code without the need to learn a completely different language.

In Node.js the new ECMAScript standards can be used without problems, as you don't have to wait for all your users to update their browsers - you are in charge of deciding which ECMAScript version to use by changing the Node.js version, and you can also enable specific experimental features by running Node.js with flags.

A Vast Number of Libraries

npm with its simple structure helped the ecosystem of Node.js proliferate, and now the npm registry hosts over 1,000,000 open source packages you can freely use.

An Example Node.js Application

The most common example Hello World of Node.js is a web server:

This code first includes the Node.js http module.

Node.js has a fantastic standard library, including first-class support for networking.

The createServer() method of http creates a new HTTP server and returns it.

The server is set to listen on the specified port and host name. When the server is ready, the callback function is called, in this case informing us that the server is running.

Whenever a new request is received, the request event is called, providing two objects: a request (an http.IncomingMessage object) and a response (an http.ServerResponse object).

Those 2 objects are essential to handle the HTTP call.

The first provides the request details. In this simple example, this is not used, but you could access the request headers and request data.

The second is used to return data to the caller.

In this case with:

JS

res.statusCode = 200

we set the statusCode property to 200, to indicate a successful response.

We set the Content-Type header:

JS

res.setHeader('Content-Type', 'text/plain')

and we close the response, adding the content as an argument to end():

JS

res.end('Hello World\n')

Node.js is a low-level platform. In order to make things easy and exciting for developers, thousands of libraries were built upon Node.js by the community.

Many of those established over time as popular options. Here is a non-comprehensive list of the ones worth learning:

  • AdonisJS: A TypeScript-based fully featured framework highly focused on developer ergonomics, stability, and confidence. Adonis is one of the fastest Node.js web frameworks.
  • Egg.js: A framework to build better enterprise frameworks and apps with Node.js & Koa.
  • Express: It provides one of the most simple yet powerful ways to create a web server. Its minimalist approach, unopinionated, focused on the core features of a server, is key to its success.
  • Fastify: A web framework highly focused on providing the best developer experience with the least overhead and a powerful plugin architecture. Fastify is one of the fastest Node.js web frameworks.
  • FeatherJS: Feathers is a lightweight web-framework for creating real-time applications and REST APIs using JavaScript or TypeScript. Build prototypes in minutes and production-ready apps in days.
  • Gatsby: A React-based, GraphQL powered, static site generator with a very rich ecosystem of plugins and starters.
  • hapi: A rich framework for building applications and services that enables developers to focus on writing reusable application logic instead of spending time building infrastructure.
  • koa: It is built by the same team behind Express, aims to be even simpler and smaller, building on top of years of knowledge. The new project born out of the need to create incompatible changes without disrupting the existing community.
  • Loopback.io: Makes it easy to build modern applications that require complex integrations.
  • Meteor: An incredibly powerful full-stack framework, powering you with an isomorphic approach to building apps with JavaScript, sharing code on the client and the server. Once an off-the-shelf tool that provided everything, now integrates with frontend libs React, Vue, and Angular. Can be used to create mobile apps as well.
  • Micro: It provides a very lightweight server to create asynchronous HTTP microservices.
  • NestJS: A TypeScript based progressive Node.js framework for building enterprise-grade efficient, reliable and scalable server-side applications.
  • Next.js: React framework that gives you the best developer experience with all the features you need for production: hybrid static & server rendering, TypeScript support, smart bundling, route pre-fetching, and more.
  • Nx: A toolkit for full-stack monorepo development using NestJS, Express, React, Angular, and more! Nx helps scale your development from one team building one application to many teams collaborating on multiple applications!
  • Sapper: Sapper is a framework for building web applications of all sizes, with a beautiful development experience and flexible filesystem-based routing. Offers SSR and more!
  • Socket.io: A real-time communication engine to build network applications.
  • Strapi: Strapi is a flexible, open-source Headless CMS that gives developers the freedom to choose their favorite tools and frameworks while also allowing editors to easily manage and distribute their content. By making the admin panel and API extensible through a plugin system, Strapi enables the world's largest companies to accelerate content delivery while building beautiful digital experiences.

Node.js can be installed in different ways. This post highlights the most common and convenient ones.

Official packages for all the major platforms are available at https://nodejs.dev/download/.

One very convenient way to install Node.js is through a package manager. In this case, every operating system has its own.

Other package managers for MacOS, Linux, and Windows are listed in https://nodejs.dev/download/package-manager/

nvm is a popular way to run Node.js. It allows you to easily switch the Node.js version, and install new versions to try and easily rollback if something breaks, for example.

It is also very useful to test your code with old Node.js versions.

See https://github.com/nvm-sh/nvm for more information about this option.

In any case, when Node.js is installed you'll have access to the node executable program in the command line.

How much JavaScript do you need to know to use Node.js

As a beginner, it's hard to get to a point where you are confident enough in your programming abilities.

While learning to code, you might also be confused at where does JavaScript end, and where Node.js begins, and vice versa.

I would recommend you to have a good grasp of the main JavaScript concepts before diving into Node.js:

  • Lexical Structure
  • Expressions
  • Types
  • Classes
  • Variables
  • Functions
  • this
  • Arrow Functions
  • Loops
  • Scopes
  • Arrays
  • Template Literals
  • Semicolons
  • Strict Mode
  • ECMAScript 6, 2016, 2017

With those concepts in mind, you are well on your road to become a proficient JavaScript developer, in both the browser and in Node.js.

The following concepts are also key to understand asynchronous programming, which is one of the fundamental parts of Node.js:

Differences between Node.js and the Browser

Both the browser and Node.js use JavaScript as their programming language.

Building apps that run in the browser is a completely different thing than building a Node.js application.

Despite the fact that it's always JavaScript, there are some key differences that make the experience radically different.

From the perspective of a frontend developer who extensively uses JavaScript, Node.js apps bring with them a huge advantage: the comfort of programming everything - the frontend and the backend - in a single language.

You have a huge opportunity because we know how hard it is to fully, deeply learn a programming language, and by using the same language to perform all your work on the web - both on the client and on the server, you're in a unique position of advantage.

What changes is the ecosystem.

In the browser, most of the time what you are doing is interacting with the DOM, or other Web Platform APIs like Cookies. Those do not exist in Node.js, of course. You don't have the document, window and all the other objects that are provided by the browser.

And in the browser, we don't have all the nice APIs that Node.js provides through its modules, like the filesystem access functionality.

Another big difference is that in Node.js you control the environment. Unless you are building an open source application that anyone can deploy anywhere, you know which version of Node.js you will run the application on. Compared to the browser environment, where you don't get the luxury to choose what browser your visitors will use, this is very convenient.

This means that you can write all the modern ES6-7-8-9 JavaScript that your Node.js version supports.

Since JavaScript moves so fast, but browsers can be a bit slow to upgrade, sometimes on the web you are stuck with using older JavaScript / ECMAScript releases.

You can use Babel to transform your code to be ES5-compatible before shipping it to the browser, but in Node.js, you won't need that.

Another difference is that Node.js uses the CommonJS module system, while in the browser we are starting to see the ES Modules standard being implemented.

In practice, this means that for the time being you use require() in Node.js and import in the browser.

The V8 JavaScript Engine

V8 is the name of the JavaScript engine that powers Google Chrome. It's the thing that takes our JavaScript and executes it while browsing with Chrome.

V8 provides the runtime environment in which JavaScript executes. The DOM, and the other Web Platform APIs are provided by the browser.

The cool thing is that the JavaScript engine is independent of the browser in which it's hosted. This key feature enabled the rise of Node.js. V8 was chosen to be the engine that powered Node.js back in 2009, and as the popularity of Node.js exploded, V8 became the engine that now powers an incredible amount of server-side code written in JavaScript.

The Node.js ecosystem is huge and thanks to V8 which also powers desktop apps, with projects like Electron.

Other JS engines

Other browsers have their own JavaScript engine:

and many others exist as well.

All those engines implement the ECMA ES-262 standard, also called ECMAScript, the standard used by JavaScript.

The quest for performance

V8 is written in C++, and it's continuously improved. It is portable and runs on Mac, Windows, Linux and several other systems.

In this V8 introduction, we will ignore the implementation details of V8: they can be found on more authoritative sites (e.g. the V8 official site), and they change over time, often radically.

V8 is always evolving, just like the other JavaScript engines around, to speed up the Web and the Node.js ecosystem.

On the web, there is a race for performance that's been going on for years, and we (as users and developers) benefit a lot from this competition because we get faster and more optimized machines year after year.

Compilation

JavaScript is generally considered an interpreted language, but modern JavaScript engines no longer just interpret JavaScript, they compile it.

This has been happening since 2009, when the SpiderMonkey JavaScript compiler was added to Firefox 3.5, and everyone followed this idea.

JavaScript is internally compiled by V8 with just-in-time (JIT) compilation to speed up the execution.

This might seem counter-intuitive, but since the introduction of Google Maps in 2004, JavaScript has evolved from a language that was generally executing a few dozens of lines of code to complete applications with thousands to hundreds of thousands of lines running in the browser.

Our applications can now run for hours inside a browser, rather than being just a few form validation rules or simple scripts.

In this new world, compiling JavaScript makes perfect sense because while it might take a little bit more to have the JavaScript ready, once done it's going to be much more performant than purely interpreted code.

Run Node.js scripts from the command line

The usual way to run a Node.js program is to run the node globally available command (once you install Node.js) and pass the name of the file you want to execute.

If your main Node.js application file is app.js, you can call it by typing:

BASH

node app.js

Above, you are explicitly telling the shell to run your script with node. You can also embed this information into your JavaScript file with a "shebang" line. The "shebang" is the first line in the file, and tells the OS which interpreter to use for running the script. Below is the first line of JavaScript:

BASH

!/usr/bin/node

Above, we are explicitly giving the absolute path of interpreter. Not all operating systems have node in the bin folder, but all should have env. You can tell the OS to run env with node as parameter:

BASH

!/usr/bin/env node

// your code

To use a shebang, your file should have executable permission. You can give app.js the executable permission by running:

BASH

chmod u+x app.js

While running the command, make sure you are in the same directory which contains the app.js file.

How to exit from a Node.js program


created: 2021-11-27T11:28:53 (UTC -05:00) +tags: [] +source: https://nodejs.dev/learn +author:


How to exit from a Node.js program

Excerpt

Learn how to terminate a Node.js app in the best possible way


There are various ways to terminate a Node.js application.

When running a program in the console you can close it with ctrl-C, but what we want to discuss here is programmatically exiting.

Let's start with the most drastic one, and see why you're better off not using it.

The process core module provides a handy method that allows you to programmatically exit from a Node.js program: process.exit().

When Node.js runs this line, the process is immediately forced to terminate.

This means that any callback that's pending, any network request still being sent, any filesystem access, or processes writing to stdout or stderr - all is going to be ungracefully terminated right away.

If this is fine for you, you can pass an integer that signals the operating system the exit code:

JS

process.exit(1)

By default the exit code is 0, which means success. Different exit codes have different meaning, which you might want to use in your own system to have the program communicate to other programs.

You can read more on exit codes at https://nodejs.org/api/process.html#process_exit_codes

You can also set the process.exitCode property:

JS

process.exitCode = 1

and when the program ends, Node.js will return that exit code.

A program will gracefully exit when all the processing is done.

Many times with Node.js we start servers, like this HTTP server:

JS

const express = require('express')

const app = express()

app.get('/', (req, res) => {

res.send('Hi!')

})

app.listen(3000, () => console.log('Server ready'))

Express is a framework that uses the http module under the hood, app.listen() returns an instance of http. You would use https.createServer if you needed to serve your app using HTTPS, as app.listen only uses the http module.

This program is never going to end. If you call process.exit(), any currently pending or running request is going to be aborted. This is not nice.

In this case you need to send the command a SIGTERM signal, and handle that with the process signal handler:

Note: process does not require a "require", it's automatically available.

JS

const express = require('express')

const app = express()

app.get('/', (req, res) => {

res.send('Hi!')

})

const server = app.listen(3000, () => console.log('Server ready'))

process.on('SIGTERM', () => {

server.close(() => {

console.log('Process terminated')

})

})

What are signals? Signals are a POSIX intercommunication system: a notification sent to a process in order to notify it of an event that occurred.

SIGKILL is the signal that tells a process to immediately terminate, and would ideally act like process.exit().

SIGTERM is the signal that tells a process to gracefully terminate. It is the signal that's sent from process managers like upstart or supervisord and many others.

You can send this signal from inside the program, in another function:

JS

process.kill(process.pid, 'SIGTERM')

Or from another Node.js running program, or any other app running in your system that knows the PID of the process you want to terminate.

+ + + + + +View My +Stats + + diff --git a/docs/content/html-docs/docs/python/index.html b/docs/content/html-docs/docs/python/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/docs/python/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/docs/regex-in-js/index.html b/docs/content/html-docs/docs/regex-in-js/index.html new file mode 100644 index 0000000000..0ea8687372 --- /dev/null +++ b/docs/content/html-docs/docs/regex-in-js/index.html @@ -0,0 +1,588 @@ + +Regular Expressions | webdevhub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Regular Expressions

Regular Expressions

description:


CODEX

Regular Expressions

### description: +

Regular expressions are patterns used to match character combinations in strings. In JavaScript, regular expressions are also objects. These patterns are used with the exec() and test() methods of RegExp, and with the match(), matchAll(), replace(), replaceAll(), search(), and split() methods of String. This chapter describes JavaScript regular expressions.

Creating a regular expression

You construct a regular expression in one of two ways:

  1. Using a regular expression literal, which consists of a pattern enclosed between slashes, as follows:

let re = /ab+c/;

  • Regular expression literals provide compilation of the regular expression when the script is loaded. If the regular expression remains constant, using this can improve performance.

2. Or calling the constructor function of the RegExp object, as follows:

  • let re = new RegExp('ab+c');

Using the constructor function provides runtime compilation of the regular expression. Use the constructor function when you know the regular expression pattern will be changing, or you don't know the pattern and are getting it from another source, such as user input.

Writing a regular expression pattern

A regular expression pattern is composed of simple characters, such as /abc/, or a combination of simple and special characters, such as /ab*c/ or /Chapter (\d+)\.\d*/.

The last example includes parentheses, which are used as a memory device. The match made with this part of the pattern is remembered for later use.

Using simple patterns

Simple patterns are constructed of characters for which you want to find a direct match.

For example, the pattern /abc/ matches character combinations in strings only when the exact sequence "abc" occurs (all characters together and in that order).

Such a match would succeed in the strings "Hi, do you know your abc's?" and "The latest airplane designs evolved from slabcraft."

In both cases the match is with the substring "abc".

There is no match in the string "Grab crab" because while it contains the substring "ab c", it does not contain the exact substring "abc".

Using special characters

When the search for a match requires something more than a direct match, such as finding one or more b's, or finding white space, you can include special characters in the pattern.

For example, to match a single "a" followed by zero or more "b"s followed by "c", you'd use the pattern /ab*c/:

the * after "b" means "0 or more occurrences of the preceding item." In the string "cbbabbbbcdebc", this pattern will match the substring "abbbbc".

Assertions** : Assertions include boundaries, which indicate the beginnings and endings of lines and words, and other patterns indicating in some way that a match is possible (including look-ahead, look-behind, and conditional expressions).**

Character classes** : Distinguish different types of characters. For example, distinguishing between letters and digits.**

Groups and ranges** : Indicate groups and ranges of expression characters.**

Quantifiers** : Indicate numbers of characters or expressions to match.**

Unicode property escapes** : Distinguish based on unicode character properties, for example, upper- and lower-case letters, math symbols, and punctuation.**

If you want to look at all the special characters that can be used in regular expressions in a single table, see the following:

### Special characters in regular expressions. +

Escaping

If you need to use any of the special characters literally (actually searching for a "*", for instance), you must escape it by putting a backslash in front of it.

For instance, to search for "a" followed by "*" followed by "b",

you'd use /a\*b/ --- the backslash "escapes" the "*", making it literal instead of special.

Similarly, if you're writing a regular expression literal and need to match a slash ("/"), you need to escape that (otherwise, it terminates the pattern)

For instance, to search for the string "/example/" followed by one or more alphabetic characters, you'd use /\/example\/[a-z]+/i

--the backslashes before each slash make them literal.

To match a literal backslash, you need to escape the backslash.

For instance, to match the string "C:\" where "C" can be any letter,

you'd use /[A-Z]:\\/

--- the first backslash escapes the one after it, so the expression searches for a single literal backslash.

If using the RegExp constructor with a string literal, remember that the backslash is an escape in string literals, so to use it in the regular expression, you need to escape it at the string literal level.

/a\*b/ and new RegExp("a\\*b") create the same expression,

which searches for "a" followed by a literal "*" followed by "b".

If escape strings are not already part of your pattern you can add them using String.replace:

function escapeRegExp(string) {
+  return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
+}
+

The "g" after the regular expression is an option or flag that performs a global search, looking in the whole string and returning all matches.

Using parentheses

Parentheses around any part of the regular expression pattern causes that part of the matched substring to be remembered.

Once remembered, the substring can be recalled for other use.

Using regular expressions in JavaScript

Regular expressions are used with the RegExp methods

test() and exec()

and with the String methods match(), replace(), search(), and split().


Method Descriptions

exec()

Executes a search for a match in a string.

It returns an array of information or null on a mismatch.

test()

Tests for a match in a string.

It returns true or false.

match()

Returns an array containing all of the matches, including capturing groups, or null if no match is found.

matchAll()

Returns an iterator containing all of the matches, including capturing groups.

search()

Tests for a match in a string.

It returns the index of the match, or -1 if the search fails.

replace()

Executes a search for a match in a string, and replaces the matched substring with a replacement substring.

replaceAll()

Executes a search for all matches in a string, and replaces the matched substrings with a replacement substring.

split()

Uses a regular expression or a fixed string to break a string into an array of substrings.

Methods that use regular expressions

When you want to know whether a pattern is found in a string, use the test() or search() methods;

for more information (but slower execution) use the exec() or match() methods.

If you use exec() or match() and if the match succeeds, these methods return an array and update properties of the associated regular expression object and also of the predefined regular expression object, RegExp.

If the match fails, the exec() method returns null (which coerces to false).

In the following example, the script uses the exec() method to find a match in a string.

let myRe = /d(b+)d/g;
+let myArray = myRe.exec('cdbbdbsbz');
+

If you do not need to access the properties of the regular expression, an alternative way of creating myArray is with this script:

let myArray = /d(b+)d/g.exec('cdbbdbsbz');
+    // similar to "cdbbdbsbz".match(/d(b+)d/g); however,
+    // "cdbbdbsbz".match(/d(b+)d/g) outputs Array [ "dbbd" ], while
+    // /d(b+)d/g.exec('cdbbdbsbz') outputs Array [ 'dbbd', 'bb', index: 1, input: 'cdbbdbsbz' ].
+

(See different behaviors for further info about the different behaviors.)

If you want to construct the regular expression from a string, yet another alternative is this script:

let myRe = new RegExp('d(b+)d', 'g');
+let myArray = myRe.exec('cdbbdbsbz');
+

With these scripts, the match succeeds and returns the array and updates the properties shown in the following table.

Results of regular expression execution.

You can use a regular expression created with an object initializer without assigning it to a letiable.

If you do, however, every occurrence is a new regular expression.

For this reason, if you use this form without assigning it to a letiable, you cannot subsequently access the properties of that regular expression.

For example, assume you have this script:

let myRe = /d(b+)d/g;
+let myArray = myRe.exec('cdbbdbsbz');
+console.log('The value of lastIndex is ' + myRe.lastIndex);
+
+// "The value of lastIndex is 5"
+

However, if you have this script:

let myArray = /d(b+)d/g.exec('cdbbdbsbz');
+console.log('The value of lastIndex is ' + /d(b+)d/g.lastIndex);
+
+// "The value of lastIndex is 0"
+

The occurrences of /d(b+)d/g in the two statements are different regular expression objects and hence have different values for their lastIndex property.

If you need to access the properties of a regular expression created with an object initializer, you should first assign it to a variable.

[Advanced searching with flags]

Regular expressions have six optional flags that allow for functionality like global and case insensitive searching.

These flags can be used separately or together in any order, and are included as part of the regular expression.

Flag Description Corresponding property


g Global search. RegExp.prototype.global

i Case-insensitive search. RegExp.prototype.ignoreCase

m Multi-line search. RegExp.prototype.multiline

s Allows . to match newline characters. RegExp.prototype.dotAll

u "unicode"; treat a pattern as a sequence of unicode code points. RegExp.prototype.unicode

y Perform a "sticky" search that matches starting at the current position in the target string. RegExp.prototype.sticky

Regular expression flags

To include a flag with the regular expression, use this syntax:

let re = /pattern/flags;
+

or

let re = new RegExp('pattern', 'flags');
+

Note that the flags are an integral part of a regular expression. They cannot be added or removed later.

For example, re = /\w+\s/g creates a regular expression that looks for one or more characters followed by a space, and it looks for this combination throughout the string.

let re = /\w+\s/g;
+let str = 'fee fi fo fum';
+let myArray = str.match(re);
+console.log(myArray);
+
+// ["fee ", "fi ", "fo "]
+

You could replace the line:

let re = /\w+\s/g;
+

with:

let re = new RegExp('\\w+\\s', 'g');
+

and get the same result.

The behavior associated with the g flag is different when the .exec() method is used.

The roles of "class" and "argument" get reversed:

In the case of .match(), the string class (or data type) owns the method and the regular expression is just an argument,

while in the case of .exec(), it is the regular expression that owns the method, with the string being the argument.

Contrast this str.match(re) versus re.exec(str).

The g flag is used with the .exec() method to get iterative progression.

let xArray; while(xArray = re.exec(str)) console.log(xArray);
+// produces:
+// ["fee ", index: 0, input: "fee fi fo fum"]
+// ["fi ", index: 4, input: "fee fi fo fum"]
+// ["fo ", index: 7, input: "fee fi fo fum"]
+

The m flag is used to specify that a multiline input string should be treated as multiple lines.

If the m flag is used, ^ and $ match at the start or end of any line within the input string instead of the start or end of the entire string.

Using special characters to verify input

In the following example, the user is expected to enter a phone number. When the user presses the "Check" button, the script checks the validity of the number. If the number is valid (matches the character sequence specified by the regular expression), the script shows a message thanking the user and confirming the number. If the number is invalid, the script informs the user that the phone number is not valid.

Within non-capturing parentheses (?: , the regular expression looks for three numeric characters \d{3} OR | a left parenthesis \( followed by three digits\d{3}, followed by a close parenthesis \), (end non-capturing parenthesis )), followed by one dash, forward slash, or decimal point and when found, remember the character ([-\/\.]), followed by three digits \d{3}, followed by the remembered match of a dash, forward slash, or decimal point \1, followed by four digits \d{4}.

The Change event activated when the user presses Enter sets the value of RegExp.input.

HTML

<p>
+  Enter your phone number (with area code) and then click "Check".
+  <br>
+  The expected format is like ###-###-####.
+</p>
+<form action="#">
+  <input id="phone">
+    <button onclick="testInfo(document.getElementById('phone'));">Check</button>
+</form>
+

JavaScript

let re = /(?:\d{3}|\(\d{3}\))([-\/\.])\d{3}\1\d{4}/;
+function testInfo(phoneInput) {
+  let OK = re.exec(phoneInput.value);
+  if (!OK) {
+    console.error(phoneInput.value + ' isn\'t a phone number with area code!');
+  } else {
+    console.log('Thanks, your phone number is ' + OK[0]);}
+}
+

Cheat Sheet

#### If you found this guide helpful feel free to checkout my GitHub/gist's where I host similar content: +

bgoonz's gists · GitHub

bgoonz — Overview
Web Developer, Electrical Engineer JavaScript | CSS | Bootstrap | Python | React | Node.js | Express | Sequelize…github.com

Or Checkout my personal Resource Site:

a/A-Student-Resources
Edit descriptiongoofy-euclid-1cd736.netlify.app

By Bryan Guner on March 6, 2021.

Canonical link

Exported from Medium on August 17, 2021.

+ + + + + +View My +Stats + + diff --git a/docs/content/html-docs/docs/sitemap/index.html b/docs/content/html-docs/docs/sitemap/index.html new file mode 100644 index 0000000000..9be1ac7597 --- /dev/null +++ b/docs/content/html-docs/docs/sitemap/index.html @@ -0,0 +1,533 @@ + +Sitemap + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Sitemap

204. 🖥️privacy-policy🌍

205. 🖥️readme🌍

206. 🏆showcase🏆

207. 🏠Home🏠



0. 🖥️blog/300-react-questions🌍

1. 🖥️blog/awesome-graphql🌍

2. 🖥️blog/big-o-complexity🌍

3. 🖥️blog/blog-archive🌍

4. 🖥️blog/blogwcomments🌍

5. 🖥️blog/data-structures🌍

6. 🖥️blog/flow-control-in-python🌍

7. 🖥️blog/functions-in-python🌍

8. 🖥️blog/git-gateway🌍

9. 🖥️blog/interview-questions-js🌍

10. 🖥️blog/netlify-cms🌍

11. 🖥️blog/platform-docs🌍

12. 🖥️blog/python-for-js-dev🌍

13. 🖥️blog/python-resources🌍

14. 🖥️blog/web-dev-trends🌍

15. 🖥️blog/web-scraping🌍

16. 🖥️blog🌍



DOCS:

17. 📖docs/about/README🌍

18. 📖docs/about/eng-portfolio🌍

19. 📖docs/about/intrests🌍

20. 📖docs/about/job-search🌍

21. 📖docs/about/resume🌍

22. 📖docs/about🌍

23. 📖docs/articles/basic-web-dev🌍

24. 📖docs/articles/buffers🌍

25. 📖docs/articles/dev-dep🌍

26. 📖docs/articles/event-loop🌍

27. 📖docs/articles/fs-module🌍

28. 📖docs/articles/how-search-engines-work🌍

29. 📖docs/articles/how-the-web-works🌍

30. 📖docs/articles/http🌍

31. 📖docs/articles/install🌍

32. 📖docs/articles/intro🌍

33. 📖docs/articles/modules🌍

34. 📖docs/articles/nextjs🌍

35. 📖docs/articles/node-api-express🌍

36. 📖docs/articles/node-cli-args🌍

37. 📖docs/articles/node-common-modules🌍

38. 📖docs/articles/node-env-variables🌍

39. 📖docs/articles/node-js-language🌍

40. 📖docs/articles/node-package-manager🌍

41. 📖docs/articles/node-repl🌍

42. 📖docs/articles/node-run-cli🌍

43. 📖docs/articles/nodejs🌍

44. 📖docs/articles/nodevsbrowser🌍

45. 📖docs/articles/npm🌍

46. 📖docs/articles/npx🌍

47. 📖docs/articles/os-module🌍

48. 📖docs/articles/reading-files🌍

49. 📖docs/articles/semantic-html🌍

50. 📖docs/articles/semantic🌍

51. 📖docs/articles/the-uniform-resource-locator-(url)🌍

52. 📖docs/articles/understanding-firebase🌍

53. 📖docs/articles/v8🌍

54. 📖docs/articles/web-standards-checklist🌍

55. 📖docs/articles/webdev-tools🌍

56. 📖docs/articles/writing-files🌍

57. 📖docs/articles🌍

58. 📖docs/audio/audio-feature-extraction🌍

59. 📖docs/audio/audio🌍

60. 📖docs/audio/dfft🌍

61. 📖docs/audio/discrete-fft🌍

62. 📖docs/audio/dtw-python-explained🌍

63. 📖docs/audio/dynamic-time-warping🌍

64. 📖docs/audio/web-audio-api🌍

65. 📖docs/audio🌍

66. 📖docs/career/dev-interview🌍

67. 📖docs/career/interview-dos-n-donts🌍

68. 📖docs/career/job-boards🌍

69. 📖docs/career🌍

71. 📖docs/community/bookmarks🌍

72. 📖docs/community/video-chat🌍

73. 📖docs/community🌍

74. 📖docs/content/algo🌍

75. 📖docs/content/archive🌍

76. 📖docs/content/gatsby-Queries-Mutations🌍

77. 📖docs/content/history-api🌍

78. 📖docs/content/main-projects🌍

79. 📖docs/content/trouble-shooting🌍

80. 📖docs/content🌍

81. 📖docs/data-structures🌍

82. 📖docs/docs/appendix🌍

83. 📖docs/docs/art-of-command-line🌍

84. 📖docs/docs/bash🌍

85. 📖docs/docs/content🌍

86. 📖docs/docs/css🌍

87. 📖docs/docs/data-structures-docs🌍

88. 📖docs/docs/es-6-features🌍

89. 📖docs/docs/git-reference🌍

90. 📖docs/docs/git-repos🌍

91. 📖docs/docs/html-spec🌍

92. 📖docs/docs/markdown🌍

93. 📖docs/docs/no-whiteboarding🌍

94. 📖docs/docs/node-docs-complete🌍

95. 📖docs/docs/node-docs-full🌍

96. 📖docs/docs/regex-in-js🌍

97. 📖docs/docs/sitemap🌍

98. 📖docs/docs🌍

99. 📖docs/faq/contact🌍

100. 📖docs/faq/plug-ins🌍

101. 📖docs/faq🌍

102. 📖docs/gists🌍

103. 📖docs/interact/callstack-visual🌍

104. 📖docs/interact/clock🌍

105. 📖docs/interact/jupyter-notebooks🌍

106. 📖docs/interact/other-sites🌍

107. 📖docs/interact/video-chat🌍

108. 📖docs/interact🌍

109. 📖docs/interview/job-search-nav🌍

110. 📖docs/interview/review-concepts🌍

111. 📖docs/interview🌍

112. 📖docs/javascript/arrow-functions🌍

113. 📖docs/javascript/asyncjs🌍

114. 📖docs/javascript/await-keyword🌍

115. 📖docs/javascript/bigo🌍

116. 📖docs/javascript/clean-code🌍

117. 📖docs/javascript/constructor-functions🌍

118. 📖docs/javascript/for-loops🌍

119. 📖docs/javascript/promises🌍

120. 📖docs/javascript/review🌍

121. 📖docs/javascript/this-is-about-this🌍

122. 📖docs/javascript🌍

123. 📖docs/leetcode🌍

124. 📖docs/privacy-policy🌍

125. 📖docs/projects/embeded-websites🌍

126. 📖docs/projects/list-of-projects🌍

127. 📖docs/projects/mini-projects2🌍

128. 📖docs/projects/mini-projects🌍

129. 📖docs/projects/my-websites🌍

130. 📖docs/projects🌍

131. 📖docs/python/at-length🌍

132. 📖docs/python/cheat-sheet🌍

133. 📖docs/python/comprehensive-guide🌍

134. 📖docs/python/examples🌍

135. 📖docs/python/flow-control🌍

136. 📖docs/python/functions🌍

137. 📖docs/python/google-sheets-api🌍

138. 📖docs/python/intro-for-js-devs🌍

139. 📖docs/python/python-ds🌍

140. 📖docs/python/snippets🌍

141. 📖docs/python🌍

142. 📖docs/quick-reference/Emmet🌍

143. 📖docs/quick-reference/all-emojis🌍

144. 📖docs/quick-reference/create-react-app🌍

145. 📖docs/quick-reference/git-bash🌍

146. 📖docs/quick-reference/git-tricks🌍

147. 📖docs/quick-reference/google-firebase🌍

148. 📖docs/quick-reference/heroku-error-codes🌍

149. 📖docs/quick-reference/installation🌍

150. 📖docs/quick-reference/markdown-dropdowns🌍

151. 📖docs/quick-reference/minifiction🌍

152. 📖docs/quick-reference/new-repo-instructions🌍

153. 📖docs/quick-reference/psql-setup🌍

154. 📖docs/quick-reference/pull-request-rubric🌍

155. 📖docs/quick-reference/quick-links🌍

156. 📖docs/quick-reference/topRepos🌍

157. 📖docs/quick-reference/understanding-path🌍

158. 📖docs/quick-reference/vscode-themes🌍

159. 📖docs/quick-reference🌍

160. 📖docs/react/ajax-n-apis🌍

161. 📖docs/react/cheatsheet🌍

162. 📖docs/react/createReactApp🌍

163. 📖docs/react/demo🌍

164. 📖docs/react/dont-use-index-as-keys🌍

165. 📖docs/react/jsx🌍

166. 📖docs/react/react-docs🌍

167. 📖docs/react/react-in-depth🌍

168. 📖docs/react/react2🌍

169. 📖docs/react/render-elements🌍

170. 📖docs/react🌍

171. 📖docs/reference/awesome-lists🌍

172. 📖docs/reference/awesome-nodejs🌍

173. 📖docs/reference/awesome-static🌍

174. 📖docs/reference/bash-commands🌍

175. 📖docs/reference/bookmarks🌍

176. 📖docs/reference/embed-the-web🌍

177. 📖docs/reference/github-search🌍

178. 📖docs/reference/google-cloud🌍

179. 📖docs/reference/how-2-reinstall-npm🌍

180. 📖docs/reference/how-to-kill-a-process🌍

181. 📖docs/reference/installing-node🌍

182. 📖docs/reference/intro-to-nodejs🌍

183. 📖docs/reference/markdown-styleguide🌍

184. 📖docs/reference/notes-template🌍

185. 📖docs/reference/psql🌍

186. 📖docs/reference/resources🌍

187. 📖docs/reference/vscode🌍

188. 📖docs/reference/web-api's🌍

189. 📖docs/reference🌍

190. 📖docs/search🌍

191. 📖docs/sitemap🌍

192. 📖docs/tips/array-methods🌍

193. 📖docs/tips/insert-into-array🌍

194. 📖docs/tips/sorting-strings🌍

195. 📖docs/tips🌍

196. 📖docs/tools/Archive🌍

197. 📖docs/tools/data-structures🌍

198. 📖docs/tools/dev-utilities🌍

199. 📖docs/tools/markdown-html🌍

200. 📖docs/tools🌍

201. 📖docs/tutorials/enviorment-setup🌍

202. 📖docs/tutorials🌍

203. 🖥️docs🌍

+ + + + + +View My +Stats + + diff --git a/docs/content/html-docs/docs/snippets/index.html b/docs/content/html-docs/docs/snippets/index.html new file mode 100644 index 0000000000..1867eee09a --- /dev/null +++ b/docs/content/html-docs/docs/snippets/index.html @@ -0,0 +1,1043 @@ + +Useful Snippets | webdevhub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Useful Snippets

A list of all of my articles to link to future posts

Scope, Closures & Context In JavaScript
"JavaScript's global scope is like a public toilet. You can't avoid going in there, but try to limit your contact with...bryanguner.medium.com

Fundamental Javascript Concepts You Should Understand
Plain Old JS Object Lesson Conceptsbryanguner.medium.com

Mutability And Reference VS Privative Types in JavaScript
Mutability && Primitive && Reference Examplesbryanguner.medium.com

Array Callback Methods Implemented With For Loops
How to implement array callback methods in JavaScriptjavascript.plainenglish.io

JavaScript in Plain English
New JavaScript and Web Development articles every day.javascript.plainenglish.io

Beginner's Guide To React Part 2
As I learn to build web applications in React I will blog about it in this series in an attempt to capture the...bryanguner.medium.com

A Very Quick Guide To Calculating Big O Computational Complexity
Big O: big picture, broad strokes, not detailsbryanguner.medium.com

Introduction to React for Complete Beginners
All of the code examples below will be included a second time at the bottom of this article as an embedded gist.javascript.plainenglish.io

Scheduling: setTimeout and setInterval
We may decide to execute a function not right now, but at a later time. That's called "scheduling a call".javascript.plainenglish.io

LocalStorage VS SessionStorage
Web storage objects localStorage and sessionStorage allow to save key/value pairs in the browser.bryanguner.medium.com

These Are The Bash Shell Commands That Stand Between Me And Insanity.
I will not profess to be a bash shell wizard... but I have managed to scour some pretty helpful little scripts from Stack...bryanguner.medium.com

How To Implement Native(ES6) Data Structures Using Arrays & Objects
Smart data structures and dumb code works better than the other way around -"Eric S. Raymond"bryanguner.medium.com

Objects in Javascript
Codepen with examples for you to practice with below!medium.com

The Beginner's Guide To JavaScript
Part 1javascript.plainenglish.io

Web Developer Resource List Part 4
A all encompassing list of tools and resources for web developersmedium.com

Star Gazers
The place that enthusiastic and stargazer to everything.medium.com

VSCode Extensions Specifically for JavaScript Development
VSCode Extensions that are indispensable in JavaScript developmentmedium.com

Fundamental Data Structures in JavaScript
A simple to follow guide to Lists Stacks and Queues, with animated gifs, diagrams, and code examples!javascript.plainenglish.io

Web Development Resources Part 3
I'm the psychological equivalent of a physical hoarder only instead of empty soda cans and dead racoons it's lists of...bryanguner.medium.com

Web Development Interview Part 3💻
This installment is going to be the least technically demanding thus far however these questions are a more realistic...medium.com

The Best Cloud-Based Code Playgrounds of 2021 (Part 1)
A plethora of front-end code playgrounds have appeared over the years. They offer a convenient way to experiment with...bryanguner.medium.com

Front End Interview Questions Part 2
These will focus more on vocabulary and concepts than the application driven approach in my last post!medium.com

Web Developer Resource List Part 2
Because I compile these things compulsively anyway...medium.com

HTTP Basics
"If you want to build a ship, don't drum up the men and women to gather wood, divide the work, and give orders...levelup.gitconnected.com

JavaScript Frameworks & Libraries
My Awesome JavaScript List Part 2javascript.plainenglish.io

My 'awesome' list of JavaScript resources
Everyone's seen the 'Awesome' lists on GitHub... and they are indeed awesome... so today I am going to attempt to curate my...javascript.plainenglish.io

Everything You Need to Get Started With VSCode + Extensions & Resources
Commands:levelup.gitconnected.com

My Favorite VSCode Themes
+Themeslevelup.gitconnected.com

Object Oriented Programming in JavaScript
Object-Oriented Programminglevelup.gitconnected.com

JavaScript Rotate (Array) ProblemWalkthrough
Explanation for Rotate Rightmedium.com

Bryan Guner - Medium
A plethora of front-end code playgrounds have appeared over the years. They offer a convenient way to experiment with...bryanguner.medium.com

Postgresql Cheat Sheet
PostgreSQL commandsmedium.com

Everything You Need to Get Started With VSCode + Extensions & Resources
Commands:levelup.gitconnected.com

Super Simple Intro To HTML
What is HTML, CSS & JS and why do we need all three?levelup.gitconnected.com

Understanding Git (A Beginners Guide Containing Cheat Sheets & Resources)
Basic Git Work Flow.levelup.gitconnected.com

Git-Tricks
Refsbryanguner.medium.com

A Quick Guide to Big-O Notation, Memoization, Tabulation, and Sorting Algorithms by Example
Curating Complexity: A Guide to Big-O Notationmedium.com

Python Study Guide for a JavaScript Programmer
A guide to commands in Python from what you know in JavaScriptlevelup.gitconnected.com

Lists Stacks and Queue's In JavaScript
A simple to follow guide to these fundamental data structures with animated gifs, diagrams, and code examples!bryanguner.medium.com

Web Development Resources Part 3
I'm the psychological equivalent of a physical hoarder only instead of empty soda cans and dead racoons it's lists of...bryanguner.medium.com

My 'awesome' list of JavaScript resources
Everyone's seen the 'Awesome' lists on GitHub... and they are indeed awesome... so today I am going to attempt to curate my...javascript.plainenglish.io

Web Developer Resource List Part 2
Because I compile these things compulsively anyway...medium.com

Web Development Interview Part 3💻
This installment is going to be the least technically demanding thus far however these questions are a more realistic...bryanguner.medium.com

The Web Developer's Technical Interview
Questions....Answers... and links to the missing pieces.medium.com

Front End Interview Questions Part 2
These will focus more on vocabulary and concepts than the application driven approach in my last post!medium.com

Running List Of Interesting Articles, Tools and Ideas... as I Explore Them
Translation if you read this today (3/21/2021) it will be exceedingly short... because it's just gonna accumulate entries...bryanguner.medium.com

The Best Cloud-Based Code Playgrounds of 2021 (Part 1)
A plethora of front-end code playgrounds have appeared over the years. They offer a convenient way to experiment with...bryanguner.medium.com

Fundamental Data Structures In JavaScript
Data structures in JavaScriptmedium.com

HTTP Basics
"If you want to build a ship, don't drum up the men and women to gather wood, divide the work, and give orders...levelup.gitconnected.com

JavaScript Frameworks & Libraries
My Awesome JavaScript List Part 2javascript.plainenglish.io

bgoonz/Cumulative-Resource-List
List of useful cheatsheets Inspired by @sindresorhus awesome and improved by these amazing contributors . If you see a...jackfan.us.kg

My Favorite VSCode Themes
+Themeslevelup.gitconnected.com

JavaScript Rotate (Array) ProblemWalkthrough
Explanation for Rotate Rightmedium.com

Everything You Need To Know About Relational Databases, SQL, PostgreSQL and Sequelize To Build...
For Front end developers who like myself struggle with making the jump to fullstack.bryanguner.medium.com

The Complete JavaScript Reference Guide
You will want to bookmark thisjavascript.plainenglish.io

Modules in Javascript
Differences between Node.js and browsersmedium.com

An Introduction to Markdown (Bonus Markdown Templates Included)
Basic Syntax Guidelevelup.gitconnected.com

Web Dev Resources
Web Developmentlevelup.gitconnected.com

Regular Expressions
description:medium.com

A Collection of my most useful Gist Entries
This list is in no particular order!bryanguner.medium.com

VsCode Extension Readme Compilation
Markdown PDFbryanguner.medium.com

Learn CSS So That Your Site Doesn't Look Like Garbage
CSS Selectorsjavascript.plainenglish.io

PostgreSQL Setup For Windows & WSL/Ubuntu
If you follow this guide to a tee... you will install PostgreSQL itself on your Windows installation. Then, you will...bryanguner.medium.com

Emmet Cheat Sheet
EMMETbryanguner.medium.com

Express Quick Sheet
Settingsbryanguner.medium.com

Deploy React App To Heroku Using Postgres & Express
Heroku is an web application that makes deploying applications easy for a beginner.bryanguner.medium.com

Basic Web Development Environment Setup
Windows Subsystem for Linux (WSL) and Ubuntulevelup.gitconnected.com

Fetch Quick Sheet
+Fetchbryanguner.medium.com

By Bryan Guner on March 22, 2021.

Canonical link

Exported from Medium on July 13, 2021.

view rawA-list-of-my-articles.md hosted with ❤ by GitHub

A Very Quick Guide To Calculating Big O Computational Complexity

Big O: big picture, broad strokes, not details


A Very Quick Guide To Calculating Big O Computational Complexity

Big O: big picture, broad strokes, not details

For a more complete guide... checkout :

A Quick Guide to Big-O Notation, Memoization, Tabulation, and Sorting Algorithms by Example
Curating Complexity: A Guide to Big-O Notationmedium.com

  • way we analyze how efficient algorithms are without getting too mired in details - can model how much time any function will take given n inputs - interested in order of magnitude of number of the exact figure - O absorbs all fluff and n = biggest term - Big O of 3x^2 +x + 1 = O(n^2)

Time Complexity

no loops or exit & return = O(1)

0 nested loops = O(n)
+1 nested loops = O(n^2)
+2 nested loops = O(n^3)
+3 nested loops = O(n^4)

recursive: as you add more terms, increase in time as you add input diminishes
recursion: when you define something in terms of itself, a function that calls itself

  • used because of ability to maintain state at diffferent levels of recursion
  • inherently carries large footprint
  • every time function called, you add call to stack

iterative: use loops instead of recursion (preferred)

  • favor readability over performance

O(n log(n)) & O(log(n)): dividing/halving

  • if code employs recursion/divide-and-conquer strategy
  • what power do i need to power my base to get n

Time Definitions

  • constant: does not scale with input, will take same amount of time
  • for any input size n, constant time performs same number of operations every time
  • logarithmic: increases number of operations it performs as logarithmic function of input size n
  • function log n grows very slowly, so as n gets longer, number of operations the algorithm needs to perform doesn't increase very much
  • halving
  • linear: increases number of operations it performs as linear function of input size n
  • number of additional operations needed to perform grows in direct proportion to increase in input size n
  • log-linear: increases number of operations it performs as log-linear function of input size n
  • looking over every element and doing work on each one
  • quadratic: increases number of operations it performs as quadratic function of input size n
  • exponential: increases number of operations it performs as exponential function of input size n
  • number of nested loops increases as function of n
  • polynomial: as size of input increases, runtime/space used will grow at a faster rate
  • factorial: as size of input increases, runtime/space used will grow astronomically even with relatively small inputs
  • rate of growth: how fast a function grows with input size

Space Complexity

  • How does the space usage scale/change as input gets very large?
  • What auxiliary space does your algorithm use or is it in place (constant)?
  • Runtime stack space counts as part of space complexity unless told otherwise.

Sorting Algorithms

Data Structures

For similar content check out my GitHub:

bgoonz - Overview
Web Developer, Electrical Engineer
https://bryanguner.medium.com/ https://portfolio42.netlify.app/...jackfan.us.kg

By Bryan Guner on May 19, 2021.

Canonical link

Exported from Medium on July 13, 2021.

view rawA-Very-Qui.md hosted with ❤ by GitHub

Array Callback Methods Implemented With For Loops

How to implement array callback methods in JavaScript


Array Callback Methods Implemented With For Loops

How to implement array callback methods in JavaScript

Functions are called "First Class Objects" in JavaScript because

  • A function is an instance of the Object type
  • A function can have properties and has a link back to its constructor method
  • You can store the function in a variable
  • You can pass the function as a parameter to another function
  • You can return the function from a function

What do you think will be printed in the following:

Anonymous callback, a named callback

function foo(callback) {
+    console.log('grape');
+    callback();
+}
+
+function bar() {
+    console.log('banana');
+}
+
+const fruitBasket = function() {
+    console.log('apple');
+    bar();
+    foo(bar);
+    foo(function() {
+        console.log('orange');
+    });
+    console.log('pear');
+};
+
+fruitBasket();
+

Function that takes in a value and two callbacks. The function should return the result of the callback who's invocation results in a larger value

function greaterValue(value, cb1, cb2) {
+    // compare cb1 invoked with value to cb2 invoked with value
+    // return the greater result
+
+    let res1 = cb1(value);
+    let res2 = cb2(value);
+    if (res1 > res2) {
+        // if this is false, we move out of if statement
+        return res1;
+    }
+    return res2;
+}
+
+let negate = function(num) {
+    return num * -1;
+};
+
+let addOne = function(num) {
+    return num + 1;
+};
+
+console.log(greaterValue(3, negate, addOne));
+console.log(greaterValue(-2, negate, addOne));
+

Note: we do not invoke negate or addOne (by using () to call them), we are passing the function itself.

Write a function, myMap, that takes in an array and a callback as arguments. The function should mimic the behavior of Array.prototype.map

function myMap(arr, callback) {
+    // iterate through the array, perform the cb on each element
+    // return a new array with those new values
+    let mapped = [];
+
+    for (let i = 0; i < arr.length; i++) {
+        // remember that map passes three args with each element.
+        let val = callback(arr[i], i, arr);
+        mapped.push(val);
+    }
+
+    return mapped;
+}
+
+let double = function(num) {
+    return num * 2;
+};
+console.log(myMap([1, 2, 3], double));
+

Write a function, myFilter, that takes in an array and a callback as arguments. The function should mimic the behavior of Array.prototype.filter

function myFilter(arr, callback) {
+    let filtered = [];
+
+    for (let i = 0; i < arr.length; i++) {
+        let element = arr[i];
+
+        if (callback(element, i, arr)) {
+            filtered.push(element);
+        }
+    }
+
+    return filtered;
+}
+

Write a function, myEvery, that takes in an array and a callback as arguments. The function should mimic the behavior of Array.prototype.every

function myEvery(arr, callback) {
+    for (let i = 0; i < arr.length; i++) {
+        let element = arr[i];
+
+        if (callback(element, i, arr) === false) {
+            return false;
+        }
+    }
+    return true;
+}
+

Further Examples of the above concepts

const createMeowValue = () => {
+  console.log(this.name);
+  let meow = function () {
+    console.log(this);
+    console.log(this.name + ' meows');
+  }
+  meow = meow.bind(this);
+  return meow;
+};
+
+const name = 'Fluffy';
+
+const cat = {
+  name: name,
+  age: 12,
+  createMeow: function () {
+    console.log(this.name);
+    let meow = () => {
+      const hello = 'hello';
+      console.log(this.name + ' meows');
+    };
+    let world = '';
+    if (true) {
+      world = 'world';
+    }
+    console.log(world);
+    // meow = meow.bind(this);
+    return meow;
+  }
+};
+
+cat.newKey = function () {
+  const outermostContext = this;
+  const innerFunc = () => {
+    secondContext = this;
+    console.log(secondContext === outermostContext)
+    return function () {
+      innermostContext = this;
+    }
+  };
+  return innerFunc.bind(outermostContext);
+};
+
+const meow = cat.createMeow(); // method-style invocation
+meow(); // function-style invocation
+
+console.log('-------')
+
+const createMeow = cat.createMeow;
+const globalMeow = createMeow(); // function-style
+globalMeow(); // function-style
+
+function createSmoothie(ingredient) {
+  const ingredients = [ingredient];
+  return ingredients;
+}
+
+// console.log(createSmoothie('banana'));
+// console.log(createSmoothie('apple'));
+
+// one parameter only
+// first argument is a string
+// return an array
+// DO NOT USE forEach
+

References:

App Academy Open
App Academy Open is the first free, online web development course that's meant to get you hired as a developer. Get...open.appacademy.io

MDN Web Docs
Read more at hacks.mozilla.org Roughly a year ago at Mozilla we started an effort to improve Firefox stability on...developer.mozilla.org

Introduction: callbacks
To demonstrate the use of callbacks, promises and other abstract concepts, we'll be using some browser methods...javascript.info

More content at plainenglish.io

By Bryan Guner on May 27, 2021.

Canonical link

Exported from Medium on July 13, 2021.

view rawArray-Call.md hosted with ❤ by GitHub

Bash Commands That Save Me Time and Frustration

Here's a list of bash commands that stand between me and insanity.


Bash Commands That Save Me Time and Frustration

Here's a list of bash commands that stand between me and insanity

This article will be accompanied by the following github repository which will contain all the commands listed as well as folders that demonstrate before and after usage!

bgoonz/bash-commands-walkthrough
to accompany the medium article I am writing. Contribute to bgoonz/bash-commands-walkthrough development by creating an...jackfan.us.kg

The readme for this git repo will provide a much more condensed list... whereas this article will break up the commands with explanations... images & links!

I will include the code examples as both github gists (for proper syntax highlighting) and as code snippets adjacent to said gists so that they can easily be copied and pasted... or ... if you're like me for instance; and like to use an extension to grab the markdown content of a page... the code will be included rather than just a link to the gist!

Here's a Cheatsheet

Getting Started (Advanced Users Skip Section)


✔ Check the Current Directory ➡ pwd

On the command line, it's important to know the directory we are currently working on. For that, we can use pwd command.

It shows that I'm working on my Desktop directory.

✔ Display List of Files ➡ ls

To see the list of files and directories in the current directory use ls command in your CLI.

Shows all of my files and directories of my Desktop directory.

  • To show the contents of a directory pass the directory name to the ls command i.e. ls directory_name.
  • Some useful ls command options:-

OptionDescriptionls -alist all files including hidden file starting with '.'ls -llist with the long formatls -lalist long format including hidden files

✔ Create a Directory ➡ mkdir

We can create a new folder using the mkdir command. To use it type mkdir folder_name.

Use ls command to see the directory is created or not.

I created a cli-practice directory in my working directory i.e. Desktop directory.

✔ Move Between Directories ➡ cd

It's used to change directory or to move other directories. To use it type cd directory_name.

Can use pwd command to confirm your directory name.

Changed my directory to the cli-practice directory. And the rest of the tutorial I'm gonna work within this directory.

✔ Parent Directory ➡ ..

We have seen cd command to change directory but if we want to move back or want to move to the parent directory we can use a special symbol .. after cd command, like cd ..

✔ Create Files ➡ touch

We can create an empty file by typing touch file_name. It's going to create a new file in the current directory (the directory you are currently in) with your provided name.

I created a hello.txt file in my current working directory. Again you can use ls command to see the file is created or not.

Now open your hello.txt file in your text editor and write Hello Everyone! into your hello.txt file and save it.

✔ Display the Content of a File ➡ cat

We can display the content of a file using the cat command. To use it type cat file_name.

Shows the content of my hello.txt file.

✔ Move Files & Directories ➡ mv

To move a file and directory, we use mv command.

By typing mv file_to_move destination_directory, you can move a file to the specified directory.

By entering mv directory_to_move destination_directory, you can move all the files and directories under that directory.

Before using this command, we are going to create two more directories and another txt file in our cli-practice directory.

mkdir html css touch bye.txt

Yes, we can use multiple directories & files names one after another to create multiple directories & files in one command.

Moved my bye.txt file into my css directory and then moved my css directory into my html directory.

✔ Rename Files & Directories ➡ mv

mv command can also be used to rename a file and a directory.

You can rename a file by typing mv old_file_name new_file_name & also rename a directory by typing mv old_directory_name new_directory_name.

Renamed my hello.txt file to the hi.txt file and html directory to the folder directory.

✔ Copy Files & Directories ➡ cp

To do this, we use the cp command.

  • You can copy a file by entering cp file_to_copy new_file_name.

Copied my hi.txt file content into hello.txt file. For confirmation open your hello.txt file in your text editor.

  • You can also copy a directory by adding the -r option, like cp -r directory_to_copy new_directory_name.

The -r option for "recursive" means that it will copy all of the files including the files inside of subfolders.

Here I copied all of the files from the folder to folder-copy.

✔ Remove Files & Directories ➡ rm

To do this, we use the rm command.

  • To remove a file, you can use the command like rm file_to_remove.

Here I removed my hi.txt file.

  • To remove a directory, use the command like rm -r directory_to_remove.

I removed my folder-copy directory from my cli-practice directory i.e. current working directory.

✔ Clear Screen ➡ clear

Clear command is used to clear the terminal screen.

✔ Home Directory ➡ ~

The Home directory is represented by ~. The Home directory refers to the base directory for the user. If we want to move to the Home directory we can use cd ~ command. Or we can only use cd command.


MY COMMANDS

1.) Recursively unzip zip files and then delete the archives when finished

here is a folder containing the before and after... I had to change folder names slightly due to a limit on the length of file-paths in a github repo.

find . -name "*.zip" | while read filename; do unzip -o -d "`dirname "$filename"`" "$filename"; done;
+
+find . -name "*.zip" -type f -print -delete
+

2.) Install node modules recursively

npm i -g recursive-install
+
+npm-recursive-install
+

3.) Clean up unnecessary files/folders in git repo

find . -empty -type f -print -delete #Remove empty files
+
+# -------------------------------------------------------
+find . -empty -type d -print -delete #Remove empty folders
+
+# -------------------------------------------------------
+
+# This will remove .git folders...    .gitmodule files as well as .gitattributes and .gitignore files.
+
+find . \( -name ".git" -o -name ".gitignore" -o -name ".gitmodules" -o -name ".gitattributes" \) -exec rm -rf -- {} +
+
+# -------------------------------------------------------
+
+# This will remove the filenames you see listed below that just take up space if a repo has been downloaded for use exclusively in your personal file system (in which case the following files just take up space)# Disclaimer... you should not use this command in a repo that you intend to use with your work as it removes files that attribute the work to their original creators!
+
+find . \( -name "*SECURITY.txt" -o -name "*RELEASE.txt" -o -name "*CHANGELOG.txt" -o -name "*LICENSE.txt" -o -name "*CONTRIBUTING.txt" -name "*HISTORY.md" -o -name "*LICENSE" -o -name "*SECURITY.md" -o -name "*RELEASE.md" -o -name "*CHANGELOG.md" -o -name "*LICENSE.md" -o -name "*CODE_OF_CONDUCT.md" -o -name "\*CONTRIBUTING.md" \) -exec rm -rf -- {} +
+

In Action

The following output from my bash shell corresponds to the directory:

bgoonz/bash-commands-walkthrough
Deployment github-pages Navigation Big O notation is the language we use for talking about how long an algorithm takes...jackfan.us.kg

which was created by running the aforementioned commands in in a perfect copy of this directory

bgoonz/DS-ALGO-OFFICIAL
Deployment github-pages Navigation Big O notation is the language we use for talking about how long an algorithm takes...jackfan.us.kg

.....below is the terminal output for the following commands:

pwd
+/mnt/c/Users/bryan/Downloads/bash-commands/steps/3-clean-up-fluf/DS-ALGO-OFFICIAL-master
+

After printing the working directory for good measure:

find . -empty -type f -print -delete
+

The above command deletes empty files recursively starting from the directory in which it was run:

./CONTENT/DS-n-Algos/File-System/file-utilities/node_modules/line-reader/test/data/empty_file.txt
+./CONTENT/DS-n-Algos/_Extra-Practice/free-code-camp/nodejs/http-collect.js
+./CONTENT/Resources/Comments/node_modules/mime/.npmignore
+./markdown/tree2.md
+./node_modules/loadashes6/lodash/README.md
+./node_modules/loadashes6/lodash/release.md
+./node_modules/web-dev-utils/Markdown-Templates/Markdown-Templates-master/filled-out-readme.md
+|01:33:16|bryan@LAPTOP-9LGJ3JGS:[DS-ALGO-OFFICIAL-master] DS-ALGO-OFFICIAL-master_exitstatus:0[╗___________o>
+

The command seen below deletes empty folders recursively starting from the directory in which it was run:

find . -empty -type d -print -delete
+

The resulting directories....

|01:33:16|bryan@LAPTOP-9LGJ3JGS:[DS-ALGO-OFFICIAL-master] DS-ALGO-OFFICIAL-master_exitstatus:0[╗___________o>
+
+find . -empty -type d -print -delete
+./.git/branches
+./.git/objects/info
+./.git/refs/tags
+|01:33:31|bryan@LAPTOP-9LGJ3JGS:[DS-ALGO-OFFICIAL-master] DS-ALGO-OFFICIAL-master_exitstatus:0[╗___________o>
+

The command seen below deletes .git folders as well as .gitignore, .gitattributes, .gitmodule files

find . \( -name ".git" -o -name ".gitignore" -o -name ".gitmodules" -o -name ".gitattributes" \) -exec rm -rf -- {} +
+

The command seen below deletes most SECURITY, RELEASE, CHANGELOG, LICENSE, CONTRIBUTING, & HISTORY files that take up pointless space in repo's you wish to keep exclusively for your own reference.

!!!Use with caution as this command removes the attribution of the work from it's original authors

!!!Use with caution as this command removes the attribution of the work from it's original authors!!!!!

!!!Use with caution as this command removes the attribution of the work from it's original authors!!!!!find . ( -name "SECURITY.txt" -o -name "RELEASE.txt" -o -name "CHANGELOG.txt" -o -name "LICENSE.txt" -o -name "CONTRIBUTING.txt" -name "HISTORY.md" -o -name "LICENSE" -o -name "SECURITY.md" -o -name "RELEASE.md" -o -name "CHANGELOG.md" -o -name "LICENSE.md" -o -name "CODEOFCONDUCT.md" -o -name "*CONTRIBUTING.md" ) -exec rm -rf -- {} +


4.) Generate index.html file that links to all other files in working directory

#!/bin/sh
+# find ./ | grep -i "\.*$" >files
+find ./ | sed -E -e 's/([^ ]+[ ]+){8}//' | grep -i "\.*$">files
+listing="files"
+out=""
+html="index.html"
+out="basename $out.html"
+html="index.html"
+cmd() {
+  echo '  <!DOCTYPE html>'
+  echo '<html>'
+  echo '<head>'
+  echo '  <meta http-equiv="Content-Type" content="text/html">'
+  echo '  <meta name="Author" content="Bryan Guner">'
+  echo '<link rel="stylesheet" href="./assets/prism.css">'
+  echo ' <link rel="stylesheet" href="./assets/style.css">'
+  echo ' <script async defer src="./assets/prism.js"></script>'
+  echo "  <title> directory </title>"
+  echo ""
+  echo '<style>'
+echo '    a {'
+echo '      color: black;'
+echo '    }'
+echo ''
+echo '    li {'
+echo '      border: 1px solid black !important;'
+echo '      font-size: 20px;'
+echo '      letter-spacing: 0px;'
+echo '      font-weight: 700;'
+echo '      line-height: 16px;'
+echo '      text-decoration: none !important;'
+echo '      text-transform: uppercase;'
+echo '      background: #194ccdaf !important;'
+echo '      color: black !important;'
+echo '      border: none;'
+echo '      cursor: pointer;'
+echo '      justify-content: center;'
+echo '      padding: 30px 60px;'
+echo '      height: 48px;'
+echo '      text-align: center;'
+echo '      white-space: normal;'
+echo '      border-radius: 10px;'
+echo '      min-width: 45em;'
+echo '      padding: 1.2em 1em 0;'
+echo '      box-shadow: 0 0 5px;'
+echo '      margin: 1em;'
+echo '      display: grid;'
+echo '      -webkit-border-radius: 10px;'
+echo '      -moz-border-radius: 10px;'
+echo '      -ms-border-radius: 10px;'
+echo '      -o-border-radius: 10px;'
+echo '    }'
+echo '  </style>'
+  echo '</head>'
+  echo '<body>'
+  echo ""
+  # continue with the HTML stuff
+  echo ""
+  echo ""
+  echo "<ul>"
+  awk '{print "<li><a href=\""$1"\">",$1,"&nbsp;</a></li>"}' $listing
+  # awk '{print "<li>"};
+  #  {print " <a href=\""$1"\">",$1,"</a></li>&nbsp;"}' \ $listing
+  echo ""
+  echo "</ul>"
+  echo "</body>"
+  echo "</html>"
+}
+cmd $listing --sort=extension >>$html
+

In Action

I will use this copy of my Data Structures Practice Site to demonstrate the result:

side-projects-42/DS-Bash-Examples-Deploy
Deployment github-pages Navigation Big O notation is the language we use for talking about how long an algorithm takes...jackfan.us.kg

The result is a index.html file that contains a list of links to each file in the directory

here is a link to and photo of the resulting html file:

index.html
CONTENT/DS-n-Algos/quirky-meninsky-4181b5.netlify.app


5.) Download all links to a files of a specified extension on a user provided (url) webpage

wget -r -A.pdf https://overapi.com/gitwget --wait=2 --level=inf --limit-rate=20K --recursive --page-requisites --user-agent=Mozilla --no-parent --convert-links --adjust-extension --no-clobber -e robots=off
+

The result is stored in this directory:


6.)Recursively remove lines of text containing the string badFolder from files in the working directory

find . -type f -exec sed -i '/badFolder/d' ./* {} \;
+
+# OR
+
+find . -name 'result.md'  -type f -exec sed -i '/badFolder/d' ./* {} \;
+

As an example I will run this command on a file containing the text:

Hacks Blog
+
+Read more at hacks.mozilla.org
+
+badFolder
+
+badFolder Implementing Private Fields for JavaScript
+
+When implementing a language feature for JavaScript, an implementer must make decisions about how the language in the specification maps to the implementation. Private fields is an example of where the specification language and implementation reality diverge, at least in SpiderMonkey-- the JavaScript engine which powers Firefox. To understand more, I'll explain what private fields are, a couple of models for thinking about them, and explain why our implementation diverges from the specification language.The post Implementing Private Fields for JavaScript appeared first on Mozilla Hacks - the Web developer blog.
+
+Posted Tuesday, June 8, 2021 by Matthew Gaudet
+
+Looking fine with Firefox 89
+
+Firefox 89 has smartened up and brings with it a slimmed-down, slightly more minimalist interface.badFolder Along with this new look, we get some great styling features including a force-colours feature for media queries and better control over how fonts are displayed. The long-awaited top-level await keyword for JavaScript modules is now enabled, as well as the PerformanceEventTiming interface, which is another addition to the performance suite of APIs: 89 really has been working out!The post Looking fine with Firefox 89 appeared first on Mozilla Hacks - the Web developer blog.
+
+badFolder
+
+Posted Tuesday, June 1, 2021 by Chris Mills
+
+badFolder
+
+Improving Firefox stability on Linux
+
+Roughly a year ago at Mozilla we started an effort to improve Firefox stability on Linux. This effort quickly became an example of good synergies between FOSS projects.The post Improving Firefox stability on Linux appeared first on Mozilla Hacks - the Web developer blog.
+
+Posted Wednesday, May 19, 2021 by Gabriele Svelto badFolder
+
+Introducing Firefox's new Site Isolation Security Architecture
+
+Like any web browser, Firefox loads code from untrusted and potentially hostile websites and runs it on your computer. To protect you against new types of attacks from malicious sites and to meet the security principles of Mozilla, we set out to redesign Firefox on desktop.The post Introducing Firefox's new Site Isolation Security Architecture appeared first on Mozilla Hacks - the Web developer blog.
+
+Posted Tuesday, May 18, 2021 by Anny Gakhokidze
+
+Pyodide Spin Out and 0.17 Release
+
+We are happy to announce that Pyodide has become an independent and community-driven project. We are also pleased to announce the 0.17 release for Pyodide with many new features and improvements. Pyodide consists of the CPython 3.8 interpreter compiled to WebAssembly which allows Python to run in the browser.The post Pyodide Spin Out and 0.17 Release appeared first on Mozilla Hacks - the Web developer blog. badFolder
+
+Posted Thursday, April 22, 2021 by Teon Brooks
+

I modified the command slightly to apply only to files called 'result.md':

The result is :

Hacks Blog
+
+Read more at hacks.mozilla.org
+
+When implementing a language feature for JavaScript, an implementer must make decisions about how the language in the specification maps to the implementation. Private fields is an example of where the specification language and implementation reality diverge, at least in SpiderMonkey-- the JavaScript engine which powers Firefox. To understand more, I'll explain what private fields are, a couple of models for thinking about them, and explain why our implementation diverges from the specification language.The post Implementing Private Fields for JavaScript appeared first on Mozilla Hacks - the Web developer blog.
+
+Posted Tuesday, June 8, 2021 by Matthew Gaudet
+
+Looking fine with Firefox 89
+
+Posted Tuesday, June 1, 2021 by Chris Mills
+
+Improving Firefox stability on Linux
+
+Roughly a year ago at Mozilla we started an effort to improve Firefox stability on Linux. This effort quickly became an example of good synergies between FOSS projects.The post Improving Firefox stability on Linux appeared first on Mozilla Hacks - the Web developer blog.
+
+Introducing Firefox's new Site Isolation Security Architecture
+
+Like any web browser, Firefox loads code from untrusted and potentially hostile websites and runs it on your computer. To protect you against new types of attacks from malicious sites and to meet the security principles of Mozilla, we set out to redesign Firefox on desktop.The post Introducing Firefox's new Site Isolation Security Architecture appeared first on Mozilla Hacks - the Web developer blog.
+
+Posted Tuesday, May 18, 2021 by Anny Gakhokidze
+
+Pyodide Spin Out and 0.17 Release
+
+Posted Thursday, April 22, 2021 by Teon Brooks
+

the test.txt and result.md files can be found here:

bgoonz/bash-commands-walkthrough
to accompany the medium article I am writing. Contribute to bgoonz/bash-commands-walkthrough development by creating an...jackfan.us.kg


7.) Execute command recursively

Here I have modified the command I wish to run recursively to account for the fact that the 'find' command already works recursively, by appending the -maxdepth 1 flag...

I am essentially removing the recursive action of the find command...

That way, if the command affects the more deeply nested folders we know the outer RecurseDirs function we are using to run the find/pandoc line once in every subfolder of the working directory... is working properly!

Run in the folder shown to the left... we would expect every .md file to be accompanied by a newly generated html file by the same name.

The results of said operation can be found in the following directory

In Action

🢃 Below 🢃

The final result is

If you want to run any bash script recursively all you have to do is substitue out line #9 with the command you want to run once in every sub-folder.

function RecurseDirs ()
+{
+    oldIFS=$IFS
+    IFS=$'\n'
+    for f in "$@"
+    do
+
+#Replace the line below with your own command!
+
+#find ./ -iname "*.md" -maxdepth 1 -type f -exec sh -c 'pandoc --standalone "${0}" -o "${0%.md}.html"' {} \;
+
+#####################################################
+# YOUR CODE BELOW!
+
+#####################################################
+
+if [[ -d "${f}" ]]; then
+            cd "${f}"
+            RecurseDirs $(ls -1 ".")
+            cd ..
+        fi
+    done
+    IFS=$oldIFS
+}
+RecurseDirs "./"
+

TBC

Here are some of the other commands I will cover in greater detail... at a later time:

9. Copy any text between

+ + + + + +View My +Stats + + diff --git a/docs/content/html-docs/faq/contact/index.html b/docs/content/html-docs/faq/contact/index.html new file mode 100644 index 0000000000..113b2b7506 --- /dev/null +++ b/docs/content/html-docs/faq/contact/index.html @@ -0,0 +1,533 @@ + +Contact! | webdevhub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Contact!








Calendar:

+ + + + + +View My +Stats + + diff --git a/docs/content/html-docs/faq/index.html b/docs/content/html-docs/faq/index.html new file mode 100644 index 0000000000..dd2d7179ca --- /dev/null +++ b/docs/content/html-docs/faq/index.html @@ -0,0 +1,534 @@ + +FAQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

FAQ

What's the most useful business-related book you've ever read?

A Random Walk Down Wall Street

What's your favorite non-business book?

Hitchhiker's Guide To The Galaxy

If money were not an issue, what would you be doing right now?

Designing recording software/hardware and using it

What words of advice would you give your younger self?

Try harder and listen to your parents more (the latter bit of advice would be almost certain to fall on deaf ears lol)

What's the most creative thing you've ever done?

I built a platform that listens to a guitarist's performance and automatically triggers guitar effects at the appropriate time in the song.

Which founders or startups do you most admire?

Is it to basic to say Tesla... I know they're prevalent now but I've been an avid fan since as early as 2012.

What's your super power?

Having really good ideas and forgetting them moments later.

What's the best way for people to get in touch with you?

A text

What aspects of your work are you most passionate about?

Creating things that change my every day life.

What was the most impactful class you took in school?

Modern Physics... almost changed my major after that class... but at the end of the day engineering was a much more fiscally secure avenue.

What's something you wish you had done years earlier?

Learned to code ... and sing

What words of wisdom do you live by?

*Disclaimer: The following wisdom is very cliche ... but... "Be the change that you wish to see in the world."

+ + + + + +View My +Stats + + diff --git a/docs/content/html-docs/faq/plug-ins/index.html b/docs/content/html-docs/faq/plug-ins/index.html new file mode 100644 index 0000000000..26cfff1b00 --- /dev/null +++ b/docs/content/html-docs/faq/plug-ins/index.html @@ -0,0 +1,835 @@ + +Gatsby Plugins For This Sites Content Model + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Plug-ins

Note: These are the gatsby plugins that power the file system of this website! See more in the Docs section. +

Code:

Gatsby Source File System

js
+const path = require('path');
+const fs = require('fs');
+const { createFilePath } = require('gatsby-source-filesystem');
+const _ = require('lodash');
+
+function findFileNode({ node, getNode }) {
+    let fileNode = node;
+    let ids = [fileNode.id];
+
+    while (fileNode && fileNode.internal.type !== `File` && fileNode.parent) {
+        fileNode = getNode(fileNode.parent);
+
+        if (!fileNode) {
+            break;
+        }
+
+        if (_.includes(ids, fileNode.id)) {
+            console.log(`found cyclic reference between nodes`);
+            break;
+        }
+
+        ids.push(fileNode.id);
+    }
+
+    if (!fileNode || fileNode.internal.type !== `File`) {
+        console.log('did not find ancestor File node');
+        return null;
+    }
+
+    return fileNode;
+}
+
+exports.onCreateNode = ({ node, getNode, actions }, options) => {
+    const { createNodeField } = actions;
+
+    if (node.internal.type === 'MarkdownRemark') {
+        let fileNode = findFileNode({ node, getNode });
+        if (!fileNode) {
+            throw new Error('could not find parent File node for MarkdownRemark node: ' + node);
+        }
+
+        let url;
+        if (node.frontmatter.url) {
+            url = node.frontmatter.url;
+        } else if (_.get(options, 'uglyUrls', false)) {
+            url = path.join(fileNode.relativeDirectory, fileNode.name + '.html');
+        } else {
+            url = createFilePath({ node, getNode });
+        }
+
+        createNodeField({ node, name: 'url', value: url });
+        createNodeField({
+            node,
+            name: 'absolutePath',
+            value: fileNode.absolutePath
+        });
+        createNodeField({
+            node,
+            name: 'relativePath',
+            value: fileNode.relativePath
+        });
+        createNodeField({ node, name: 'absoluteDir', value: fileNode.dir });
+        createNodeField({
+            node,
+            name: 'relativeDir',
+            value: fileNode.relativeDirectory
+        });
+        createNodeField({ node, name: 'base', value: fileNode.base });
+        createNodeField({ node, name: 'ext', value: fileNode.ext });
+        createNodeField({ node, name: 'name', value: fileNode.name });
+    }
+};
+
+exports.createPages = ({ graphql, getNode, actions, getNodesByType }) => {
+    const { createPage, deletePage } = actions;
+
+    // Use GraphQL to bring only the "id" and "html" (added by gatsby-transformer-remark)
+    // properties of the MarkdownRemark nodes. Don't bring additional fields
+    // such as "relativePath". Otherwise, Gatsby's GraphQL resolvers might infer
+    // types these fields as File and change their structure. For example, the
+    // "html" attribute exists only on a GraphQL node, but does not exist on the
+    // underlying node.
+    return graphql(`
+        {
+            allMarkdownRemark {
+                edges {
+                    node {
+                        id
+                        html
+                    }
+                }
+            }
+        }
+    `).then((result) => {
+        if (result.errors) {
+            return Promise.reject(result.errors);
+        }
+
+        const nodes = result.data.allMarkdownRemark.edges.map(({ node }) => node);
+        const siteNode = getNode('Site');
+        const siteDataNode = getNode('SiteData');
+        const sitePageNodes = getNodesByType('SitePage');
+        const sitePageNodesByPath = _.keyBy(sitePageNodes, 'path');
+        const siteData = _.get(siteDataNode, 'data', {});
+
+        const pages = nodes.map((graphQLNode) => {
+            // Use the node id to get the underlying node. It is not exactly the
+            // same node returned by GraphQL, because GraphQL resolvers might
+            // transform node fields.
+            const node = getNode(graphQLNode.id);
+            return {
+                url: node.fields.url,
+                relativePath: node.fields.relativePath,
+                relativeDir: node.fields.relativeDir,
+                base: node.fields.base,
+                name: node.fields.name,
+                frontmatter: node.frontmatter,
+                html: graphQLNode.html
+            };
+        });
+
+        nodes.forEach((graphQLNode) => {
+            const node = getNode(graphQLNode.id);
+            const url = node.fields.url;
+
+            const template = node.frontmatter.template;
+            if (!template) {
+                console.error(`Error: undefined template for ${url}`);
+                return;
+            }
+
+            const component = path.resolve(`./src/templates/${template}.js`);
+            if (!fs.existsSync(component)) {
+                console.error(`Error: component "src/templates/${template}.js" missing for ${url}`);
+                return;
+            }
+
+            const existingPageNode = _.get(sitePageNodesByPath, url);
+
+            const page = {
+                path: url,
+                component: component,
+                context: {
+                    url: url,
+                    relativePath: node.fields.relativePath,
+                    relativeDir: node.fields.relativeDir,
+                    base: node.fields.base,
+                    name: node.fields.name,
+                    frontmatter: node.frontmatter,
+                    html: graphQLNode.html,
+                    pages: pages,
+                    site: {
+                        siteMetadata: _.get(siteData, 'site-metadata', {}),
+                        pathPrefix: siteNode.pathPrefix,
+                        data: _.omit(siteData, 'site-metadata')
+                    }
+                }
+            };
+
+            if (existingPageNode && !_.get(page, 'context.menus')) {
+                page.context.menus = _.get(existingPageNode, 'context.menus');
+            }
+
+            createPage(page);
+        });
+    });
+};
+
+```
+</pre>
+
Gatsby Source Data
<pre>
+```javascript
+
const path = require('path');
+const yaml = require('js-yaml');
+const fse = require('fs-extra');
+const chokidar = require('chokidar');
+const _ = require('lodash');
+
+const metadataFileName = 'site-metadata.json';
+
+const parsers = {
+    yaml: (data) => yaml.safeLoad(data, { schema: yaml.JSON_SCHEMA }),
+    json: (data) => JSON.parse(data)
+};
+
+const supportedExtensions = {
+    yaml: parsers.yaml,
+    yml: parsers.yaml,
+    json: parsers.json
+};
+
+exports.sourceNodes = (props, pluginOptions = {}) => {
+    const createContentDigest = props.createContentDigest;
+    const { createNode } = props.actions;
+    const reporter = props.reporter;
+
+    if (!_.get(pluginOptions, 'path')) {
+        pluginOptions.path = 'src/data';
+    }
+
+    if (!path.isAbsolute(pluginOptions.path)) {
+        pluginOptions.path = path.resolve(process.cwd(), pluginOptions.path);
+    }
+
+    reporter.info(`[gatsby-source-data] setup file watcher and create site data`);
+
+    const dataPath = pluginOptions.path;
+    const createSiteDataFromFilesPartial = _.partial(createSiteDataFromFiles, {
+        dataPath,
+        createNode,
+        createContentDigest,
+        reporter
+    });
+    const watcher = chokidar.watch([dataPath, metadataFileName], {
+        cwd: '.',
+        ignoreInitial: true
+    });
+    watcher.on('add', createSiteDataFromFilesPartial);
+    watcher.on('change', createSiteDataFromFilesPartial);
+    watcher.on('unlink', createSiteDataFromFilesPartial);
+
+    return createSiteDataFromFiles({ dataPath, createNode, createContentDigest, reporter }, null);
+};
+
+async function createSiteDataFromFiles({ dataPath, createNode, createContentDigest, reporter }, changedFile) {
+    reporter.info(`[gatsby-source-data] create site data from files, updated path: ${changedFile}`);
+    let dataFiles = [];
+
+    const dataPathExists = await fse.pathExists(dataPath);
+    if (dataPathExists) {
+        dataFiles = await readDirRecursively(dataPath);
+    }
+
+    const metadataPath = path.resolve(metadataFileName);
+    const metadataExists = await fse.pathExists(metadataPath);
+    if (metadataExists) {
+        dataFiles.push(metadataFileName);
+    }
+
+    const sortedDataFiles = dataFiles.slice().sort();
+    const data = await convertDataFilesToJSON(sortedDataFiles, dataPath, reporter);
+
+    createNode({
+        id: 'SiteData',
+        parent: null,
+        children: [],
+        data: data,
+        internal: {
+            type: 'SiteData',
+            contentDigest: createContentDigest(JSON.stringify(data)),
+            description: `Site data from ${path.relative(process.cwd(), dataPath)}`
+        }
+    });
+}
+
+async function readDirRecursively(dir, options) {
+    const rootDir = _.get(options, 'rootDir', dir);
+    const files = await fse.readdir(dir);
+    const promises = _.map(files, async (file) => {
+        const filePath = path.join(dir, file);
+        const stats = await fse.stat(filePath);
+        if (stats.isDirectory()) {
+            return readDirRecursively(filePath, { rootDir });
+        } else if (stats.isFile()) {
+            return path.relative(rootDir, filePath);
+        } else {
+            return null;
+        }
+    });
+    const recFiles = await Promise.all(promises);
+    return _.chain(recFiles).compact().flatten().value();
+}
+
+function convertDataFilesToJSON(dataFiles, dataDirPath, reporter) {
+    let promises = _.map(dataFiles, (filePath) => {
+        const pathObject = path.parse(filePath);
+        const absFilePath = pathObject.base === metadataFileName ? metadataFileName : path.join(dataDirPath, filePath);
+        const relPath = pathObject.base === metadataFileName ? metadataFileName : filePath;
+        const relDir = pathObject.base === metadataFileName ? '' : pathObject.dir;
+        const ext = pathObject.ext.substring(1);
+        if (!_.has(supportedExtensions, ext)) {
+            return null;
+        }
+        return fse.readFile(absFilePath).then((data) => {
+            const propPath = _.compact(relDir.split(path.sep).concat(pathObject.name));
+            const res = {};
+            try {
+                const parsedData = supportedExtensions[ext](data);
+                _.set(res, propPath, parsedData);
+            } catch (err) {
+                reporter.warn(`[gatsby-source-data] could not parse file: ${relPath}`);
+            }
+            return res;
+        });
+    });
+    return Promise.all(promises).then((results) => {
+        return _.reduce(results, (data, res) => _.merge(data, res), {});
+    });
+}
+
</pre>
+
+ + + + + +View My +Stats + + diff --git a/docs/content/html-docs/gallery/index.html b/docs/content/html-docs/gallery/index.html new file mode 100644 index 0000000000..7b8a45254e --- /dev/null +++ b/docs/content/html-docs/gallery/index.html @@ -0,0 +1,116 @@ + + + + + + + Page Not Found + + + + +
+
+
+

Page Not Found

+
+
+

Looks like you've followed a broken link or entered a URL that doesn't exist on this site.

+

+ + + + + Back to our site + +

+
+

+ If this is your site, and you weren't expecting a 404 for this path, please visit Netlify's + "page not found" support guide + for troubleshooting tips. +

+
+
+
+ + + diff --git a/docs/content/html-docs/gists/index.html b/docs/content/html-docs/gists/index.html new file mode 100644 index 0000000000..ce086a5992 --- /dev/null +++ b/docs/content/html-docs/gists/index.html @@ -0,0 +1,814 @@ + +Gist Archive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

my gists

Gist Archive

Gist Archive





Featured Gists:

const Promise = require("bluebird");
+const fs = Promise.promisifyAll(require("fs"));
+const crypto = require("crypto");
+const path = require("path");
+const pathA = ".";
+const pathB = "/path/to/the/directory/you/want/to/compare/it/to";
+let hashes = [];
+function hashDirIn(folder) {
+  var pathPromiseA = fs
+    .readdirAsync(folder)
+    .map(function (fileName) {
+      var workPath = path.join(folder, fileName);
+      var statPromise = fs.statAsync(workPath);
+      return Promise.join(
+        statPromise,
+        fileName,
+        function (statPromise, fileName) {
+          if (statPromise.isFile()) {
+            function makeStream(file, callback) {
+              var result = fs.createReadStream(workPath);
+              return callback(result);
+            }
+            function process(stream) {
+              var hash = crypto.createHash("md5");
+              return new Promise(function (resolve, reject) {
+                stream.on("data", function updateProcess(chunk) {
+                  hash.update(chunk, "utf8");
+                });
+                stream.on("end", resolve);
+              }).then(function publish() {
+                var digest = hash.digest("hex");
+                hashes.push({ digest: digest, path: workPath });
+              });
+            }
+            return makeStream(fileName, process);
+          }
+        }
+      );
+    })
+    .then(function () {
+      if (i == 1) {
+        hashes.sort(function (a, b) {
+          if (a.digest < b.digest) {
+            return -1;
+          }
+          if (a.digest > b.digest) {
+            return 1;
+          }
+          return 0;
+        });
+        var dupe = 1;
+        hashes.map(function (obj, index) {
+          if (index - 1 >= 0) {
+            if (obj.digest == hashes[index - 1].digest) {
+              console.log("Dupe " + dupe + " found:");
+              console.log(obj.path);
+              console.log("Equal to:");
+              console.log(hashes[index - 1].path + "\n");
+              dupe++;
+            }
+          }
+        });
+      }
+      i++;
+    });
+}
+var i = 0;
+hashDirIn(pathA);
+hashDirIn(pathB);
+


will replace any spaces in file names with an underscore!

 for file in *; do mv "$file" `echo $file | tr ' ' '_'` ; done
+  ## TAKING IT A STEP FURTHER:
+ # Let's do it recursivley:
+  function RecurseDirs ()
+{
+    oldIFS=$IFS
+    IFS=$'\n'
+    for f in "$@"
+    do
+  # YOUR CODE HERE!
+
+[![-----------------------------------------------------](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/colored.png)]
+
+for file in \*; do mv "$file" `echo $file | tr ' ' '_'` ; done
+        if [[ -d "${f}" ]]; then
+cd "${f}"
+            RecurseDirs $(ls -1 ".")
+            cd ..
+        fi
+    done
+    IFS=$oldIFS
+}
+RecurseDirs "./"
+

Copy to clipboard jQuerry

Language: Javascript/Jquery

In combination with the script tag : , this snippet will add a copy to clipboard button to all of your embedded blocks.

$(document).ready(function () {
+  $("code, pre").append(
+    '<span class="command-copy" ><i class="fa fa-clipboard" aria-hidden="true"></i></span>'
+  );
+  $("code span.command-copy").click(function (e) {
+    var text = $(this).parent().text().trim(); //.text();
+    var copyHex = document.createElement("input");
+    copyHex.value = text;
+    document.body.appendChild(copyHex);
+    copyHex.select();
+    document.execCommand("copy");
+    console.log(copyHex.value);
+    document.body.removeChild(copyHex);
+  });
+
+  $("pre span.command-copy").click(function (e) {
+    var text = $(this).parent().text().trim();
+    var copyHex = document.createElement("input");
+    copyHex.value = text;
+    document.body.appendChild(copyHex);
+    copyHex.select();
+    document.execCommand("copy");
+    console.log(copyHex.value);
+    document.body.removeChild(copyHex);
+  });
+});
+

Append Files in PWD

//APPEND-DIR.js
+const fs = require("fs");
+let cat = require("child_process").execSync("cat *").toString("UTF-8");
+fs.writeFile("output.md", cat, (err) => {
+  if (err) throw err;
+});
+

doesUserFrequentStarbucks.js

const isAppleDevice = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
+console.log(isAppleDevice);
+// Result: will return true if user is on an Apple device
+

arr-intersection.js

/*
+ function named intersection(firstArr) that takes in an array and
+returns a function. 
+When the function returned by intersection is invoked
+passing in an array (secondArr) it returns a new array containing the elements
+common to both firstArr and secondArr.
+*/
+function intersection(firstArr) {
+  return (secondArr) => {
+    let common = [];
+    for (let i = 0; i < firstArr.length; i++) {
+      let el = firstArr[i];
+      if (secondArr.indexOf(el) > -1) {
+        common.push(el);
+      }
+    }
+    return common;
+  };
+}
+let abc = intersection(["a", "b", "c"]); // returns a function
+console.log(abc(["b", "d", "c"])); // returns [ 'b', 'c' ]
+
+let fame = intersection(["f", "a", "m", "e"]); // returns a function
+console.log(fame(["a", "f", "z", "b"])); // returns [ 'f', 'a' ]
+

arr-of-cum-partial-sums.js

/*
+First is recurSum(arr, start) which returns the sum of the elements of arr from the index start till the very end.
+Second is partrecurSum() that recursively concatenates the required sum into an array and when we reach the end of the array, it returns the concatenated array.
+*/
+//arr.length -1 = 5
+//                   arr   [    1,    7,    12,   6,    5,    10   ]
+//                   ind   [    0     1     2     3     4      5   ]
+//                              ↟                              ↟
+//                            start                           end
+
+function recurSum(arr, start = 0, sum = 0) {
+  if (start < arr.length) {
+    return recurSum(arr, start + 1, sum + arr[start]);
+  }
+  return sum;
+}
+
+function rPartSumsArr(arr, partSum = [], start = 0, end = arr.length - 1) {
+  if (start <= end) {
+    return rPartSumsArr(
+      arr,
+      partSum.concat(recurSum(arr, start)),
+      ++start,
+      end
+    );
+  }
+  return partSum.reverse();
+}
+
+console.log(
+  "------------------------------------------------rPartSumArr------------------------------------------------"
+);
+console.log("rPartSumsArr(arr)=[ 1, 1, 5, 2, 6, 10 ]: ", rPartSumsArr(arr));
+console.log("rPartSumsArr(arr1)=[ 1, 7, 12, 6, 5, 10 ]: ", rPartSumsArr(arr1));
+console.log(
+  "------------------------------------------------rPartSumArr------------------------------------------------"
+);
+/*
+------------------------------------------------rPartSumArr------------------------------------------------
+rPartSumsArr(arr)=[ 1, 1, 5, 2, 6, 10 ]:  [ 10, 16, 18, 23, 24, 25 ]
+rPartSumsArr(arr1)=[ 1, 7, 12, 6, 5, 10 ]:  [ 10, 15, 21, 33, 40, 41 ]
+------------------------------------------------rPartSumArr------------------------------------------------
+*/
+

camel2Kabab.js

function camelToKebab(value) {
+  return value.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
+}
+

camelCase.js

function camel(str) {
+  return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function (match, index) {
+    if (+match === 0) return ""; // or if (/\s+/.test(match)) for white spaces
+    return index === 0 ? match.toLowerCase() : match.toUpperCase();
+  });
+}
+

concatLinkedLists.js

function addTwoNumbers(l1, l2) {
+  let result = new ListNode(0);
+  let currentNode = result;
+  let carryOver = 0;
+  while (l1 != null || l2 != null) {
+    let v1 = 0;
+    let v2 = 0;
+    if (l1 != null) v1 = l1.val;
+    if (l2 != null) v2 = l2.val;
+    let sum = v1 + v2 + carryOver;
+    carryOver = Math.floor(sum / 10);
+    sum = sum % 10;
+    currentNode.next = new ListNode(sum);
+    currentNode = currentNode.next;
+    if (l1 != null) l1 = l1.next;
+    if (l2 != null) l2 = l2.next;
+  }
+  if (carryOver > 0) {
+    currentNode.next = new ListNode(carryOver);
+  }
+  return result.next;
+}
+

fast-is-alpha-numeric.js

//Function to test if a character is alpha numeric that is faster than a regular
+//expression in JavaScript
+
+let isAlphaNumeric = (char) => {
+  char = char.toString();
+  let id = char.charCodeAt(0);
+  if (
+    !(id > 47 && id < 58) && // if not numeric(0-9)
+    !(id > 64 && id < 91) && // if not letter(A-Z)
+    !(id > 96 && id < 123) // if not letter(a-z)
+  ) {
+    return false;
+  }
+  return true;
+};
+
+console.log(isAlphaNumeric("A")); //true
+console.log(isAlphaNumeric(2)); //true
+console.log(isAlphaNumeric("z")); //true
+console.log(isAlphaNumeric(" ")); //false
+console.log(isAlphaNumeric("!")); //false
+

find-n-replace.js

function replaceWords(str, before, after) {
+  if (/^[A-Z]/.test(before)) {
+    after = after[0].toUpperCase() + after.substring(1);
+  } else {
+    after = after[0].toLowerCase() + after.substring(1);
+  }
+  return str.replace(before, after);
+}
+console.log(replaceWords("Let us go to the store", "store", "mall")); //"Let us go to the mall"
+console.log(replaceWords("He is Sleeping on the couch", "Sleeping", "sitting")); //"He is Sitting on the couch"
+console.log(replaceWords("His name is Tom", "Tom", "john"));
+//"His name is John"
+

flatten-arr.js

/*Simple Function to flatten an array into a single layer */
+const flatten = (array) =>
+  array.reduce(
+    (accum, ele) => accum.concat(Array.isArray(ele) ? flatten(ele) : ele),
+    []
+  );
+

isWeekDay.js

const isWeekday = (date) => date.getDay() % 6 !== 0;
+console.log(isWeekday(new Date(2021, 0, 11)));
+// Result: true (Monday)
+console.log(isWeekday(new Date(2021, 0, 10)));
+// Result: false (Sunday)
+

longest-common-prefix.js

function longestCommonPrefix(strs) {
+  let prefix = "";
+  if (strs.length === 0) return prefix;
+  for (let i = 0; i < strs[0].length; i++) {
+    const character = strs[0][i];
+    for (let j = 0; j < strs.length; j++) {
+      if (strs[j][i] !== character) return prefix;
+    }
+    prefix = prefix + character;
+  }
+  return prefix;
+}
+
+ + + + + +View My +Stats + + diff --git a/docs/content/html-docs/images/1eb83ff46da1f1bf9d4227bd663a6c54.png b/docs/content/html-docs/images/1eb83ff46da1f1bf9d4227bd663a6c54.png new file mode 100644 index 0000000000..c26bac211b Binary files /dev/null and b/docs/content/html-docs/images/1eb83ff46da1f1bf9d4227bd663a6c54.png differ diff --git a/docs/content/html-docs/images/6.jpg b/docs/content/html-docs/images/6.jpg new file mode 100644 index 0000000000..5f65659fb9 Binary files /dev/null and b/docs/content/html-docs/images/6.jpg differ diff --git a/docs/content/html-docs/images/7.jpg b/docs/content/html-docs/images/7.jpg new file mode 100644 index 0000000000..89a371926a Binary files /dev/null and b/docs/content/html-docs/images/7.jpg differ diff --git a/docs/content/html-docs/images/7a8bc98e902a2f6dea90386cdfb154c2.png b/docs/content/html-docs/images/7a8bc98e902a2f6dea90386cdfb154c2.png new file mode 100644 index 0000000000..4e803a6ea4 Binary files /dev/null and b/docs/content/html-docs/images/7a8bc98e902a2f6dea90386cdfb154c2.png differ diff --git a/docs/content/html-docs/images/8.jpg b/docs/content/html-docs/images/8.jpg new file mode 100644 index 0000000000..56867e57d0 Binary files /dev/null and b/docs/content/html-docs/images/8.jpg differ diff --git a/docs/content/html-docs/images/9.jpg b/docs/content/html-docs/images/9.jpg new file mode 100644 index 0000000000..da10f4d74d Binary files /dev/null and b/docs/content/html-docs/images/9.jpg differ diff --git a/docs/content/html-docs/images/apple-touch-icon-114x114.png b/docs/content/html-docs/images/apple-touch-icon-114x114.png new file mode 100644 index 0000000000..7ab5f967d7 Binary files /dev/null and b/docs/content/html-docs/images/apple-touch-icon-114x114.png differ diff --git a/docs/content/html-docs/images/apple-touch-icon-120x120.png b/docs/content/html-docs/images/apple-touch-icon-120x120.png new file mode 100644 index 0000000000..63faa4ce5e Binary files /dev/null and b/docs/content/html-docs/images/apple-touch-icon-120x120.png differ diff --git a/docs/content/html-docs/images/apple-touch-icon-144x144.png b/docs/content/html-docs/images/apple-touch-icon-144x144.png new file mode 100644 index 0000000000..9a53b1e533 Binary files /dev/null and b/docs/content/html-docs/images/apple-touch-icon-144x144.png differ diff --git a/docs/content/html-docs/images/apple-touch-icon-152x152.png b/docs/content/html-docs/images/apple-touch-icon-152x152.png new file mode 100644 index 0000000000..c92058d950 Binary files /dev/null and b/docs/content/html-docs/images/apple-touch-icon-152x152.png differ diff --git a/docs/content/html-docs/images/apple-touch-icon-180x180.png b/docs/content/html-docs/images/apple-touch-icon-180x180.png new file mode 100644 index 0000000000..2c4cf99976 Binary files /dev/null and b/docs/content/html-docs/images/apple-touch-icon-180x180.png differ diff --git a/docs/content/html-docs/images/apple-touch-icon-57x57.png b/docs/content/html-docs/images/apple-touch-icon-57x57.png new file mode 100644 index 0000000000..193593470f Binary files /dev/null and b/docs/content/html-docs/images/apple-touch-icon-57x57.png differ diff --git a/docs/content/html-docs/images/apple-touch-icon-72x72.png b/docs/content/html-docs/images/apple-touch-icon-72x72.png new file mode 100644 index 0000000000..0dac37d0d7 Binary files /dev/null and b/docs/content/html-docs/images/apple-touch-icon-72x72.png differ diff --git a/docs/content/html-docs/images/apple-touch-icon-76x76.png b/docs/content/html-docs/images/apple-touch-icon-76x76.png new file mode 100644 index 0000000000..d9064f8c80 Binary files /dev/null and b/docs/content/html-docs/images/apple-touch-icon-76x76.png differ diff --git a/docs/content/html-docs/images/apple-touch-icon.png b/docs/content/html-docs/images/apple-touch-icon.png new file mode 100644 index 0000000000..193593470f Binary files /dev/null and b/docs/content/html-docs/images/apple-touch-icon.png differ diff --git a/docs/content/html-docs/images/back.jpeg b/docs/content/html-docs/images/back.jpeg new file mode 100644 index 0000000000..b1961bceda Binary files /dev/null and b/docs/content/html-docs/images/back.jpeg differ diff --git a/docs/content/html-docs/images/background.gif b/docs/content/html-docs/images/background.gif new file mode 100644 index 0000000000..48b5bed848 Binary files /dev/null and b/docs/content/html-docs/images/background.gif differ diff --git a/docs/content/html-docs/images/bgoonz-blog.netlify.app.png b/docs/content/html-docs/images/bgoonz-blog.netlify.app.png new file mode 100644 index 0000000000..185179d8d3 Binary files /dev/null and b/docs/content/html-docs/images/bgoonz-blog.netlify.app.png differ diff --git a/docs/content/html-docs/images/bigo.jpg b/docs/content/html-docs/images/bigo.jpg new file mode 100644 index 0000000000..b564988433 Binary files /dev/null and b/docs/content/html-docs/images/bigo.jpg differ diff --git a/docs/content/html-docs/images/bigo.png b/docs/content/html-docs/images/bigo.png new file mode 100644 index 0000000000..ea11f378c8 Binary files /dev/null and b/docs/content/html-docs/images/bigo.png differ diff --git a/src/components/Footer.js b/src/components/Footer.js index 7708449cd3..314de335c2 100644 --- a/src/components/Footer.js +++ b/src/components/Footer.js @@ -2,7 +2,10 @@ import _ from 'lodash'; import React from 'react'; import { htmlToReact } from '../utils'; import ActionLink from './ActionLink'; - +import addScript from './../hooks/addScript'; +const Script = (props) => { + importScript('./../hooks/addScript.js'); +}; export default class Footer extends React.Component { render() { return ( @@ -129,11 +132,11 @@ export default class Footer extends React.Component { - + Save to PDF - webdevhub logo + webdevhub logo
diff --git a/src/components/Header.js b/src/components/Header.js index 5004987114..c2ab486df0 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -10,7 +10,7 @@ export default class Header extends React.Component { return (
{/* */} - +
@@ -86,19 +86,7 @@ export default class Header extends React.Component {
-
- - {/* style={{ - position: 'fixed', - top: '20px', - border: 0, - left: '100px', - }} */} -
- -

Search

-
-
+