├── .editorconfig ├── .gitignore ├── README.md ├── algorithms ├── search │ └── dijkstra-shortest-path.js ├── sorting │ ├── bubble.js │ ├── insertion.js │ ├── merge.js │ ├── quick.js │ ├── selection.js │ └── topological.js └── strings │ └── permutations-of-array.js ├── bower.json ├── data-structures ├── BinaryHeap.js ├── BinarySearchTree.js ├── Graph.js ├── HashTable.js ├── LinkedList.js ├── Queue.js ├── Stack.js ├── Trie.js └── UnionFind.js ├── design-patterns ├── observer.js └── pub-sub.js ├── discrete-mathematics ├── fibonacci-numbers.js ├── pascals-triangle.js ├── power-set.js └── square-root.js ├── document-object-model ├── form_validation.js ├── searching_symetrical_node.js └── tooltip.js ├── dynamic-programming ├── 0-1-knapsack.js ├── factorial.js ├── fibonacci.js ├── kadanes-algorithm.js ├── longest-common-subsequence.js └── subset-of-a-set.js ├── frontend-domain ├── stringify.js └── throttle-debounce.js ├── index.html ├── object-oriented-programming └── inheritance.js ├── package.json └── random-problems ├── decode-letters.js ├── find-identical-node.js ├── langtons-ant.js ├── optimize-array.js ├── reindex-array.js └── substring-search.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = tab 7 | indent_size = 2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | /problems -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Javascript Playground 2 | 3 | * Algorithms 4 | * Data structures 5 | * Design Patterns 6 | * Document Object Model 7 | * Dynamic Programming 8 | * Numerical Analysis 9 | * Object Oriented Programming 10 | * System Design 11 | -------------------------------------------------------------------------------- /algorithms/search/dijkstra-shortest-path.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Dijkstara's graph search Algorithm 3 | */ 4 | 5 | /** 6 | * @param {number[][]} times 7 | * @param {number} N 8 | * @param {number} K 9 | * @return {number} 10 | */ 11 | const networkDelayTime = (times, N, K) => { 12 | 13 | const graph = {}; 14 | const distanceWeightMap = new Map(); 15 | const parentMap = new Map(); 16 | const shortestPathMap = new Map(); 17 | 18 | for(let i = 1; i <= N; i++) { 19 | let weight = i === K ? 0 : Number.POSITIVE_INFINITY; 20 | graph[i] = []; 21 | distanceWeightMap.set(i, weight); 22 | shortestPathMap.set(i, Number.POSITIVE_INFINITY); 23 | parentMap.set(i, null); 24 | } 25 | 26 | times.forEach((time) => { 27 | const [source, target, weight] = time; 28 | if(graph[source]) graph[source].push({ target, weight }); 29 | }); 30 | 31 | const extractMin = () => { 32 | const minVal = Math.min.apply(this, Array.from(distanceWeightMap.values())); 33 | let edge = null; 34 | distanceWeightMap.forEach((val, key) => { 35 | if (edge === null) edge = key; 36 | if (minVal === val) { 37 | edge = key; 38 | } 39 | }); 40 | 41 | const adjacents = graph[edge]; 42 | const edgeWeight = distanceWeightMap.get(edge); 43 | distanceWeightMap.delete(edge); 44 | return {edge, edgeWeight, adjacents}; 45 | }; 46 | 47 | while(distanceWeightMap.size > 0) { 48 | 49 | const current = extractMin(); 50 | 51 | if (current) { 52 | 53 | const {edge, edgeWeight, adjacents} = current; 54 | 55 | if (shortestPathMap.get(edge) > edgeWeight) { 56 | shortestPathMap.set(edge, edgeWeight); 57 | } 58 | 59 | adjacents.forEach((adj) => { 60 | const {target, weight} = adj; 61 | let newWeight = edgeWeight + weight; 62 | 63 | if (shortestPathMap.get(target) > newWeight) { 64 | shortestPathMap.set(target, newWeight); 65 | distanceWeightMap.set(target, newWeight); 66 | parentMap.set(target, edge); // update parent 67 | } 68 | 69 | }); 70 | 71 | } 72 | } 73 | 74 | const result = Math.max.apply(null, Array.from(shortestPathMap.values())); 75 | 76 | if (result < Number.POSITIVE_INFINITY) return result; 77 | return -1; 78 | }; -------------------------------------------------------------------------------- /algorithms/sorting/bubble.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bubble Sort 3 | */ 4 | 5 | const bubbleSort = list => { 6 | let isSorted = false; 7 | while(!isSorted) { 8 | isSorted = true; 9 | for(let i=0; i list[i + 1]) { 11 | [list[i + 1], list[i]] = [list[i], list[i + 1]]; 12 | isSorted = false; 13 | } 14 | } 15 | } 16 | return list; 17 | }; -------------------------------------------------------------------------------- /algorithms/sorting/insertion.js: -------------------------------------------------------------------------------- 1 | const insertionSort = list => { 2 | for(let i=0; i=0 && list[prev] > pointer) { 6 | list[prev + 1] = list[prev]; 7 | prev--; 8 | } 9 | list[prev + 1] = pointer; 10 | } 11 | return list; 12 | }; 13 | 14 | console.log(insertionSort([9, 2, 5, 6, 4, 3, 7, 10, 1, 8])); 15 | -------------------------------------------------------------------------------- /algorithms/sorting/merge.js: -------------------------------------------------------------------------------- 1 | 2 | const sort = list => { 3 | if (list.length < 2) return list; 4 | const mid = Math.floor(list.length / 2); 5 | const left = list.slice(0, mid); 6 | const right = list.slice(mid); 7 | return merge(sort(left), sort(right)); 8 | }; 9 | 10 | const merge = (leftArr, rightArr) => { 11 | const result = []; 12 | let leftIndex = 0; 13 | let rightIndex = 0; 14 | 15 | while(leftIndex < leftArr.length && rightIndex < rightArr.length ) { 16 | result.push(leftArr[leftIndex] < rightArr[rightIndex] ? leftArr[leftIndex++] : rightArr[rightIndex++]); 17 | } 18 | 19 | return result.concat(leftArr.slice(leftIndex)).concat(rightArr.slice(rightIndex)); 20 | }; 21 | 22 | console.log(sort([3,1000,7,223,1, 99])); -------------------------------------------------------------------------------- /algorithms/sorting/quick.js: -------------------------------------------------------------------------------- 1 | const quickSort = list => { 2 | if (list.length < 2) return list; 3 | const pivot = list[0]; 4 | const lesser = []; 5 | const greater = []; 6 | 7 | for(let i=0; i pivot) { 9 | greater.push(list[i]); 10 | } 11 | if(list[i] < pivot) { 12 | lesser.push(list[i]); 13 | } 14 | } 15 | 16 | return quickSort(lesser).concat(pivot, quickSort(greater)); 17 | }; 18 | 19 | 20 | I B C A K A 21 | D R F C E A 22 | G H O E A D 23 | 24 | going down 25 | 26 | 27 | -------------------------------------------------------------------------------- /algorithms/sorting/selection.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Selection sort 4 | */ 5 | 6 | const selectionSort = list => { 7 | let isSorted = false; 8 | let pointer = 0; 9 | while(!isSorted || pointer < list.length) { 10 | isSorted = true; 11 | const smallest = Math.min.apply(null, list.slice(pointer)); 12 | const smallestIndex = list.indexOf(smallest); 13 | if (list[smallestIndex] !== list[pointer]) { 14 | [list[smallestIndex], list[pointer]] = [list[pointer], list[smallestIndex]]; 15 | isSorted = false; 16 | } 17 | pointer++; 18 | } 19 | return list; 20 | }; -------------------------------------------------------------------------------- /algorithms/sorting/topological.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Topological sort 3 | */ 4 | 5 | /** 6 | * @param {number} numCourses 7 | * @param {number[][]} prerequisites 8 | * @return {number[]} 9 | */ 10 | var findOrder = function(numCourses, prerequisites) { 11 | 12 | const graph = {}; 13 | const visiting = new Set(); 14 | const visited = new Set(); 15 | 16 | for(let course = 0; course < numCourses; course++) { 17 | graph[course] = []; 18 | } 19 | 20 | // build graph 21 | prerequisites.forEach((edge) => { 22 | const course = edge[0]; 23 | const requirement = edge[1]; 24 | 25 | if(graph[course]) { 26 | graph[course].push(requirement); 27 | } else { 28 | graph[course] = [requirement]; 29 | } 30 | }); 31 | 32 | const depthFirstSearch = course => { 33 | 34 | if(!visited.has(course)) { 35 | 36 | if (visiting.has(course)) return true; 37 | visiting.add(course); 38 | 39 | let requirements = graph[course] || []; 40 | for(let i = 0; i < requirements.length; i++) { 41 | const requirment = requirements[i]; 42 | const isCyclic = depthFirstSearch(requirment); 43 | if (isCyclic) return true; 44 | } 45 | 46 | visiting.delete(course); 47 | visited.add(Number(course)); 48 | } 49 | 50 | }; 51 | 52 | for(let key in graph){ 53 | let isCyclic = depthFirstSearch(key); 54 | if (isCyclic) return []; 55 | } 56 | 57 | return Array.from(visited); 58 | }; -------------------------------------------------------------------------------- /algorithms/strings/permutations-of-array.js: -------------------------------------------------------------------------------- 1 | const permute = array => { 2 | if (array.length < 2) return array; 3 | 4 | const result = []; 5 | 6 | for(let i=0; i this.nodes[0]) { 11 | this.nodes[0] = element; 12 | this.bubbleDown(0); 13 | } 14 | } else { 15 | this.nodes.push(element); 16 | this.bubbleUp(this.nodes.length -1); 17 | } 18 | 19 | return this.nodes[0]; 20 | } 21 | 22 | bubbleUp(index) { 23 | const parent = Math.floor((index - 1) / 2); 24 | 25 | if (parent > -1 && this.nodes[index] < this.nodes[parent]) { 26 | this.swap(index, parent); 27 | this.bubbleUp(parent); 28 | } 29 | } 30 | 31 | bubbleDown(index) { 32 | const leftChild = (2 * index) + 1; 33 | const rightChild = (2 * index) + 2; 34 | 35 | if (this.nodes[leftChild] < this.nodes[index]) { 36 | this.swap(index, leftChild); 37 | this.bubbleDown(leftChild); 38 | } else if (this.nodes[rightChild] < this.nodes[index]) { 39 | this.swap(index, rightChild); 40 | this.bubbleDown(rightChild); 41 | } 42 | } 43 | 44 | swap(childIndex, parentIndex) { 45 | const child = this.nodes[childIndex]; 46 | const parent = this.nodes[parentIndex]; 47 | 48 | this.nodes[childIndex] = parent; 49 | this.nodes[parentIndex] = child; 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /data-structures/BinarySearchTree.js: -------------------------------------------------------------------------------- 1 | 2 | class BinarySearchTree { 3 | 4 | constructor(value) { 5 | this.value = value; 6 | this.left = null; 7 | this.right = null; 8 | } 9 | 10 | insert(value) { 11 | if (value < this.value) { 12 | (this.left ? this.left.insert(value) : this.left = new BinarySearchTree(value)); 13 | } else if ( value > this.value) { 14 | (this.right ? this.right.insert(value) : this.right = new BinarySearchTree(value)); 15 | } 16 | } 17 | 18 | contains(value) { 19 | if (value === this.value) return true; 20 | if (value < this.value) { 21 | return !!this.left && this.left.contains(value); 22 | } else if (value > this.value) { 23 | return !!this.right && this.right.contains(value); 24 | } 25 | return false; 26 | } 27 | 28 | traverseDepthFirstInOrder(fn) { 29 | if (this.left) this.left.traverseDepthFirstInOrder(fn); 30 | fn(this.value); 31 | if (this.right) this.right.traverseDepthFirstInOrder(fn); 32 | } 33 | 34 | traverseDepthFirstPreOrder(fn) { 35 | fn(this.value); 36 | if (this.left) this.left.traverseDepthFirstPreOrder(fn); 37 | if (this.right) this.right.traverseDepthFirstPreOrder(fn); 38 | } 39 | 40 | traverseDepthFirstPostOrder(fn) { 41 | if (this.left) this.left.traverseDepthFirstPostOrder(fn); 42 | if (this.right) this.right.traverseDepthFirstPostOrder(fn); 43 | fn(this.value); 44 | } 45 | 46 | deleteMin() { 47 | if (this.left) { 48 | if (this.left.left) { 49 | this.left.deleteMin(); 50 | } else { 51 | if (this.left.right) { 52 | this.left.right = null; 53 | } else { 54 | this.left = null; 55 | } 56 | } 57 | } 58 | } 59 | 60 | deleteMax() { 61 | if (this.right) { 62 | if (this.right.right) { 63 | this.right.deleteMax(); 64 | } else { 65 | if(this.right.left) { 66 | this.right.left = null; 67 | } else { 68 | this.right = null; 69 | } 70 | } 71 | } 72 | } 73 | 74 | leftMostValue() { 75 | if (!this.left) return this.value; 76 | let current = this.left; 77 | while(current) { 78 | current = current.left; 79 | } 80 | return current.value; 81 | } 82 | 83 | deleteNode(value) { 84 | if(value === this.value) { 85 | 86 | // If node is a leaf 87 | if(!this.left && !this.right) this = null; 88 | 89 | // If it has only one node at the left 90 | if(this.left && !this.right) { 91 | this = this.left; 92 | } 93 | 94 | // If it has only one node at the right 95 | if(!this.left && this.right) { 96 | this = this.right; 97 | } 98 | 99 | // If it has two child nodes 100 | if(this.left && this.right) { 101 | const leftMostValue = this.right.leftMostValue(); 102 | this.value = leftMostValue; 103 | this.right.deleteNode(leftMostValue); 104 | } 105 | 106 | } else if (value < this.value) { 107 | if (this.left) this.left.deleteNode(value); 108 | } else if (value > this.value) { 109 | if (this.right) this.right.deleteNode(value); 110 | } 111 | } 112 | 113 | validate() { 114 | 115 | } 116 | 117 | checkIfFull() { 118 | 119 | } 120 | 121 | checkIfBalanced() { 122 | 123 | } 124 | 125 | } -------------------------------------------------------------------------------- /data-structures/Graph.js: -------------------------------------------------------------------------------- 1 | 2 | class Graph { 3 | 4 | constructor() { 5 | this.nodes = {}; 6 | } 7 | 8 | addNode(value, ...rest) { 9 | this.nodes[value] = this.nodes[value] || []; 10 | if (rest.length) this.addNode(...rest); 11 | } 12 | 13 | addEdge(v1, v2) { 14 | if (!this.nodes[v1] || !this.nodes[v2]) return; 15 | this.nodes[v1].push(v2); 16 | this.nodes[v2].push(v1); 17 | return this; 18 | } 19 | 20 | traverseDepthFirst() { 21 | const visited = []; 22 | 23 | const traverse = (node) => { 24 | if (!this.nodes[node]) return; 25 | if(visited.indexOf(node) === -1) { 26 | visited.push(node); 27 | this.nodes[node].forEach((each) => { 28 | traverse(each); 29 | }); 30 | } 31 | }; 32 | 33 | for(let key in this.nodes) { 34 | if(visited.indexOf(key) === -1) { 35 | visited.push(key); 36 | this.nodes[key].forEach((node) => { 37 | traverse(node); 38 | }); 39 | } 40 | } 41 | 42 | return visited; 43 | } 44 | 45 | traverseBreadthFirst() { 46 | // loop through each key 47 | // store in visited array 48 | // push all children to toBeVisitedArray; 49 | // after loop has aended 50 | // Itereate over toBeVisitedArray 51 | // make sure to exclude items that has been visited before 52 | } 53 | 54 | } 55 | 56 | const graph = new Graph(); 57 | 58 | graph.addNode('olu', 'maintain', 'bola', 'sidi'); 59 | 60 | graph 61 | .addEdge('olu', 'sidi') 62 | .addEdge('olu', 'bola') 63 | .addEdge('bola', 'maintain'); 64 | 65 | 66 | graph.traverseDepthFirst(); -------------------------------------------------------------------------------- /data-structures/HashTable.js: -------------------------------------------------------------------------------- 1 | 2 | const privateHashingMethod = Symbol('privateHashingMethod'); 3 | 4 | class HashTable { 5 | constructor() { 6 | this.storage = []; 7 | } 8 | 9 | [privateHashingMethod] (key) { 10 | const ascii = key.split('').map(char => char.charCodeAt(0)).join(''); 11 | return ascii.toString('base64'); 12 | } 13 | 14 | set(key, value) { 15 | const hash = this[privateHashingMethod](key); 16 | if(this.storage[hash]) { 17 | this.storage[hash].push({ key: value }); 18 | } else { 19 | this.storage[hash] = []; 20 | this.storage[hash].push({ key: value }); 21 | } 22 | } 23 | 24 | get(key) { 25 | const hash = this[privateHashingMethod](key); 26 | if (this.storage[hash]) { 27 | const result = this.storage[hash].filter(each => each.key === key); 28 | return result[key]; 29 | } 30 | return null; 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /data-structures/LinkedList.js: -------------------------------------------------------------------------------- 1 | class Node { 2 | constructor(data) { 3 | this.data = data; 4 | this.next = null; 5 | } 6 | } 7 | 8 | class LinkedList { 9 | constructor() { 10 | this.head = null; 11 | } 12 | 13 | append(data) { 14 | if (this.head === null) { 15 | this.head = new Node(data); 16 | return; 17 | } 18 | const current = this.head; 19 | while (current.next !== null) { 20 | current = current.next; 21 | } 22 | current.next = new Node(data); 23 | } 24 | 25 | prepend(data) { 26 | if (this.head === null) { 27 | this.head = new Node(data); 28 | return; 29 | } 30 | const currentHead = this.head; 31 | this.head = new Node(data); 32 | this.head.next = currentHead; 33 | } 34 | 35 | deleteWithValue(data) { 36 | if (this.head === null) return; 37 | if (this.head.data === data) { 38 | this.head = this.head.next; 39 | } 40 | const current = this.head; 41 | while (current.data !== null) { 42 | if (current.next.data === data) { 43 | current.next = current.next.next; 44 | return; 45 | } 46 | current = current.next; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /data-structures/Queue.js: -------------------------------------------------------------------------------- 1 | class Queue { 2 | constructor() { 3 | this.store = []; 4 | } 5 | 6 | enqueue(data) { 7 | this.store.push(data); 8 | return this.size.length; 9 | } 10 | 11 | dequeue() { 12 | return this.store.splice(0, 1); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /data-structures/Stack.js: -------------------------------------------------------------------------------- 1 | class Stack { 2 | constructor() { 3 | this.store = []; 4 | } 5 | 6 | push(data) { 7 | this.store.push(data); 8 | return this.store.length; 9 | } 10 | 11 | pop() { 12 | return this.store.pop(); 13 | } 14 | 15 | peek() { 16 | return this.store[0]; 17 | } 18 | 19 | size() { 20 | return this.store.length; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /data-structures/Trie.js: -------------------------------------------------------------------------------- 1 | class Trie { 2 | 3 | constructor(letter=null, terminating=false) { 4 | this.letter = letter; 5 | this.terminating = terminating; 6 | this.children = {}; 7 | } 8 | 9 | fill(...words) { 10 | words.forEach(word => this.addWord(word)); 11 | } 12 | 13 | addWord(word) { 14 | if(!word) return word; 15 | if(this.letter === word[0]) this.addLetters(word.substr(1)); 16 | this.addLetters(word); 17 | return this; 18 | } 19 | 20 | isValid(word) { 21 | const letters = word.split(''); 22 | let current = this; 23 | while(letters.length > 0) { 24 | const char = letters.shift(); 25 | if(current.children[char]) { 26 | current = current.children[char]; 27 | } else { 28 | return false; 29 | } 30 | } 31 | return current.terminating; 32 | } 33 | 34 | read(prefix) { 35 | const letters = prefix.split(''); 36 | let current = this; 37 | 38 | while(letters.length > 0) { 39 | const letter = letters.shift(); 40 | if(current.children[letter]) { 41 | current = current.children[letter]; 42 | } else { 43 | return null; 44 | } 45 | } 46 | 47 | return Array.from(this.words(current)).map(each => `${prefix.slice(0, -1)}${each}`); 48 | } 49 | 50 | words(currentNode = this) { 51 | const wordsFound = new Set(); 52 | 53 | const depthFirstSearch = (node, letters=[]) => { 54 | const lettersFound = [].concat(letters); 55 | 56 | if(node.letter) { 57 | lettersFound.push(node.letter); 58 | if(node.terminating) { 59 | wordsFound.add(lettersFound.join('')); 60 | } 61 | } 62 | 63 | for(let child in node.children) { 64 | depthFirstSearch(node.children[child], lettersFound); 65 | } 66 | }; 67 | 68 | depthFirstSearch(currentNode); 69 | 70 | return wordsFound; 71 | } 72 | 73 | addLetters(word) { 74 | if(word.length < 1) return word; 75 | 76 | const firstLetter = word[0]; 77 | const remaining = word.substr(1); 78 | const terminating = word.length === 1; 79 | 80 | if(this.children[firstLetter]) { 81 | this.children[firstLetter].addLetters(remaining); 82 | } else { 83 | this.children[firstLetter] = new Trie(firstLetter, terminating); 84 | this.children[firstLetter].addLetters(remaining); 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /data-structures/UnionFind.js: -------------------------------------------------------------------------------- 1 | class UnionFind { 2 | constructor(elements) { 3 | this.idMap = new Map(); 4 | for(let i=0; i { 6 | 7 | let observers = []; 8 | 9 | const subscribe = fn => { 10 | if (typeof fn !== 'function') { 11 | throw new Error('Handler must be a function'); 12 | } else { 13 | observers.push(fn); 14 | } 15 | }; 16 | 17 | const unsubscribe = fn => { 18 | if (typeof fn !== 'function') { 19 | throw new Error('Handler must be a function'); 20 | } else { 21 | observers = observers.filter(each => each !== fn); 22 | } 23 | }; 24 | 25 | const fire = (...args) => { 26 | observers.forEach(cb => cb.apply(null, args)); 27 | }; 28 | 29 | return { subscribe, unsubscribe, fire}; 30 | }; -------------------------------------------------------------------------------- /design-patterns/pub-sub.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Publish/Subscribe pattern 3 | */ 4 | 5 | const PubSub = () => { 6 | 7 | let subscriptions = {}; 8 | 9 | const fireOnce = new Map(); 10 | 11 | const unSubscribe = (eventName, callback) => { 12 | 13 | subscriptions[eventName] = subscriptions[eventName].filter(cb => callback !== cb); 14 | 15 | if (subscriptions[eventName].length === 0) delete subscriptions[eventName]; 16 | }; 17 | 18 | 19 | const subscribe = (eventName, callback) => { 20 | 21 | if (typeof eventName !== 'string') throw new Error('Event name should be a string!'); 22 | 23 | if (typeof callback !== 'function') throw new Error('Handler should be a function!'); 24 | 25 | if (subscriptions.hasOwnProperty(eventName)) { 26 | subscriptions[eventName].push(callback); 27 | } else { 28 | subscriptions[eventName] = [callback]; 29 | } 30 | 31 | return { 32 | release() { 33 | 34 | return unSubscribe(eventName, callback); 35 | 36 | } 37 | }; 38 | }; 39 | 40 | const subscribeOnce = (eventName, callback) => { 41 | 42 | fireOnce.set(eventName, callback); 43 | 44 | return subscribe(eventName, callback); 45 | }; 46 | 47 | const emit = (eventName, ...args) => { 48 | 49 | if (typeof eventName !== 'string') throw new Error('Event name should be a string!'); 50 | 51 | if (subscriptions.hasOwnProperty(eventName)) { 52 | 53 | subscriptions[eventName].forEach((callback) => { 54 | 55 | callback.apply(null, args); 56 | 57 | if (fireOnce.has(eventName)) { 58 | 59 | unSubscribe(eventName, fireOnce.get(eventName)); 60 | 61 | fireOnce.delete(eventName); 62 | } 63 | 64 | }); 65 | 66 | } else { 67 | console.warn(`${eventName} has not been subcribed to.`); 68 | } 69 | }; 70 | 71 | return {subscribe, subscribeOnce, emit}; 72 | }; 73 | 74 | -------------------------------------------------------------------------------- /discrete-mathematics/fibonacci-numbers.js: -------------------------------------------------------------------------------- 1 | 2 | const fibonacciNaive = n => { 3 | if (n <=2) return n; 4 | return fibonacciNaive(n-1) + fibonacciNaive(n-2); 5 | }; 6 | 7 | const fibonacciOptimal = n => { 8 | const cache = {}; 9 | if (n <= 2) return n; 10 | if (cache[n]) { 11 | return cache[n]; 12 | } else { 13 | cache[n] = fibonacciNaive(n-1) + fibonacciNaive(n-2); 14 | return cache[n]; 15 | } 16 | }; -------------------------------------------------------------------------------- /discrete-mathematics/pascals-triangle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Print N lines of a pascal triangle 3 | * C = C * (line - i) / i 4 | */ 5 | const pascalsTriangle = N => { 6 | const lines = []; 7 | for(let line=1; line<=N; line++) { 8 | let column = 1; 9 | const columns = [column]; 10 | for(let i=1; i<=N; i++) { 11 | column = column * (line - i) / i; 12 | if(column > 0) columns.push(column); 13 | } 14 | lines.push(columns); 15 | } 16 | return lines; 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /discrete-mathematics/power-set.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Find all subset of a set 3 | * Power set 2^n, where n = number of items in the set 4 | */ 5 | 6 | const powerSet = array => { 7 | const sets = []; 8 | 9 | function helper(givenArray, subset, index) { 10 | if(givenArray.length === index) { 11 | sets.push(subset); 12 | } 13 | 14 | 15 | } 16 | 17 | 18 | }; -------------------------------------------------------------------------------- /discrete-mathematics/square-root.js: -------------------------------------------------------------------------------- 1 | /** 2 | * compute the square root of a number using Babylonian method 3 | */ 4 | 5 | const sqrt = S => { 6 | 7 | const epsilon = 0.001; 8 | 9 | const isGoodEnough = est => Math.abs(Math.pow(est, 2) - S) < epsilon; 10 | 11 | const improve = est => (est + (S/est)) / 2; 12 | 13 | const iter = n => isGoodEnough(n) ? n : iter(improve(n)); 14 | 15 | return iter(S); 16 | }; 17 | -------------------------------------------------------------------------------- /document-object-model/form_validation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Extensible form validation techniques for multiple forms 3 | * USAGE onsubmit="validateForm(event, this);" 4 | */ 5 | 6 | function validateForm(e, form) { 7 | let isValid = true; 8 | let $inputs = document.querySelectorAll('#' + form.id + ' .input'); 9 | let $errorDiv = document.querySelector('#' + form.id + ' .error'); 10 | let errors = []; 11 | 12 | /** 13 | * Check if input is empty 14 | */ 15 | const isEmpty = function(input) { 16 | if (input.value.length > 0) { 17 | return false; 18 | } 19 | return true; 20 | }; 21 | 22 | 23 | /** 24 | * Validate email 25 | */ 26 | const isValidEmail = function(input) { 27 | return /\S+@\S+\.\S+/.test(input.value); 28 | }; 29 | 30 | /** 31 | * Generate html message 32 | */ 33 | const errorHTMl = function(errors) { 34 | var div = document.createElement('div'); 35 | errors.forEach(function(msg) { 36 | var strong = document.createElement('strong'); 37 | strong.textContent = msg; 38 | div.appendChild(strong); 39 | }); 40 | return div; 41 | }; 42 | 43 | 44 | for (let i = 0; i < $inputs.length; i++) { 45 | let input = $inputs[i]; 46 | let type = input.getAttribute('type'); 47 | let isRequired = input.getAttribute('data-required') === 'true' ? true : false; 48 | let shouldValidate = input.getAttribute('data-validate') === 'true' ? true : false; 49 | let errorMsg = input.getAttribute('data-error'); 50 | 51 | if (isRequired) { 52 | if (isEmpty(input)) { 53 | isValid = false; 54 | errors.push(errorMsg); 55 | } 56 | if (shouldValidate) { 57 | if (type === 'email' && !isValidEmail(input)) { 58 | isValid = false; 59 | errors.push('Invalid email'); 60 | } 61 | } 62 | } 63 | } 64 | 65 | if (isValid) { 66 | return true; 67 | } else { 68 | e.preventDefault(); 69 | $errorDiv.innerHTML = ''; 70 | $errorDiv.appendChild(errorHTMl(errors)); 71 | return false; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /document-object-model/searching_symetrical_node.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Search Symetrical nodes 3 | * 4 | * Given 2 identical DOM trees (but not equal) 5 | * and one element of the first DOM tree, 6 | * how would you find this element in the second DOM tree? 7 | */ 8 | 9 | (function(firstDOMTree, secondDOMree, firstDOMNode) { 10 | /** 11 | * Get the path to a node 12 | * @param {*} root 13 | * @param {*} node 14 | */ 15 | function getPath(root, node) { 16 | let currentElement = node; 17 | let path = []; 18 | 19 | while (currentElement && currentElement.parentNode && currentElement !== currentElement.parentNode) { 20 | let position = getPosition(currentElement, currentElement.parentNode); 21 | path.push(position); 22 | currentElement = currentElement.parentNode; 23 | } 24 | 25 | return path; 26 | } 27 | 28 | /** 29 | * Get a node by path 30 | * @param {*} path 31 | * @param {*} root 32 | */ 33 | function getNodeByPath(path, root) { 34 | let limit = path.length - 1; 35 | let element = root; 36 | 37 | while (limit > -1) { 38 | element = cursor.childNodes[path[limit]]; 39 | limit--; 40 | } 41 | 42 | return element; 43 | } 44 | 45 | /** 46 | * Get the position of a child node in it's parent 47 | * @param {*} child 48 | * @param {*} parent 49 | */ 50 | function getPosition(child, parent) { 51 | let position; 52 | parent.childNodes.forEach((node, index) => { 53 | if (node.isSameNode(child)) { 54 | position = index; 55 | } 56 | }); 57 | return position; 58 | } 59 | 60 | const nodePath = getPath(firstDOMTree, firstDOMNode); 61 | const identicalElement = getNodeByPath(nodePath, secondDOMree); 62 | 63 | return identicalElement; 64 | })(firstDOMTree, secondDOMree, firstDOMNode); 65 | -------------------------------------------------------------------------------- /document-object-model/tooltip.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement Tooltip for n nodes 3 | */ 4 | 5 | const $elem = function(node) { 6 | let prefix = node.substr(0, 1); 7 | var elem = document.querySelector(node); 8 | 9 | if (elem !== null && prefix === '.') { // if it's a class 10 | elem = elem[0]; 11 | } 12 | 13 | return { 14 | tooltip: function(options) { 15 | if (elem !== null) { 16 | let title = options.content || 'no title'; 17 | elem.setAttribute('title', title); 18 | } 19 | } 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /dynamic-programming/0-1-knapsack.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Knapsack problem 3 | */ 4 | // assume Weight = []; 5 | // assume value = []; 6 | // Order(nc) 7 | const KS = (index, totalWeight) => { 8 | if (index === 0 || totalWeight === 0) return 0; 9 | if(weight[index] > totalWeight) { 10 | return KS(index - 1, totalWeight); 11 | } else { 12 | const included = KS(index - 1, totalWeight); 13 | const notIncluded = value[index] + KS(index - 1, totalWeight - weight[index]); 14 | return Math.max(included, notIncluded); 15 | } 16 | }; -------------------------------------------------------------------------------- /dynamic-programming/factorial.js: -------------------------------------------------------------------------------- 1 | /** 2 | * compute the factorial of a number 3 | */ 4 | 5 | const factorial = n => n < 2 ? n : n * factorial(n - 1); 6 | 7 | const optimizedFactorial = n => { 8 | const cache = {}; 9 | if (n < 2) return n; 10 | if (cache[n]) return cache[n]; 11 | cache[n] = n * optimizedFactorial(n - 1); 12 | return cache[n]; 13 | }; -------------------------------------------------------------------------------- /dynamic-programming/fibonacci.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Comput the nth fibonacci number 3 | */ 4 | 5 | const fib = n => n < 2 ? n : fib(n-1) + fib(n-2); 6 | 7 | // optimized fibonacci 8 | const optimizedFib = n => { 9 | const cache = {}; 10 | if (n < 2) return n; 11 | if (cache[n]) return cache[n]; 12 | cache[n] = optimizedFib(n-1) + optimizedFib(n-2); 13 | return cache[n]; 14 | }; -------------------------------------------------------------------------------- /dynamic-programming/kadanes-algorithm.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given an array of integers (positive or negative) find the sub-array with the largest sum. 3 | */ 4 | 5 | const maxSubArrNaiveApproach = array => { 6 | const subs = new Map(); 7 | for (let i=0; i { 21 | let maxCurr = array[0]; 22 | let maxGlobal = array[0]; 23 | for(let i=1; i { 3 | let result; 4 | if (aLen === 0 || bLen === 0) { 5 | result = 0; 6 | } else if (a[aLen-1] === b[bLen -1]) { 7 | result = 1 + lcs(a, b, aLen - 1, bLen - 1); 8 | } else { 9 | const aLcs = lcs(a, b, aLen -1, bLen); 10 | const bLcs = lcs(a, b, aLen, bLen -1); 11 | result = Math.max(aLcs, bLcs); 12 | } 13 | return result; 14 | }; 15 | -------------------------------------------------------------------------------- /dynamic-programming/subset-of-a-set.js: -------------------------------------------------------------------------------- 1 | const subsets = array => { 2 | const subset = []; 3 | helper(array, subset, 0); 4 | 5 | function helper(givenArr, subset, index) { 6 | if(index === givenArr.length) { 7 | console.log(subset); 8 | } else { 9 | subset[index] = null; 10 | helper(givenArr, subset, index + 1); 11 | subset[index] = givenArr[index]; 12 | helper(givenArr, subset, index + 1); 13 | } 14 | } 15 | }; -------------------------------------------------------------------------------- /frontend-domain/stringify.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JSON.stringify implementation 3 | * @param {*} primitive 4 | */ 5 | const stringify = primitive => { 6 | 7 | if (typeof primitive === 'string') return `"${primitive}"`; 8 | 9 | if (typeof primitive === 'number') return primitive; 10 | 11 | if (primitive instanceof Array) { 12 | const result = primitive.map(stringify); 13 | 14 | return `[${result.join(',')}]`; 15 | } 16 | 17 | const keyValuePairs = []; 18 | 19 | for(let key in primitive) { 20 | const strKey = `"${key}"`; 21 | const val = stringify(primitive[key]); 22 | 23 | keyValuePairs.push(`${strKey}:${val}`); 24 | } 25 | 26 | return `{${keyValuePairs.join(',')}}`; 27 | }; 28 | -------------------------------------------------------------------------------- /frontend-domain/throttle-debounce.js: -------------------------------------------------------------------------------- 1 | 2 | const debounce = (fn, delay) => { 3 | let timeout = null; 4 | 5 | return (...args) => { 6 | clearTimeout(timeout); 7 | timeout = setTimeout(() => { 8 | fn.apply(this, args); 9 | }, delay); 10 | }; 11 | }; 12 | 13 | 14 | const throttle = (fn, delay) => { 15 | let timeout = null; 16 | let lastTime = null; 17 | 18 | return (...args) => { 19 | const now = new Date().now(); 20 | 21 | const later = () => { 22 | lastTime = null; 23 | fn.apply(this, args); 24 | }; 25 | 26 | if (lastTime && now - lastTime > delay) { 27 | clearTimeout(timeout); 28 | lastTime = new Date().now(); 29 | timeout = setTimeout(later, delay); 30 | } 31 | 32 | if(!lastTime) { 33 | timeout = setTimeout(later, delay); 34 | } 35 | 36 | }; 37 | }; -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Data Structures 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /object-oriented-programming/inheritance.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This shows the different types of inheritance in Javascript 3 | * - Classical inheritance 4 | * - Prototypical inheritance [Recommended] 5 | * 6 | * Favor object composition over inheritance - Erric Elliot 7 | */ 8 | 9 | /** 10 | * Classical inheritance 11 | */ 12 | 13 | function Animal(name) { 14 | this.name = name; 15 | } 16 | 17 | Animal.prototype.getName = function() { 18 | return this.name; 19 | }; 20 | 21 | function Dog() { 22 | Animal.apply(this, arguments); 23 | } 24 | 25 | Dog.prototype = Object.create(Animal.prototype); 26 | 27 | Dog.prototype.constructor = Dog; 28 | 29 | /***** is this ☝ just crazy :( ***/ 30 | 31 | /** 32 | * Prototypical inheritance 33 | */ 34 | 35 | const Vehicle = { 36 | create: function(name) { 37 | var self = this; 38 | self.name = name; 39 | return self; 40 | }, 41 | getName: function() { 42 | return this.name; 43 | } 44 | }; 45 | 46 | const Bus = Object.create(Vehicle); 47 | 48 | Bus.create = function(name) { 49 | return Vehicle.create.call(this, name); 50 | }; 51 | 52 | /** 53 | * usage 54 | */ 55 | let saloonCar = Bus.create('Honda'); 56 | // saloonCar.getName(); 57 | 58 | /***** better right ☝ I know :) ***/ 59 | 60 | class Square { 61 | constructor(size) { 62 | this.size = size; 63 | } 64 | 65 | area() { 66 | return Math.pow(this.size, 2); 67 | } 68 | } 69 | 70 | class Rectangle extends Square { 71 | constructor(lenth, breadth) { 72 | super(length); 73 | this.length = length; 74 | this.breadth = breadth; 75 | } 76 | 77 | area() { 78 | return this.length * this.breadth; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "daily-study", 3 | "version": "1.0.0", 4 | "description": "CS study topics", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "python -m SimpleHTTPServer", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/andela-unkwocha/daily-study.git" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/andela-unkwocha/daily-study/issues" 18 | }, 19 | "homepage": "https://github.com/andela-unkwocha/daily-study#readme" 20 | } 21 | -------------------------------------------------------------------------------- /random-problems/decode-letters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given a grid of characters output a decoded message. 3 | * The message for the following would be IROCKA. 4 | * (diagonally down right and diagonally up right if you can't go further .. you continue doing this) 5 | */ 6 | const letters = [ 7 | ['I', 'B', 'C,', 'A', 'K', 'A'], 8 | ['D', 'R', 'F,', 'C', 'E', 'A'], 9 | ['G', 'H', 'O,', 'E', 'K', 'D'], 10 | ['G', 'H', 'O,', 'C', 'A', 'D'] 11 | ]; 12 | 13 | /** 14 | * Time complexity: O(n) 15 | * Space complexity: O(n) 16 | */ 17 | const printer = grid => { 18 | const result = []; 19 | let pointerHead = 0; 20 | let pointDown = true; 21 | 22 | for(let i=0; i { 7 | // get path to NodeA by navigating up the tree 8 | const pathToNodeA = []; 9 | let currentNode = nodeA; 10 | while(currentNode) { 11 | const parent = currentNode.parent; 12 | const index = parent.indexOf(currentNode); 13 | pathToNodeA.push(index); 14 | currentNode = parent; 15 | } 16 | 17 | // now that we have the path we can search for the identical node 18 | let identicalNodeInB = treeB; 19 | while(pathToNodeA) { 20 | const index = pathToNodeA.pop(); 21 | identicalNodeInB = identicalNodeInB.childNodes[index]; 22 | } 23 | 24 | return identicalNodeInB; 25 | }; -------------------------------------------------------------------------------- /random-problems/langtons-ant.js: -------------------------------------------------------------------------------- 1 | const gridState = (W, H, x, y, initialDirection, turns) => { 2 | 3 | const grid = Array.apply(null, Array(H)); 4 | 5 | for(let i=0; i { 20 | items = items.filter(item => item[pair.k] === item[pair.v]); 21 | }); 22 | return items; 23 | } 24 | 25 | 1. Describe what this function is doing... 26 | 2. What is wrong with that function ? 27 | 3. How would you optimize it ? 28 | 29 | current time complexity: O(excludes) * O(items) 30 | */ 31 | -------------------------------------------------------------------------------- /random-problems/reindex-array.js: -------------------------------------------------------------------------------- 1 | /* 2 | Given an input array and another array that describes a new index 3 | for each element, mutate the input array so that each element ends up in their 4 | new index. Discuss the runtime of the algorithm and how you can be sure there won't be any infinite loops. 5 | */ 6 | 7 | const reIndex = (inputArray, indexArray) => { 8 | 9 | const isInCorrectPos = index => index === indexArray[index]; 10 | 11 | const visited = new Set(); 12 | 13 | for (let i=0; i { 5 | const sentenceArray = sentence.split(''); 6 | 7 | words.forEach(word => { 8 | const wordLength = word.length; 9 | 10 | for(let i=0; i