diff --git a/DSA/Arrays/BubbleSort.js b/DSA/Arrays/BubbleSort.js index c37a9d6..2781e9c 100644 --- a/DSA/Arrays/BubbleSort.js +++ b/DSA/Arrays/BubbleSort.js @@ -9,6 +9,7 @@ if(arr[j] > arr[j+1]){ + // creates a temp array var temp = arr[j] arr[j] = arr[j + 1] arr[j+1] = temp diff --git a/DSA/Arrays/RadixSort.js b/DSA/Arrays/RadixSort.js new file mode 100644 index 0000000..e4f1b5b --- /dev/null +++ b/DSA/Arrays/RadixSort.js @@ -0,0 +1,43 @@ +const arr = [121, 432, 564, 23, 1, 45, 788]; +const countingSort = (arr, size, place) => { + + let output = new Array(size + 1).fill(0); + let max = Math.max(...arr); + + let freq = new Array(max + 1).fill(0); + + + for (let i = 0; i < size; i++){ + const num = Math.floor(arr[i] / place) % 10; + freq[num]++; + } + + + for (let i = 1; i < 10; i++){ + freq[i] += freq[i - 1]; + } + + + for (let i = size - 1; i >= 0; i--) { + const num = Math.floor(arr[i] / place) % 10; + output[freq[num] - 1] = arr[i]; + freq[num]--; + } + + + for (let i = 0; i < size; i++){ + arr[i] = output[i]; + } +} + +const radixSort = (arr, size = arr.length) => { + + let max = Math.max(...arr); + + + for(let i = 1; parseInt(max / i) > 0; i *= 10){ + countingSort(arr, size, i); + } +} +radixSort(arr); +console.log(arr); \ No newline at end of file diff --git a/DSA/DFS/BinaryTreePaths.js b/DSA/DFS/BinaryTreePaths.js new file mode 100644 index 0000000..a99c1c0 --- /dev/null +++ b/DSA/DFS/BinaryTreePaths.js @@ -0,0 +1,18 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +var binaryTreePaths = function(root) { + let allAr = []; + getPath(root); + function getPath(node,arr=[]){ + (!node.left && !node.right) &&allAr.push([...arr, node.val]); + node.left && getPath(node.left, [...arr,node.val]); + node.right && getPath(node.right, [...arr,node.val]); + } + return allAr.map((a)=>a.join('->')); +}; diff --git a/DSA/DFS/Diameter of Binary Tree b/DSA/DFS/Diameter of Binary Tree new file mode 100644 index 0000000..6ee879c --- /dev/null +++ b/DSA/DFS/Diameter of Binary Tree @@ -0,0 +1,24 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +var diameterOfBinaryTree = function(root) { + let result = 0; + helper(root); + return result + + function helper(root){ + if(!root)return 0; + + let left = helper(root.left); + let right = helper(root.right); + + result = Math.max(left + right , result); + + return 1 + Math.max(left, right) + } +}; diff --git a/DSA/DFS/Find Mode in Binary Search Tree.js b/DSA/DFS/Find Mode in Binary Search Tree.js new file mode 100644 index 0000000..1f77f2c --- /dev/null +++ b/DSA/DFS/Find Mode in Binary Search Tree.js @@ -0,0 +1,28 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +var findMode = function(r) { + const map = new Map() + const maxMap = new Map() + let max = -Infinity + + function dfs(root){ + if(!root) return + map.set(root.val, (map.get(root.val) || 0) + 1) + if(map.get(root.val) >= max){ + max = map.get(root.val) + if(!maxMap.get(max)) maxMap.set(max, new Set()) + maxMap.get(max).add(root.val) + } + dfs(root.left) + dfs(root.right) + } + + dfs(r) + return [...maxMap.get(max)] +}; diff --git a/DSA/DFS/InvertBinaryTree.js b/DSA/DFS/InvertBinaryTree.js new file mode 100644 index 0000000..6491180 --- /dev/null +++ b/DSA/DFS/InvertBinaryTree.js @@ -0,0 +1,16 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ + +var invertTree = function(root) { + if(!root) { + return null; + } + [root.left, root.right] = [invertTree(root.right), invertTree(root.left)]; + return root; +}; diff --git a/DSA/DFS/KthSmallestElementInABST.js b/DSA/DFS/KthSmallestElementInABST.js new file mode 100644 index 0000000..6179420 --- /dev/null +++ b/DSA/DFS/KthSmallestElementInABST.js @@ -0,0 +1,21 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +var kthSmallest = function(root, k) { + let arrayOfElements = [] + + function dfs(node) { + if(!node) return; + + dfs(node.left); + arrayOfElements.push(node.val); + dfs(node.right); + } + dfs(root); + return arrayOfElements[k - 1]; +}; diff --git a/DSA/DFS/LowestCommonAncestorOfABinarySearchTree.js b/DSA/DFS/LowestCommonAncestorOfABinarySearchTree.js new file mode 100644 index 0000000..a9320f7 --- /dev/null +++ b/DSA/DFS/LowestCommonAncestorOfABinarySearchTree.js @@ -0,0 +1,37 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +var lowestCommonAncestor = function(root, p, q) { + +let ansNode = null +if(p.val == root.val || q.val == root.val){ + return root +} +function traverse(node){ + if((p.val < node.val && q.val > node.val) || (p.val > node.val && q.val < node.val)){ + ansNode = node + return + } + else if(p.val == node.val || q.val == node.val){ + ansNode = node + return + } + if(node.left) + traverse(node.left) + if(node.right) + traverse(node.right) +} + +if((p.val < root.val && q.val > root.val) || (p.val > root.val && q.val < root.val)) + return root +else if(p.val > root.val && q.val > root.val) + traverse(root.right) +else if(p.val < root.val && q.val < root.val) + traverse(root.left) + +return ansNode +}; diff --git a/DSA/DFS/SameTree.js b/DSA/DFS/SameTree.js new file mode 100644 index 0000000..936e6ad --- /dev/null +++ b/DSA/DFS/SameTree.js @@ -0,0 +1,20 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +var isSameTree = function(p, q) { + return is_tree_equal(p, q); + function is_tree_equal(tree1, tree2){ + if (tree1 === null && tree2 === null) return true; + if (tree1 === null || tree2 === null) return false; + if (tree1.val !== tree2.val) return false; + + const is_left_tree_equal = is_tree_equal(tree1.left, tree2.left) + const is_right_tree_equal = is_tree_equal(tree1.right, tree2.right) + return is_left_tree_equal && is_right_tree_equal + } +}; diff --git a/DSA/DFS/SymmetricTree.js b/DSA/DFS/SymmetricTree.js new file mode 100644 index 0000000..fb7a863 --- /dev/null +++ b/DSA/DFS/SymmetricTree.js @@ -0,0 +1,29 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +var isSymmetric = function(root) { +if(!root.left && !root.right) + return true +if((root.left && !root.right) || (!root.left && root.right)) + return false +let ans = true +function traverse(nodeL,nodeR){ + if(nodeL.val != nodeR.val) + ans = false + if(nodeL.left && nodeR.right) + traverse(nodeL.left,nodeR.right) + else if((!nodeL.left && nodeR.right) || (nodeL.left && !nodeR.right)) + ans = false + if(nodeL.right && nodeR.left) + traverse(nodeL.right, nodeR.left) + else if((!nodeL.right && nodeR.left) || (nodeL.right && !nodeR.left)) + ans = false +} +traverse(root.left,root.right) +return ans +}; diff --git a/DSA/DFS/lowest-common-ancestor-of-a-binary-tree.js b/DSA/DFS/lowest-common-ancestor-of-a-binary-tree.js new file mode 100644 index 0000000..4c99d9d --- /dev/null +++ b/DSA/DFS/lowest-common-ancestor-of-a-binary-tree.js @@ -0,0 +1,24 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val) { + * this.val = val; + * this.left = this.right = null; + * } + */ +var lowestCommonAncestor = function(root, p, q) { + if(!root) { + return null; + } + const left = lowestCommonAncestor(root.left, p, q), + right = lowestCommonAncestor(root.right, p, q); + if((left && right) || root.val === p.val || root.val === q.val) { + return root; + } + else if(left) { + return left; + } + else if(right) { + return right; + } + return null; +}; diff --git a/DSA/DFS/minimum-distance-between-bst-nodes.js b/DSA/DFS/minimum-distance-between-bst-nodes.js new file mode 100644 index 0000000..a931a50 --- /dev/null +++ b/DSA/DFS/minimum-distance-between-bst-nodes.js @@ -0,0 +1,26 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +var minDiffInBST = function(root) { + let min = Number.MAX_VALUE +let arr = [] +function traverse(node){ + if(node.left) + traverse(node.left) + arr.push(node.val) + if(node.right) + traverse(node.right) +} +traverse(root) +for(let i = arr.length - 1 ; i > 0 ; i--){ + if(arr[i] - arr[i-1] < min){ + min = arr[i] - arr[i-1] + } +} +return min +}; diff --git a/DSA/DFS/validateBinaryTree.js b/DSA/DFS/validateBinaryTree.js new file mode 100644 index 0000000..60f49ea --- /dev/null +++ b/DSA/DFS/validateBinaryTree.js @@ -0,0 +1,17 @@ +var isValidBST = function (root) { + + + function helper(node, min, max) { + if (!node) return true; + + if (node.val <= min || node.val >= max) return false; + + let left = helper(node.left, min, node.val); + let right = helper(node.right, node.val, max) + + return left && right + + } + + return helper(root, -Infinity, Infinity) +}; \ No newline at end of file diff --git a/DSA/Graphs/DijkstraAlgorithm.js b/DSA/Graphs/DijkstraAlgorithm.js new file mode 100644 index 0000000..534c848 --- /dev/null +++ b/DSA/Graphs/DijkstraAlgorithm.js @@ -0,0 +1,73 @@ +const graphInfo = { + source: {A: 4, B: 1}, + A: {C: 3, D: 5}, + B: {A: 7, D: 2}, + C: {D: 4, destination: 3}, + D: {destination: 2}, + destination: {} + }; + + // primary function + const dijkstra = (graph) => { + + // shortest distance to each vertex + const distance = Object.assign( + {destination: Infinity}, graph.source); + + // display path + const parentArray = {destination: null}; + for (let child in graph.source) { + parentArray[child] = 'source'; + } + + // track vertexs that are marked + const marked = []; + + let vertex = minimumDistancevertex(distance, marked); + + while (vertex) { + let cost = distance[vertex]; + let children = graph[vertex]; + for (let n in children) { + let latestDistance = cost + children[n]; + if (!distance[n]) { + distance[n] = latestDistance; + parentArray[n] = vertex; + } + if (distance[n] > latestDistance) { + distance[n] = latestDistance; + parentArray[n] = vertex; + } + } + marked.push(vertex); + vertex = minimumDistancevertex(distance, marked); + } + + let shortestPath = ['destination']; + let parent = parentArray.destination; + while (parent) { + shortestPath.push(parent); + parent = parentArray[parent]; + } + shortestPath.reverse(); + + const result = { + distance: distance.destination, + path: shortestPath + }; + + return result; + }; + + const minimumDistancevertex = (distance, marked) => { + return Object.keys(distance).reduce((lowest, vertex) => { + if (lowest === null || distance[vertex] < distance[lowest]) { + if (!marked.includes(vertex)) { + lowest = vertex; + } + } + return lowest; + }, null); + }; + + console.log(dijkstra(graphInfo)); \ No newline at end of file diff --git a/DSA/Graphs/Kruskal Algorithm b/DSA/Graphs/Kruskal Algorithm new file mode 100644 index 0000000..8080866 --- /dev/null +++ b/DSA/Graphs/Kruskal Algorithm @@ -0,0 +1,43 @@ +class UnionFind { + constructor(elements) { + // Number of disconnected components + this.count = elements.length; + + // Keep Track of connected components + this.parent = {}; + + // Initialize the data structure such that all + // elements have themselves as parents + elements.forEach(e => (this.parent[e] = e)); + } + + union(a, b) { + let rootA = this.find(a); + let rootB = this.find(b); + + // Roots are same so these are already connected. + if (rootA === rootB) return; + + // Always make the element with smaller root the parent. + if (rootA < rootB) { + if (this.parent[b] != b) this.union(this.parent[b], a); + this.parent[b] = this.parent[a]; + } else { + if (this.parent[a] != a) this.union(this.parent[a], b); + this.parent[a] = this.parent[b]; + } + } + + // Returns final parent of a node + find(a) { + while (this.parent[a] !== a) { + a = this.parent[a]; + } + return a; + } + + // Checks connectivity of the 2 nodes + connected(a, b) { + return this.find(a) === this.find(b); + } +} diff --git a/DSA/Graphs/Maze.js b/DSA/Graphs/Maze.js new file mode 100644 index 0000000..d33ef15 --- /dev/null +++ b/DSA/Graphs/Maze.js @@ -0,0 +1,209 @@ +const maze = [ + ['S', 0, 1, 0, 1], + [0, 1, 0, 0, 0], + [0, 0, 1, 1, 0], + [1, 0, 0, 0, 'E'], +]; + + +function solveMazeDFS(maze) { + const rows = maze.length; + const cols = maze[0].length; + + function dfs(x, y) { + if (x < 0 || x >= rows || y < 0 || y >= cols || maze[x][y] === 1) { + return false; + } + + if (maze[x][y] === 'E') { + return true; // Reached the end of the maze + } + + maze[x][y] = 1; // Mark as visited + + // Explore in all four directions (up, down, left, right) + if (dfs(x + 1, y) || dfs(x - 1, y) || dfs(x, y + 1) || dfs(x, y - 1)) { + return true; + } + + maze[x][y] = 0; // Mark as unvisited if no path was found + return false; + } + + // Find the start point + let startX, startY; + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (maze[i][j] === 'S') { + startX = i; + startY = j; + break; + } + } + } + + // Call DFS from the start point + if (dfs(startX, startY)) { + return "Maze is solvable."; + } else { + return "Maze has no solution."; + } +} + +console.log(solveMazeDFS(maze)); + + + + +function solveMazeBFS(maze) { + const rows = maze.length; + const cols = maze[0].length; + const queue = []; + + // Find the start point + let startX, startY; + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (maze[i][j] === 'S') { + startX = i; + startY = j; + break; + } + } + } + + // Define possible moves (up, down, left, right) + const moves = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + queue.push([startX, startY]); + + while (queue.length > 0) { + const [x, y] = queue.shift(); + + if (maze[x][y] === 'E') { + return "Maze is solvable."; + } + + maze[x][y] = 1; // Mark as visited + + for (const [dx, dy] of moves) { + const newX = x + dx; + const newY = y + dy; + + if (newX >= 0 && newX < rows && newY >= 0 && newY < cols && maze[newX][newY] === 0) { + queue.push([newX, newY]); + } + } + } + + return "Maze has no solution."; +} + +console.log(solveMazeBFS(maze)); + + +class PriorityQueue { + constructor() { + this.elements = []; + } + + enqueue(element, priority) { + this.elements.push({ element, priority }); + this.elements.sort((a, b) => a.priority - b.priority); + } + + dequeue() { + return this.elements.shift().element; + } + + isEmpty() { + return this.elements.length === 0; + } +} + +function heuristic(x1, y1, x2, y2) { + // A simple heuristic function (Manhattan distance) + return Math.abs(x1 - x2) + Math.abs(y1 - y2); +} + +function solveMazeAStar(maze) { + const rows = maze.length; + const cols = maze[0].length; + + // Find the start and end points + let startX, startY, endX, endY; + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (maze[i][j] === 'S') { + startX = i; + startY = j; + } else if (maze[i][j] === 'E') { + endX = i; + endY = j; + } + } + } + + const openSet = new PriorityQueue(); + openSet.enqueue({ x: startX, y: startY, cost: 0 }, 0); + + const cameFrom = {}; + const gScore = {}; + + gScore[`${startX}-${startY}`] = 0; + + while (!openSet.isEmpty()) { + const current = openSet.dequeue(); + const { x, y } = current; + + if (x === endX && y === endY) { + // Reconstruct the path + const path = []; + let currentNode = { x: endX, y: endY }; + while (currentNode) { + path.unshift(currentNode); + currentNode = cameFrom[`${currentNode.x}-${currentNode.y}`]; + } + return path; + } + + for (const [dx, dy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) { + const newX = x + dx; + const newY = y + dy; + + if ( + newX >= 0 && + newX < rows && + newY >= 0 && + newY < cols && + maze[newX][newY] !== 1 + ) { + const tentativeGScore = gScore[`${x}-${y}`] + 1; + + if ( + !gScore[`${newX}-${newY}`] || + tentativeGScore < gScore[`${newX}-${newY}`] + ) { + cameFrom[`${newX}-${newY}`] = { x, y }; + gScore[`${newX}-${newY}`] = tentativeGScore; + const fScore = + tentativeGScore + heuristic(newX, newY, endX, endY); + openSet.enqueue({ x: newX, y: newY, cost: fScore }, fScore); + } + } + } + } + + return "Maze has no solution."; +} + +const path = solveMazeAStar(maze); + +if (path !== "Maze has no solution.") { + console.log("Path found:"); + path.forEach((cell, index) => { + console.log(`Step ${index + 1}: (${cell.x}, ${cell.y})`); + }); +} else { + console.log("Maze has no solution."); +} diff --git a/DSA/Graphs/Prim'sAlgorithm.js b/DSA/Graphs/Prim'sAlgorithm.js new file mode 100644 index 0000000..0201e0c --- /dev/null +++ b/DSA/Graphs/Prim'sAlgorithm.js @@ -0,0 +1,53 @@ +primsMST() { + // Initialize graph that'll contain the MST + const MST = new Graph(); + if (this.nodes.length === 0) { + return MST; + } + + + // Select first node as starting node + let s = this.nodes[0]; + + + // Create a Priority Queue and explored set + let edgeQueue = new PriorityQueue(this.nodes.length * this.nodes.length); + let explored = new Set(); + explored.add(s); + MST.addNode(s); + + + // Add all edges from this starting node to the PQ taking weights as priority + this.edges[s].forEach(edge => { + edgeQueue.enqueue([s, edge.node], edge.weight); + }); + + + // Take the smallest edge and add that to the new graph + let currentMinEdge = edgeQueue.dequeue(); + while (!edgeQueue.isEmpty()) { + + + // Continue removing edges till we get an edge with an unexplored node + while (!edgeQueue.isEmpty() && explored.has(currentMinEdge.data[1])) { + currentMinEdge = edgeQueue.dequeue(); + } + let nextNode = currentMinEdge.data[1]; + + + // Check again as queue might get empty without giving back unexplored element + if (!explored.has(nextNode)) { + MST.addNode(nextNode); + MST.addEdge(currentMinEdge.data[0], nextNode, currentMinEdge.priority); + // Again add all edges to the PQ + this.edges[nextNode].forEach(edge => { + edgeQueue.enqueue([nextNode, edge.node], edge.weight); + }); + + + // Mark this node as explored explored.add(nextNode); + s = nextNode; + } + } + return MST; +} diff --git a/DSA/Graphs/Travelling Salesman Problem/AntColonyOptimization.js b/DSA/Graphs/Travelling Salesman Problem/AntColonyOptimization.js new file mode 100644 index 0000000..3096252 --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/AntColonyOptimization.js @@ -0,0 +1,101 @@ +function antColonyOptimizationTSP(cities, distances, numAnts, maxIterations, pheromoneEvaporationRate, alpha, beta) { + const numCities = cities.length; + const initialPheromoneLevel = 1 / (numCities * numCities); + let pheromoneMatrix = Array.from({ length: numCities }, () => + Array(numCities).fill(initialPheromoneLevel) + ); + let bestOrder; + let bestDistance = Infinity; + + for (let iteration = 0; iteration < maxIterations; iteration++) { + const antPaths = []; + for (let ant = 0; ant < numAnts; ant++) { + const path = constructAntPath(pheromoneMatrix, distances, alpha, beta); + antPaths.push(path); + const pathDistance = calculateTotalDistance(path, distances); + if (pathDistance < bestDistance) { + bestOrder = path; + bestDistance = pathDistance; + } + } + + updatePheromoneMatrix(pheromoneMatrix, antPaths, pheromoneEvaporationRate); + } + + return { order: bestOrder, distance: bestDistance }; +} + +function constructAntPath(pheromoneMatrix, distances, alpha, beta) { + const numCities = pheromoneMatrix.length; + const startCity = Math.floor(Math.random() * numCities); + let currentCity = startCity; + const path = [startCity]; + const unvisitedCities = new Set([...Array(numCities).keys()].filter((i) => i !== startCity)); + + while (unvisitedCities.size > 0) { + const nextCity = chooseNextCity(currentCity, unvisitedCities, pheromoneMatrix, distances, alpha, beta); + path.push(nextCity); + unvisitedCities.delete(nextCity); + currentCity = nextCity; + } + + return path; +} + +function chooseNextCity(currentCity, unvisitedCities, pheromoneMatrix, distances, alpha, beta) { + const pheromoneLevels = []; + const totalProbability = [...unvisitedCities].reduce((sum, city) => { + const pheromone = pheromoneMatrix[currentCity][city]; + const distance = distances[currentCity][city]; + const probability = Math.pow(pheromone, alpha) * Math.pow(1 / distance, beta); + pheromoneLevels.push(probability); + return sum + probability; + }, 0); + + const randomValue = Math.random() * totalProbability; + let accumulatedProbability = 0; + + for (let i = 0; i < pheromoneLevels.length; i++) { + accumulatedProbability += pheromoneLevels[i]; + if (accumulatedProbability >= randomValue) { + return [...unvisitedCities][i]; + } + } + + return [...unvisitedCities][0]; // Fallback in case of numerical instability +} + +function updatePheromoneMatrix(pheromoneMatrix, antPaths, pheromoneEvaporationRate) { + const numCities = pheromoneMatrix.length; + + // Evaporate pheromone + for (let i = 0; i < numCities; i++) { + for (let j = 0; j < numCities; j++) { + pheromoneMatrix[i][j] *= (1 - pheromoneEvaporationRate); + } + } + + // Deposit pheromone based on ant paths + for (const path of antPaths) { + const pathDistance = calculateTotalDistance(path, distances); + for (let i = 0; i < path.length - 1; i++) { + const fromCity = path[i]; + const toCity = path[i + 1]; + pheromoneMatrix[fromCity][toCity] += 1 / pathDistance; + pheromoneMatrix[toCity][fromCity] += 1 / pathDistance; + } + } +} + +// Test case +const cities = ["A", "B", "C", "D"]; +const distances = [ + [0, 10, 15, 20], + [10, 0, 35, 25], + [15, 35, 0, 30], + [20, 25, 30, 0], +]; + +const result = antColonyOptimizationTSP(cities, distances, 10, 100, 0.5, 1.0, 2.0); +console.log("Ant Colony Optimization Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Ant Colony Optimization Distance:", result.distance); diff --git a/DSA/Graphs/Travelling Salesman Problem/BranchAndBound.js b/DSA/Graphs/Travelling Salesman Problem/BranchAndBound.js new file mode 100644 index 0000000..68e764e --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/BranchAndBound.js @@ -0,0 +1,50 @@ +function tspBranchAndBound(cities, distances) { + const n = cities.length; + const visited = new Array(n).fill(false); + visited[0] = true; + const initialPath = [0]; + const { order, distance } = branchAndBoundHelper(0, initialPath, 0, Infinity); + + function branchAndBoundHelper(currentCity, path, currentDistance, bestDistance) { + if (path.length === n) { + return { order: path, distance: currentDistance + distances[currentCity][0] }; + } + + let minDistance = bestDistance; + let minOrder = []; + + for (let nextCity = 0; nextCity < n; nextCity++) { + if (!visited[nextCity]) { + visited[nextCity] = true; + const newPath = [...path, nextCity]; + const newDistance = currentDistance + distances[currentCity][nextCity]; + + if (newDistance < minDistance) { + const result = branchAndBoundHelper(nextCity, newPath, newDistance, minDistance); + if (result.distance < minDistance) { + minDistance = result.distance; + minOrder = result.order; + } + } + + visited[nextCity] = false; + } + } + + return { order: minOrder, distance: minDistance }; + } + + return { order, distance }; +} + +// Test case +const cities = ["A", "B", "C"]; +const distances = [ + [0, 10, 15], + [10, 0, 20], + [15, 20, 0], +]; + +const result = tspBranchAndBound(cities, distances); +console.log("Branch and Bound Optimal Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Branch and Bound Optimal Distance:", result.distance); diff --git a/DSA/Graphs/Travelling Salesman Problem/BruteForce.js b/DSA/Graphs/Travelling Salesman Problem/BruteForce.js new file mode 100644 index 0000000..fdba98c --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/BruteForce.js @@ -0,0 +1,45 @@ +function permute(arr) { + if (arr.length === 0) return [[]]; + const [first, ...rest] = arr; + const permutationsWithoutFirst = permute(rest); + return permutationsWithoutFirst.flatMap((perm) => + perm.map((_, idx) => [...perm.slice(0, idx), first, ...perm.slice(idx)]) + ); +} + +function calculateTotalDistance(order, distances) { + let totalDistance = 0; + for (let i = 0; i < order.length - 1; i++) { + totalDistance += distances[order[i]][order[i + 1]]; + } + return totalDistance; +} + +function bruteForceTSP(cities, distances) { + const cityIndices = Array.from({ length: cities.length }, (_, i) => i); + const permutations = permute(cityIndices); + let minDistance = Infinity; + let bestOrder = []; + + for (const perm of permutations) { + const distance = calculateTotalDistance(perm, distances); + if (distance < minDistance) { + minDistance = distance; + bestOrder = perm; + } + } + + return { order: bestOrder, distance: minDistance }; +} + +// Test case +const cities = ["A", "B", "C"]; +const distances = [ + [0, 10, 15], + [10, 0, 20], + [15, 20, 0], +]; + +const result = bruteForceTSP(cities, distances); +console.log("Optimal Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Optimal Distance:", result.distance); \ No newline at end of file diff --git a/DSA/Graphs/Travelling Salesman Problem/ChristofidesAlgorithm.js b/DSA/Graphs/Travelling Salesman Problem/ChristofidesAlgorithm.js new file mode 100644 index 0000000..1ea53c3 --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/ChristofidesAlgorithm.js @@ -0,0 +1,127 @@ +function minimumSpanningTree(graph) { + const numVertices = graph.length; + const parent = new Array(numVertices).fill(-1); + const key = new Array(numVertices).fill(Infinity); + const inMST = new Array(numVertices).fill(false); + + key[0] = 0; + + for (let count = 0; count < numVertices - 1; count++) { + const u = minKey(key, inMST); + inMST[u] = true; + + for (let v = 0; v < numVertices; v++) { + if (graph[u][v] && !inMST[v] && graph[u][v] < key[v]) { + parent[v] = u; + key[v] = graph[u][v]; + } + } + } + + return parent; +} + +function minKey(key, inMST) { + const numVertices = key.length; + let min = Infinity; + let minIndex = -1; + + for (let v = 0; v < numVertices; v++) { + if (!inMST[v] && key[v] < min) { + min = key[v]; + minIndex = v; + } + } + + return minIndex; +} + +function minimumWeightPerfectMatching(graph, parent) { + const numVertices = graph.length; + const edges = []; + + for (let i = 1; i < numVertices; i++) { + edges.push([i, parent[i]]); + } + + const matching = []; + + const visited = new Array(numVertices).fill(false); + + for (const [u, v] of edges) { + if (!visited[u] && !visited[v]) { + matching.push([u, v]); + visited[u] = visited[v] = true; + } + } + + return matching; +} + +function eulerianTour(graph, startVertex) { + const tour = []; + const stack = [startVertex]; + while (stack.length) { + const currentVertex = stack.pop(); + tour.push(currentVertex); + for (let i = graph[currentVertex].length - 1; i >= 0; i--) { + if (graph[currentVertex][i] !== 0) { + stack.push(i); + graph[currentVertex][i] = graph[i][currentVertex] = 0; // Remove edge to avoid revisiting + } + } + } + return tour; +} + +function christofidesTSP(cities, distances) { + const numCities = cities.length; + const minimumSpanningTreeGraph = minimumSpanningTree(distances); + const matching = minimumWeightPerfectMatching(distances, minimumSpanningTreeGraph); + const graph = Array(numCities).fill(null).map(() => Array(numCities).fill(0)); + + for (const [u, v] of matching) { + graph[u][v] = graph[v][u] = distances[u][v]; + } + + for (let i = 0; i < numCities; i++) { + for (let j = 0; j < numCities; j++) { + if (minimumSpanningTreeGraph[i] !== j) { + graph[i][j] = distances[i][j]; + } + } + } + + const eulerianTourPath = eulerianTour(graph, 0); + const visited = new Set(); + const hamiltonianTour = []; + + for (const vertex of eulerianTourPath) { + if (!visited.has(vertex)) { + hamiltonianTour.push(vertex); + visited.add(vertex); + } + } + + hamiltonianTour.push(hamiltonianTour[0]); + + let totalDistance = 0; + for (let i = 0; i < hamiltonianTour.length - 1; i++) { + totalDistance += distances[hamiltonianTour[i]][hamiltonianTour[i + 1]]; + } + + return { order: hamiltonianTour, distance: totalDistance }; +} + +// Test case +const cities = ["A", "B", "C", "D"]; +const distances = [ + [0, 10, 15, 20], + [10, 0, 35, 25], + [15, 35, 0, 30], + [20, 25, 30, 0], +]; + +const result = christofidesTSP(cities, distances); +console.log("Christofides Algorithm Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Christofides Algorithm Distance:", result.distance); diff --git a/DSA/Graphs/Travelling Salesman Problem/DynamicProgramingBitmask.js b/DSA/Graphs/Travelling Salesman Problem/DynamicProgramingBitmask.js new file mode 100644 index 0000000..60d237a --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/DynamicProgramingBitmask.js @@ -0,0 +1,40 @@ +function tspDynamicProgrammingBitmask(cities, distances) { + const n = cities.length; + const memo = new Array(n).fill(null).map(() => new Array(1 << n).fill(null)); + + function dp(node, bitmask) { + if (bitmask === (1 << n) - 1) { + return distances[node][0]; + } + + if (memo[node][bitmask] !== null) { + return memo[node][bitmask]; + } + + let minDistance = Infinity; + for (let nextNode = 0; nextNode < n; nextNode++) { + if ((bitmask & (1 << nextNode)) === 0) { + const newDistance = distances[node][nextNode] + dp(nextNode, bitmask | (1 << nextNode)); + minDistance = Math.min(minDistance, newDistance); + } + } + + memo[node][bitmask] = minDistance; + return minDistance; + } + + const initialBitmask = 1; // Start from city 0 + const minDistance = dp(0, initialBitmask); + return minDistance; +} + +// Test case +const cities = ["A", "B", "C"]; +const distances = [ + [0, 10, 15], + [10, 0, 20], + [15, 20, 0], +]; + +const result = tspDynamicProgrammingBitmask(cities, distances); +console.log("Dynamic Programming (Bitmask) Optimal Distance:", result); diff --git a/DSA/Graphs/Travelling Salesman Problem/Dynamic_programming.js b/DSA/Graphs/Travelling Salesman Problem/Dynamic_programming.js new file mode 100644 index 0000000..f66fb90 --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/Dynamic_programming.js @@ -0,0 +1,44 @@ +function dynamicProgrammingTSP(cities, distances) { + const numCities = cities.length; + const numStates = 1 << numCities; // 2^n states to represent subsets of cities + const memo = Array(numCities) + .fill(null) + .map(() => Array(numStates).fill(null)); + + function tspDP(currentCity, state) { + if (state === (1 << numCities) - 1) { + // All cities visited, return to the starting city + return distances[currentCity][0]; + } + + if (memo[currentCity][state] !== null) { + return memo[currentCity][state]; + } + + let minDistance = Infinity; + for (let nextCity = 0; nextCity < numCities; nextCity++) { + if ((state & (1 << nextCity)) === 0) { + // Next city not visited + const newDistance = distances[currentCity][nextCity] + tspDP(nextCity, state | (1 << nextCity)); + minDistance = Math.min(minDistance, newDistance); + } + } + + memo[currentCity][state] = minDistance; + return minDistance; + } + + const optimalDistance = tspDP(0, 1); // Start from city 0 (the first city), with only city 0 visited + return optimalDistance; +} + +// Test case +const cities = ["A", "B", "C"]; +const distances = [ + [0, 10, 15], + [10, 0, 20], + [15, 20, 0], +]; + +const result = dynamicProgrammingTSP(cities, distances); +console.log("Dynamic Programming Optimal Distance:", result); diff --git a/DSA/Graphs/Travelling Salesman Problem/GeneticAlgorithm.js b/DSA/Graphs/Travelling Salesman Problem/GeneticAlgorithm.js new file mode 100644 index 0000000..c0ad8fd --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/GeneticAlgorithm.js @@ -0,0 +1,76 @@ +function generateRandomOrder(n) { + const order = Array.from({ length: n }, (_, i) => i); + for (let i = n - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [order[i], order[j]] = [order[j], order[i]]; // Swap elements randomly + } + return order; +} + +function calculateTotalDistance(order, distances) { + let totalDistance = 0; + for (let i = 0; i < order.length - 1; i++) { + totalDistance += distances[order[i]][order[i + 1]]; + } + return totalDistance; +} + +function geneticAlgorithmTSP(cities, distances, populationSize, generations) { + let population = Array.from({ length: populationSize }, () => generateRandomOrder(cities.length)); + let bestOrder = population[0]; + let minDistance = calculateTotalDistance(bestOrder, distances); + + for (let generation = 0; generation < generations; generation++) { + population = population.sort(() => 0.5 - Math.random()); // Shuffle population + + for (let i = 0; i < populationSize; i += 2) { + const parent1 = population[i]; + const parent2 = population[i + 1]; + const [child1, child2] = crossover(parent1, parent2); + mutate(child1); + mutate(child2); + + population[i] = child1; + population[i + 1] = child2; + } + + const newBestOrder = population[0]; + const newMinDistance = calculateTotalDistance(newBestOrder, distances); + if (newMinDistance < minDistance) { + bestOrder = newBestOrder; + minDistance = newMinDistance; + } + } + + return { order: bestOrder, distance: minDistance }; +} + +// Helper functions for Genetic Algorithm +function crossover(parent1, parent2) { + const n = parent1.length; + const start = Math.floor(Math.random() * n); + const end = Math.floor(Math.random() * (n - start)) + start; + const child1 = parent1.slice(start, end + 1).concat(parent2.filter((city) => !child1.includes(city))); + const child2 = parent2.slice(start, end + 1).concat(parent1.filter((city) => !child2.includes(city))); + return [child1, child2]; +} + +function mutate(order) { + const n = order.length; + const i = Math.floor(Math.random() * n); + const j = (i + 1) % n; + [order[i], order[j]] = [order[j], order[i]]; // Swap two cities +} + +// Test case +const cities = ["A", "B", "C", "D"]; +const distances = [ + [0, 10, 15, 20], + [10, 0, 35, 25], + [15, 35, 0, 30], + [20, 25, 30, 0], +]; + +const result = geneticAlgorithmTSP(cities, distances, 100, 1000); +console.log("Genetic Algorithm Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Genetic Algorithm Distance:", result.distance); diff --git a/DSA/Graphs/Travelling Salesman Problem/HeuristicApproach.js b/DSA/Graphs/Travelling Salesman Problem/HeuristicApproach.js new file mode 100644 index 0000000..d68cb97 --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/HeuristicApproach.js @@ -0,0 +1,44 @@ +function solveILPHeuristic() { + const coefficients = [2, 3, 5]; // Coefficients of the objective function + const constraintCoefficients = [ + [1, 2, 1], // Coefficients of the constraints + ]; + const constraintLimits = [0]; // Right-hand side of the constraints + + const numVariables = coefficients.length; + const numConstraints = constraintCoefficients.length; + + const solution = Array(numVariables).fill(0); + let objectiveValue = 0; + + for (let i = 0; i < numVariables; i++) { + // Calculate the marginal contribution of variable i to the objective function + const marginalContribution = coefficients[i]; + + // Check if adding variable i to the solution violates any constraints + let isFeasible = true; + for (let j = 0; j < numConstraints; j++) { + let constraintValue = 0; + for (let k = 0; k < numVariables; k++) { + constraintValue += solution[k] * constraintCoefficients[j][k]; + } + if (constraintValue + constraintCoefficients[j][i] > constraintLimits[j]) { + isFeasible = false; + break; + } + } + + // If adding variable i is feasible and improves the objective, include it in the solution + if (isFeasible && marginalContribution > 0) { + solution[i] = 1; + objectiveValue += marginalContribution; + } + } + + return { solution, objectiveValue }; +} + +// Test the heuristic approach for ILP +const result = solveILPHeuristic(); +console.log('Solution:', result.solution); +console.log('Objective Value (approximate):', result.objectiveValue); diff --git a/DSA/Graphs/Travelling Salesman Problem/IntegerLinearProgramming.js b/DSA/Graphs/Travelling Salesman Problem/IntegerLinearProgramming.js new file mode 100644 index 0000000..1d29fe4 --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/IntegerLinearProgramming.js @@ -0,0 +1,52 @@ +function solveILP() { + const coefficients = [2, 3, 5]; // Coefficients of the objective function + const constraintCoefficients = [ + [1, 2, 1], // Coefficients of the constraints + ]; + const constraintLimits = [0]; // Right-hand side of the constraints + + const numVariables = coefficients.length; + const numConstraints = constraintCoefficients.length; + + let bestObjectiveValue = -Infinity; + let bestSolution = Array(numVariables).fill(0); + + // Generate all possible binary combinations for the variables + for (let i = 0; i < Math.pow(2, numVariables); i++) { + const binary = (i >>> 0).toString(2).padStart(numVariables, '0').split('').map(Number); + let isFeasible = true; + + // Check if the binary combination satisfies the constraints + for (let j = 0; j < numConstraints; j++) { + let constraintValue = 0; + for (let k = 0; k < numVariables; k++) { + constraintValue += binary[k] * constraintCoefficients[j][k]; + } + if (constraintValue > constraintLimits[j]) { + isFeasible = false; + break; + } + } + + if (isFeasible) { + // Calculate the objective value for this combination + let objectiveValue = 0; + for (let k = 0; k < numVariables; k++) { + objectiveValue += binary[k] * coefficients[k]; + } + + // Update the best solution if this combination has a better objective value + if (objectiveValue > bestObjectiveValue) { + bestObjectiveValue = objectiveValue; + bestSolution = [...binary]; + } + } + } + + return { solution: bestSolution, objectiveValue: bestObjectiveValue }; +} + +// Test the ILP solver +const result = solveILP(); +console.log('Solution:', result.solution); +console.log('Optimal Objective Value:', result.objectiveValue); diff --git a/DSA/Graphs/Travelling Salesman Problem/IterativeImprovement(Heuristic).js b/DSA/Graphs/Travelling Salesman Problem/IterativeImprovement(Heuristic).js new file mode 100644 index 0000000..3fb118c --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/IterativeImprovement(Heuristic).js @@ -0,0 +1,48 @@ +function iterativeImprovementTSP(cities, distances) { + const numCities = cities.length; + let currentOrder = generateRandomOrder(numCities); + let currentDistance = calculateTotalDistance(currentOrder, distances); + let improvement = true; + + while (improvement) { + improvement = false; + + for (let i = 0; i < numCities - 1; i++) { + for (let j = i + 1; j < numCities; j++) { + const newOrder = twoOptSwap(currentOrder, i, j); + const newDistance = calculateTotalDistance(newOrder, distances); + + if (newDistance < currentDistance) { + currentOrder = newOrder; + currentDistance = newDistance; + improvement = true; + } + } + } + } + + return { order: currentOrder, distance: currentDistance }; +} + +function twoOptSwap(order, i, j) { + const newOrder = [...order]; + while (i < j) { + [newOrder[i], newOrder[j]] = [newOrder[j], newOrder[i]]; + i++; + j--; + } + return newOrder; +} + +// Test case +const cities = ["A", "B", "C", "D"]; +const distances = [ + [0, 10, 15, 20], + [10, 0, 35, 25], + [15, 35, 0, 30], + [20, 25, 30, 0], +]; + +const result = iterativeImprovementTSP(cities, distances); +console.log("Iterative Improvement Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Iterative Improvement Distance:", result.distance); diff --git a/DSA/Graphs/Travelling Salesman Problem/Lin-Kernighan.js b/DSA/Graphs/Travelling Salesman Problem/Lin-Kernighan.js new file mode 100644 index 0000000..79b0f88 --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/Lin-Kernighan.js @@ -0,0 +1,73 @@ +function linKernighanTSP(cities, distances) { + const n = cities.length; + let bestTour = []; + let bestCost = Infinity; + + function calculateTourCost(tour) { + let cost = 0; + for (let i = 0; i < n - 1; i++) { + const from = tour[i]; + const to = tour[i + 1]; + cost += distances[from][to]; + } + cost += distances[tour[n - 1]][tour[0]]; // Return to the starting city + return cost; + } + + function reverseSubtour(tour, i, j) { + while (i < j) { + const temp = tour[i]; + tour[i] = tour[j]; + tour[j] = temp; + i++; + j--; + } + } + + function explore(k, tour, gain, canRemove) { + if (k === n) { + const tourCost = calculateTourCost(tour); + if (tourCost < bestCost) { + bestTour = [...tour]; + bestCost = tourCost; + } + } else { + for (let i = k; i < n; i++) { + if (canRemove[tour[i]]) { + for (let j = 0; j < n; j++) { + if (!canRemove[tour[j]]) { + const nextTour = [...tour]; + reverseSubtour(nextTour, k, i); + nextTour[k] = tour[i]; + const newGain = gain - distances[tour[k - 1]][tour[k]] + distances[tour[i]][tour[i + 1]]; + + if (newGain < 0) { + explore(k + 1, nextTour, newGain, canRemove); + } + } + } + } + } + } + } + + const initialTour = Array.from({ length: n }, (_, i) => i); + const canRemove = Array(n).fill(true); + canRemove[0] = false; // Starting city cannot be removed + explore(1, initialTour, 0, canRemove); + + return { order: bestTour.map((idx) => cities[idx]), distance: bestCost }; +} + +// Test case +const cities = ["A", "B", "C", "D"]; +const distances = [ + [0, 10, 15, 20], + [10, 0, 35, 25], + [15, 35, 0, 30], + [20, 25, 30, 0], +]; + +const result = linKernighanTSP(cities, distances); +console.log("Lin-Kernighan Algorithm Order:", result.order.join(" -> ")); +console.log("Lin-Kernighan Algorithm Distance:", result.distance); diff --git a/DSA/Graphs/Travelling Salesman Problem/NearestNeighborAlgorithm.js b/DSA/Graphs/Travelling Salesman Problem/NearestNeighborAlgorithm.js new file mode 100644 index 0000000..9202d2e --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/NearestNeighborAlgorithm.js @@ -0,0 +1,38 @@ +function nearestNeighborTSP(cities, distances) { + const n = cities.length; + const visited = Array(n).fill(false); + const order = [0]; // Start from the first city (index 0) + let totalDistance = 0; + + visited[0] = true; + for (let i = 1; i < n; i++) { + let nearestIdx = -1; + let minDistance = Infinity; + + for (let j = 0; j < n; j++) { + if (!visited[j] && distances[order[i - 1]][j] < minDistance) { + nearestIdx = j; + minDistance = distances[order[i - 1]][j]; + } + } + + order.push(nearestIdx); + visited[nearestIdx] = true; + totalDistance += minDistance; + } + + totalDistance += distances[order[n - 1]][order[0]]; // Return to the starting city + return { order, distance: totalDistance }; +} + +// Test case +const cities = ["A", "B", "C"]; +const distances = [ + [0, 10, 15], + [10, 0, 20], + [15, 20, 0], +]; + +const result = nearestNeighborTSP(cities, distances); +console.log("Approximate Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Approximate Distance:", result.distance); diff --git a/DSA/Graphs/Travelling Salesman Problem/README.md b/DSA/Graphs/Travelling Salesman Problem/README.md new file mode 100644 index 0000000..d2f0d43 --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/README.md @@ -0,0 +1,57 @@ +# Travelling Salesman Problem + +The Traveling Salesman Problem (TSP) is a classic combinatorial optimization problem in the field of mathematics and computer science. It is a challenging problem that can be stated as follows: + +Given a list of cities and the distances between each pair of cities, the objective is to find the shortest possible route that visits each city exactly once and returns to the starting city. + +In other words, a traveling salesman needs to determine the most efficient way to visit all the cities in their itinerary without revisiting any city and returning to the starting point while minimizing the total distance traveled. The problem is often represented as a graph, where cities are nodes, and the distances between them are represented as edges. + +The Traveling Salesman Problem has numerous practical applications beyond sales, such as: + +1. Logistics and Transportation: Optimizing delivery routes for couriers, trucks, or drones to minimize travel time and fuel costs. + +2. Circuit Design: Designing the most efficient electronic circuit layout to connect a set of components. + +3. Manufacturing: Determining the order in which machines should process parts to minimize production time. + +4. DNA Sequencing: Finding the optimal order to sequence fragments of DNA in genomics research. + +5. Network Design: Planning the most efficient layout for connecting network nodes while minimizing data transmission costs. + +The TSP is a well-known NP-hard problem, which means that finding the optimal solution becomes increasingly difficult as the number of cities increases. There are various algorithms and heuristics used to approximate solutions to the TSP, such as brute force, dynamic programming, and various metaheuristic methods like genetic algorithms, simulated annealing, and ant colony optimization. + +Due to its computational complexity, finding the exact optimal solution for large instances of the TSP is often infeasible, and approximation algorithms are used to find near-optimal solutions that are practical for real-world applications. + +## Solution to the Travelling Salesman Problem + +### Exact Algorithms: + + 1. Brute Force: Enumerate all possible permutations of cities and calculate the total distance for each permutation to find the optimal solution. Practical only for small problem instances due to its exponential time complexity. + + 2. Dynamic Programming: The Held-Karp algorithm is a dynamic programming approach that improves efficiency by avoiding redundant calculations. It can solve TSP for moderate-sized instances but still has exponential time complexity in the worst case. + +### Approximation Algorithms: + + 1. Nearest Neighbor Algorithm: Start from a selected city and repeatedly choose the nearest unvisited city until all cities are visited. This is a simple and fast heuristic but may not always produce optimal solutions. + + 2. Christofides Algorithm: An approximation algorithm that guarantees a solution within 3/2 times the optimal solution for metric TSP instances (where distances satisfy the triangle inequality). It includes minimum spanning tree and minimum-weight perfect matching steps. + + 3. Lin-Kernighan Algorithm: An improvement heuristic that iteratively swaps edges in the tour to improve the solution quality. + +### Metaheuristic Algorithms: + + 1. Genetic Algorithms: Inspired by natural selection, these algorithms involve creating a population of candidate solutions and iteratively evolving them through selection, crossover, and mutation operations. Genetic algorithms can be effective for finding near-optimal solutions. + + 2. Simulated Annealing: Based on the annealing process in metallurgy, this algorithm explores the solution space by accepting suboptimal solutions with a decreasing probability as the search progresses. It can escape local optima. + + 3. Ant Colony Optimization: Inspired by the foraging behavior of ants, this algorithm models the exploration of solution space as ants deposit pheromones on edges. Over time, paths with higher pheromone concentrations are more likely to be chosen. + + 4. Integer Linear Programming (ILP): Formulate the TSP as an ILP problem and use ILP solvers like CPLEX or Gurobi to find exact solutions for small to moderately sized instances. + + 5. Heuristic Approaches: Various heuristics and custom algorithms can be designed specifically for certain types of TSP instances, such as planar TSP, Euclidean TSP, or TSP with time windows. + + 6. Hybrid Approaches: Combine multiple algorithms or heuristics to improve solution quality and efficiency. For example, a genetic algorithm might be enhanced with local search. + + 7. Preprocessing and Data Reduction: Reduce the problem size by removing dominated or redundant cities or by using geometric properties of the problem to simplify the search. + +The choice of method depends on factors such as the size and type of the TSP instance, the desired solution quality, and available computational resources. In practice, many large TSP instances are solved using approximation algorithms or metaheuristic methods to find high-quality solutions efficiently. diff --git a/DSA/Graphs/Travelling Salesman Problem/SimulatedAnnealing.js b/DSA/Graphs/Travelling Salesman Problem/SimulatedAnnealing.js new file mode 100644 index 0000000..907878a --- /dev/null +++ b/DSA/Graphs/Travelling Salesman Problem/SimulatedAnnealing.js @@ -0,0 +1,41 @@ +function simulatedAnnealingTSP(cities, distances, temperature, coolingRate) { + let currentOrder = generateRandomOrder(cities.length); + let currentDistance = calculateTotalDistance(currentOrder, distances); + let bestOrder = currentOrder.slice(); + let minDistance = currentDistance; + + while (temperature > 1) { + const i = Math.floor(Math.random() * cities.length); + const j = Math.floor(Math.random() * cities.length); + const newOrder = currentOrder.slice(); + [newOrder[i], newOrder[j]] = [newOrder[j], newOrder[i]]; // Swap two cities + const newDistance = calculateTotalDistance(newOrder, distances); + + const delta = newDistance - currentDistance; + if (delta < 0 || Math.random() < Math.exp(-delta / temperature)) { + currentOrder = newOrder; + currentDistance = newDistance; + if (currentDistance < minDistance) { + bestOrder = currentOrder.slice(); + minDistance = currentDistance; + } + } + + temperature *= coolingRate; + } + + return { order: bestOrder, distance: minDistance }; +} + +// Test case +const cities = ["A", "B", "C", "D"]; +const distances = [ + [0, 10, 15, 20], + [10, 0, 35, 25], + [15, 35, 0, 30], + [20, 25, 30, 0], +]; + +const result = simulatedAnnealingTSP(cities, distances, 1000, 0.99); +console.log("Simulated Annealing Order:", result.order.map((idx) => cities[idx]).join(" -> ")); +console.log("Simulated Annealing Distance:", result.distance); diff --git a/DSA/Miscelleneous/SmallestCommonMultiple.js b/DSA/Miscelleneous/SmallestCommonMultiple.js new file mode 100644 index 0000000..b18d7e8 --- /dev/null +++ b/DSA/Miscelleneous/SmallestCommonMultiple.js @@ -0,0 +1,36 @@ +/* + +Find the smallest common multiple of the provided parameters that can be evenly divided by both, as well as by +all sequential numbers in the range between these parameters. + +The range will be an array of two numbers that will not necessarily be in numerical order. + +For example, if given 1 and 3, find the smallest common multiple of both 1 and 3 that is also evenly divisible +by all numbers between 1 and 3. The answer here would be 6. + +*/ + +function smallestCommons(arr) { + var range = []; + for (var i = Math.max(arr[0], arr[1]); i >= Math.min(arr[0], arr[1]); i--) { + range.push(i); + } + + // could use reduce() in place of this block + var lcm = range[0]; + for (i = 1; i < range.length; i++) { + var GCD = gcd(lcm, range[i]); + lcm = (lcm * range[i]) / GCD; + } + return lcm; + + function gcd(x, y) { + if (y === 0) + return x; + else + return gcd(y, x%y); + } +} + +// test here +smallestCommons([1,5]); diff --git a/DSA/Strings/ShortestSubstring.js b/DSA/Strings/ShortestSubstring.js new file mode 100644 index 0000000..2335f0e --- /dev/null +++ b/DSA/Strings/ShortestSubstring.js @@ -0,0 +1,24 @@ +const compareSets = (a, b) => a.size === b.size && [...a].every(e => b.has(e)) + +function shortestSubstring(s) { + let len = s.length + let uniqueChars = new Set(Array.from(s)) + let subString = '' + let mLen = len + 1; + + for (let i = 0; i < len; i++) { + for (let j = i; j < len; j++) { + subString = subString + s[j] + if (compareSets(new Set(subString), uniqueChars)) { + if (mLen > subString.length) { + mLen = subString.length + } + break; + } + } + subString = '' + } + return mLen +} + +console.log(shortestSubstring('bcaacbc')) diff --git a/InterviewQuestions/findSecondLargestElementInArray.js b/InterviewQuestions/findSecondLargestElementInArray.js new file mode 100644 index 0000000..088e6ed --- /dev/null +++ b/InterviewQuestions/findSecondLargestElementInArray.js @@ -0,0 +1,20 @@ +/** + * Problem statement: Find second largest element in the array. Consider the case wherein duplicates can exist + * arr = [12, 35, 1, 10, 34, 35, 35] - Here 35 occurs 3 times, but second largest element should be 34 + */ +function findSecondLargestElementInArray(arr) { + let largest = -1, + secondLargest = -1; + + for (let i = 0; i <= arr.length - 1; i++) { + if (arr[i] > largest) { + secondLargest = largest; + largest = arr[i]; + } else if (arr[i] > secondLargest && arr[i] != largest) { + secondLargest = arr[i]; + } + } + console.log(secondLargest); +} +let arr = [12, 35, 1, 10, 34, 35, 35]; +findSecondLargestElementInArray(arr); diff --git a/README.md b/README.md index f0dd535..55feb21 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Feel free to create an issue about what topic you want to add explanations or co ### How can I contribute? - If you are into Data Structures and Algorithms ft. JavaScript, contribute by adding your favourite ones to [here](/DSA) +- Following Striver's [SDE-Sheet](https://docs.google.com/document/d/1SM92efk8oDl8nyVw8NHPnbGexTS9W-1gmTEYfEurLWQ/edit)? We believe that it is a good resource to prepare for DSA rounds. Contribute your JS Solutions from the sheet [here](/SDE_Sheet)! - If you are practicing JavaScript Interview Questions, share your approach and learnings [here](/InterviewQuestions) - Have got Code Snippets in JavaScript? Dump them in [here](/Snippets) - Love taking Notes? We are looking for contributions to [NamasteJS](/NamasteJS) and [EloquentJS](/EloquentJS) currently! diff --git a/ReferenceBooks/JavaScript-The-Definitive-Guide-Master-The-Worlds-Most-Used-Programming-Language-7th-Edition.pdf b/ReferenceBooks/JavaScript-The-Definitive-Guide-Master-The-Worlds-Most-Used-Programming-Language-7th-Edition.pdf new file mode 100644 index 0000000..1ee9fdd Binary files /dev/null and b/ReferenceBooks/JavaScript-The-Definitive-Guide-Master-The-Worlds-Most-Used-Programming-Language-7th-Edition.pdf differ diff --git a/SDE_Sheet/IntersectionOfTwoLinkedLists.js b/SDE_Sheet/IntersectionOfTwoLinkedLists.js new file mode 100644 index 0000000..898fd17 --- /dev/null +++ b/SDE_Sheet/IntersectionOfTwoLinkedLists.js @@ -0,0 +1,56 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ + +/** + * @param {ListNode} headA + * @param {ListNode} headB + * @return {ListNode} + */ +var getIntersectionNode = function(headA, headB) { + + let shortList + let longList + let sizeA = headA + let sizeB = headB + let lengthA = 0 + let lengthB = 0 + while (sizeA!==null || sizeB!==null){ + if(sizeA !== null) { + sizeA = sizeA.next + lengthA ++ + } + if(sizeB !==null) { + sizeB = sizeB.next + lengthB ++ + } + } + //starts the longest list index, to the starting point of short list + //the idea is that an intersection of a node will ALWAYS be at the end. + //therefore, SAME SIZE lists will reach the intersection value at the same time + let sizeDiff = Math.abs(lengthA - lengthB) + + if(lengthB > lengthA) { + shortList = headA + longList = headB + } else { + shortList = headB + longList = headA + } + + while(shortList.next !== null || longList.next !== null){ + if(sizeDiff<1){ + if(shortList === longList) return shortList + shortList = shortList.next + } + longList = longList.next + sizeDiff-- + } + //catch any 1 length lists + if(shortList === longList) return shortList + return shortList.next +}; diff --git a/SDE_Sheet/LongestSubstringWithoutRepeatingCharacters.js b/SDE_Sheet/LongestSubstringWithoutRepeatingCharacters.js new file mode 100644 index 0000000..fae2648 --- /dev/null +++ b/SDE_Sheet/LongestSubstringWithoutRepeatingCharacters.js @@ -0,0 +1,23 @@ +class Solution { + public int lengthOfLongestSubstring(String s) { + int maxl = 0; + Map charIdxMap = new HashMap(); //O(1) space + + char c; + int startIdx = 0; + int i; + for(i=0; i= startIdx){ + maxl = Math.max(maxl, i-startIdx); + startIdx = charIdxMap.get(c) + 1; + } + + charIdxMap.put(c, i); + } + + maxl = Math.max(maxl, i-startIdx); + + return maxl; + } +} diff --git a/SDE_Sheet/MiddleOfTheLinkedList.js b/SDE_Sheet/MiddleOfTheLinkedList.js new file mode 100644 index 0000000..d20f3a3 --- /dev/null +++ b/SDE_Sheet/MiddleOfTheLinkedList.js @@ -0,0 +1,22 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { + public ListNode middleNode(ListNode head) { + ListNode slow = head; + ListNode fast = head; + + while(fast != null && fast.next != null){ + fast = fast.next.next; + slow = slow.next; + } + return slow; + } +} diff --git a/SDE_Sheet/ReverseLinkedList.js b/SDE_Sheet/ReverseLinkedList.js new file mode 100644 index 0000000..12ed87d --- /dev/null +++ b/SDE_Sheet/ReverseLinkedList.js @@ -0,0 +1,29 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { + public ListNode reverseList(ListNode head) { + Stack stack =new Stack(); + while(head!=null) + { + stack.push(head); + head = head.next; + } + ListNode dummy = new ListNode(-1); + head=dummy; + while(!stack.isEmpty()) + { + ListNode current= stack.pop(); + head.next=new ListNode(current.val); + head=head.next; + } + return dummy.next; + } +} diff --git a/SDE_Sheet/twoSum.js b/SDE_Sheet/twoSum.js new file mode 100644 index 0000000..0de5d41 --- /dev/null +++ b/SDE_Sheet/twoSum.js @@ -0,0 +1,23 @@ +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +/* A brute-force approach. Traverse the nums array via two pointers i and j and return + indexes stored in an array if the total matches the target*/ + var twoSum = function(nums, target) { + let targetResult =[]; + + for(let i =0; i< nums.length; i++){ + for(let j = i+1; j< nums.length; j++){ + + if( nums[i]+ nums[j] === target){ + + targetResult.push(i); + targetResult.push(j); + } + } + } + + return targetResult; + }; \ No newline at end of file diff --git a/Snippets/Arrays/arraySubset.js b/Snippets/Arrays/arraySubset.js new file mode 100644 index 0000000..5523e39 --- /dev/null +++ b/Snippets/Arrays/arraySubset.js @@ -0,0 +1,25 @@ +const arr1 = [2, 3, 4, 5, 6, 88, 9]; +const arr2 = [3, 3, 3, 5, 100]; + +function isSubset(arr1, arr2) { + const arr1Set = []; + const arr2Set = []; + + arr1.forEach((item) => { + if (!arr1Set.includes(item)) { + arr1Set.push(item); + } + }); + arr2.forEach((item) => { + if (!arr2Set.includes(item)) { + arr2Set.push(item); + } + }); + + const res = arr1Set.reduce((previousValue, currentValue) => { + return previousValue + +arr2Set.includes(currentValue); + }, 0); + return res === arr2Set.length; +} + +console.log(isSubset(arr1, arr2)); diff --git a/Snippets/Arrays/atPolyfill.js b/Snippets/Arrays/atPolyfill.js new file mode 100644 index 0000000..3853401 --- /dev/null +++ b/Snippets/Arrays/atPolyfill.js @@ -0,0 +1,10 @@ +Array.prototype.myAt = myAt; + +function myAt(index) { + if (index >= 0) { + return this[index]; + } + return this[this.length - -index]; +} + +console.log([1, 2, 3].myAt(-4)); diff --git a/Snippets/Arrays/checkItemPresentInArray.js b/Snippets/Arrays/checkItemPresentInArray.js new file mode 100644 index 0000000..fa16a88 --- /dev/null +++ b/Snippets/Arrays/checkItemPresentInArray.js @@ -0,0 +1,27 @@ +/* + * Problem: Checks whether the dish you wish to eat is available in the menu + */ + +let polarBearMenu = [ + "Chocolate Fudge", + "Death by chocolate", + "Gudbud", + "Fruit zest", +]; + +let isYourDishAvailable = false; +let itemYouWishToEat = "Gudbud"; + +for (let itemNumber = 0; itemNumber < polarBearMenu.length; itemNumber++) { + if (polarBearMenu[itemNumber] === itemYouWishToEat) { + console.log("Yay " + itemYouWishToEat + " is there in the menu"); + isYourDishAvailable = true; + break; + } else { + isYourDishAvailable = false; + } +} + +if (!isYourDishAvailable) { + console.log("Oops! " + itemYouWishToEat + " is not available. Try searching at a different place."); +} diff --git a/Snippets/Arrays/checkItemPresentInArrayUsingMethod.js b/Snippets/Arrays/checkItemPresentInArrayUsingMethod.js new file mode 100644 index 0000000..e88329a --- /dev/null +++ b/Snippets/Arrays/checkItemPresentInArrayUsingMethod.js @@ -0,0 +1,18 @@ +/** + * Problem: Checks whether pizza is available in the menu + */ + +let polarBearMenu = [ + "Chocolate Fudge", + "Death by chocolate", + "Gudbud", + "Fruit zest", +]; + +let itemYouWishToEat = "Gudbud"; + +if (polarBearMenu.includes(itemYouWishToEat)) { + console.log(itemYouWishToEat + " is present in the menu "); +} else { + console.log(itemYouWishToEat + " is not present in the menu"); +} \ No newline at end of file diff --git a/Snippets/Arrays/checkPositionOfElementInArray.js b/Snippets/Arrays/checkPositionOfElementInArray.js new file mode 100644 index 0000000..0553384 --- /dev/null +++ b/Snippets/Arrays/checkPositionOfElementInArray.js @@ -0,0 +1,7 @@ +/** + * Check the position of IPL teams in 2021 + */ + +let iplTeams = ["CSK", "KKR", "DC", "RCB", "MI", "PBKS", "RR", "SRH"]; +let iplTeamPositionYouWantToKnow = "MI"; +console.log(iplTeams.indexOf(iplTeamPositionYouWantToKnow) + 1); diff --git a/Snippets/Arrays/convertArrayToString.js b/Snippets/Arrays/convertArrayToString.js new file mode 100644 index 0000000..9852912 --- /dev/null +++ b/Snippets/Arrays/convertArrayToString.js @@ -0,0 +1,12 @@ +/** + * Convert given array to string + */ + +const numbers = [1, 2, 3, 4, 5]; +console.log(Array.isArray(numbers)); // true since numbers is an array +console.log(numbers.join(",")); // 1,2,3,4,5 +console.log(typeof numbers.join(",")); // string + +const names = ["Javascript", "Python", "PHP"]; +console.log(names.join(",")); // 'Javascript', 'Python', 'PHP' + diff --git a/Snippets/Arrays/filterPolyfill.js b/Snippets/Arrays/filterPolyfill.js new file mode 100644 index 0000000..d70c249 --- /dev/null +++ b/Snippets/Arrays/filterPolyfill.js @@ -0,0 +1,14 @@ +Array.prototype.myFilter = myFilter; + +function myFilter(cb) { + const filteredArr = []; + this.forEach((item) => { + if (cb(item)) { + filteredArr.push(item); + } + }); + return filteredArr; +} + +const arr = [1, 2, 3, 4]; +console.log(arr.myFilter((item) => item !== 2)); diff --git a/Snippets/Arrays/flattenADeeplyNestedArray.js b/Snippets/Arrays/flattenADeeplyNestedArray.js new file mode 100644 index 0000000..379f66c --- /dev/null +++ b/Snippets/Arrays/flattenADeeplyNestedArray.js @@ -0,0 +1,27 @@ +function flattenArray(arr, n = Infinity) { + let res = []; + for (i in arr) { + if (n !== 0 && Array.isArray(arr[i])) { + res.push(...flattenArray(arr[i], n - 1)); + } else { + res.push(arr[i]); + } + } + return res; +} +let input = [ + 1, + 2, + 3, + [4], + [5, 6, [7], [8, [9, [10]]]], + 11, + 12, + 13, + [14, [[[[[15, [16]]]]]]], + 17, + 18, + [19, [20, [21, [22, [23, [24, [[[[[25]]]]]]]]]]], +]; + +console.log(flattenArray(input, 2)); diff --git a/Snippets/Arrays/groupBy.js b/Snippets/Arrays/groupBy.js new file mode 100644 index 0000000..d4d0731 --- /dev/null +++ b/Snippets/Arrays/groupBy.js @@ -0,0 +1,125 @@ +const arr = [6.1, 2.4, 2.7, 6.8]; + +function groupBy(arr, property) { + const groupByString = (arr, query) => { + let res = {}; + + arr.forEach((item, index) => { + const queryList = query.split("."); + + let currKey; + let currItem = item; + let count = 0; + for (let i = 0; i < queryList.length; i++) { + currKey = queryList[i]; + if (!currItem[currKey]) { + break; + } else { + count += 1; + currItem = currItem[currKey]; + } + } + + if (count !== queryList.length) { + currItem = undefined; + } + if (!res[currItem]) { + res[currItem] = [item]; + } else { + res[currItem].push(item); + } + }); + return res; + }; + const groupByCallback = (arr, cb) => { + let res = {}; + arr.forEach((item) => { + const ans = cb.call(null, item); + + if (Object.keys(res).includes(ans.toString())) { + res[ans].push(item); + } else { + res[ans] = [item]; + } + }); + return res; + }; + + let res; + switch (typeof property) { + case "function": + res = groupByCallback(arr, property); + break; + case "string": + res = groupByString(arr, property); + break; + default: + return new Error("Invalid Property Type"); + } + + return res; +} + +// groupBy(arr, Math.floor); +console.log(groupBy(arr, Math.ceil)); +console.log(groupBy([6.1, 4.2, 6.3], Math.floor)); +console.log(groupBy(["one", "two", "three"], "length")); +console.log( + groupBy( + [{ a: { b: { c: 1 } } }, { a: { b: { c: 1 } } }, { a: { b: { c: 2 } } }], + "b.a.c" + ) +); + +console.log( + groupBy( + [{ a: { b: { c: 1 } } }, { a: { b: { c: 1 } } }, { a: { b: { c: 2 } } }], + "a.b.c" + ) +); + +// alternate solution +function groupByAlternative(collection, property) { + const output = {}; + + if (!collection || typeof collection !== "object") { + return output; + } + + const isPropertyFunction = typeof property === "function"; + const isPropertyPath = typeof property === "string"; + + for (const value of Object.values(collection)) { + let current = undefined; + + if (isPropertyFunction) { + current = property(value); + } else if (isPropertyPath) { + // a.b.c -> [a, b, c]; + const path = property.split("."); + let i; + let currentKey; + let currentItem = value; // { a: { b: { c: 1 } } } + + for (i = 0; i < path.length; i++) { + // [a, b, c] -> currentKey: path[0] -> a + // [a, b, c] -> currentKey: path[1] -> b + // [a, b, c] -> currentKey: path[2] -> c + currentKey = path[i]; + + if (!currentItem[currentKey]) { + currentItem = undefined; + break; + } + currentItem = currentItem[currentKey]; + } + + current = currentItem; + } + + output[current] = output[current] || []; + output[current].push(value); + } + + return output; +} diff --git a/Snippets/Arrays/mapPolyfill.js b/Snippets/Arrays/mapPolyfill.js new file mode 100644 index 0000000..4caf00c --- /dev/null +++ b/Snippets/Arrays/mapPolyfill.js @@ -0,0 +1,12 @@ +Array.prototype.myMap = myMap; + +function myMap(cb) { + const newArr = []; + for (let i = 0; i < this.length; i++) { + newArr.push(cb(this[i])); + } + return newArr; +} +const arr = [1, 2, 3, 4, 5]; + +console.log(arr.myMap((item) => item + 2)); diff --git a/Snippets/Arrays/pushPolyfill.js b/Snippets/Arrays/pushPolyfill.js new file mode 100644 index 0000000..ce9c9e2 --- /dev/null +++ b/Snippets/Arrays/pushPolyfill.js @@ -0,0 +1,9 @@ +Array.prototype.myPush = myPush; + +function myPush(item) { + this[this.length] = item; + return this; +} +const arr = [1, 2, 3]; +arr.myPush(2).push(2); // chaining possible +console.log(arr); diff --git a/Snippets/Arrays/reducePolyfill.js b/Snippets/Arrays/reducePolyfill.js new file mode 100644 index 0000000..ede890b --- /dev/null +++ b/Snippets/Arrays/reducePolyfill.js @@ -0,0 +1,17 @@ +Array.prototype.myReduce = myReduce; + +function myReduce(cb, initialValue = this[0]) { + let result = initialValue; + this.forEach((item) => { + result = cb(result, item); + }); + return result; +} + +const array1 = [1, 2, 3, 4]; +const sumWithInitial = array1.myReduce( + (previousValue, currentValue) => previousValue + currentValue, + 0 +); + +console.log(sumWithInitial); diff --git a/Snippets/Arrays/removeDuplicates.js b/Snippets/Arrays/removeDuplicates.js new file mode 100644 index 0000000..a493295 --- /dev/null +++ b/Snippets/Arrays/removeDuplicates.js @@ -0,0 +1,16 @@ +const arr = [1, 2, 2, 3]; + +const updatedArrWithSet = [...new Set(arr)]; + +const updatedArrWithFilter = arr.filter((c, index) => { + return index !== arr.indexOf(c); +}); + +const updatedArrWithIncludes = []; +arr.forEach((c) => { + if (!updatedArrWithIncludes.includes(c)) { + updatedArrWithIncludes.push(c); + } +}); + +console.log(updatedArrWithIncludes); diff --git a/Snippets/Arrays/unshiftPolyfill.js b/Snippets/Arrays/unshiftPolyfill.js new file mode 100644 index 0000000..dfa1f65 --- /dev/null +++ b/Snippets/Arrays/unshiftPolyfill.js @@ -0,0 +1,19 @@ +Array.prototype.myUnshift = myUnshift; + +function myUnshift(item) { + let prevItem = item; + const originalLength = this.length; + for (let i = 0; i <= originalLength; i++) { + let temp = this[i]; + this[i] = prevItem; + prevItem = temp; + } + return this; +} + +const arr = [1, 2, 3, 4]; +arr + .myUnshift(2) + .myUnshift(100) + .myUnshift(() => console.log("WOW")); +console.log(arr); diff --git a/Snippets/Closures/armyOfFunctions.js b/Snippets/Closures/armyOfFunctions.js new file mode 100644 index 0000000..9157e9a --- /dev/null +++ b/Snippets/Closures/armyOfFunctions.js @@ -0,0 +1,23 @@ +function makeArmy() { + let shooters = []; + + let i = 0; + while (i < 10) { + // we can use for loop with let i to avoid the problem + let a = i; // avoid the closure problem of referencing same variable + let shooter = function () { + // create a shooter function, + console.log(a); // that should show its number + }; + shooters.push(shooter); // and add it to the array + i++; + } + + // ...and return the array of shooters + return shooters; +} + +let army = makeArmy(); + +army[1](); +army[9](); diff --git a/Snippets/Closures/filterThroughFunction.js b/Snippets/Closures/filterThroughFunction.js new file mode 100644 index 0000000..00a02c4 --- /dev/null +++ b/Snippets/Closures/filterThroughFunction.js @@ -0,0 +1,18 @@ +const inBetween = (left, right) => { + return function (x) { + return x >= left && x <= right; + }; +}; + +const inArray = (arr) => { + return function (x) { + return arr.includes(x); + }; +}; + +let arr = [1, 2, 3, 4, 5, 6, 7]; + +console.log(arr.filter(inBetween(3, 6))); +// or console.log(arr.filter((item) => inBetween(3, 6)(item))); + +console.log(arr.filter(inArray([1, 2, 10]))); diff --git a/Snippets/Closures/makeCounter.js b/Snippets/Closures/makeCounter.js new file mode 100644 index 0000000..1ae6657 --- /dev/null +++ b/Snippets/Closures/makeCounter.js @@ -0,0 +1,17 @@ +function makeCounter() { + let count = 0; + + return function () { + return ++count; + }; +} + +let counter = makeCounter(); + +counter(); +counter(); +counter(); + +const count = counter(); + +console.log(count); diff --git a/Snippets/Closures/sortByFields.js b/Snippets/Closures/sortByFields.js new file mode 100644 index 0000000..f299a4c --- /dev/null +++ b/Snippets/Closures/sortByFields.js @@ -0,0 +1,18 @@ +let users = [ + { name: "John", age: 20, surname: "Johnson" }, + { name: "Pete", age: 18, surname: "Peterson" }, + { name: "Ann", age: 19, surname: "Hathaway" }, +]; + +// Normal Approach will be +//users.sort((a, b) => (a.name > b.name ? 1 : -1)); +//users.sort((a, b) => (a.age > b.age ? 1 : -1)); + +// cleaner way +const byField = (fieldName) => { + return function (x, y) { + return x[fieldName] > y[fieldName] ? 1 : -1; + }; +}; + +console.log(users.sort(byField("age"))); diff --git a/Snippets/Currying/advancedCurryImplementation.js b/Snippets/Currying/advancedCurryImplementation.js new file mode 100644 index 0000000..a8debcf --- /dev/null +++ b/Snippets/Currying/advancedCurryImplementation.js @@ -0,0 +1,33 @@ +function sum(a, b, c) { + return a + b + c; +} + +// function curry(fn) { +// return function curried(...args) { +// if (args.length >= fn.length) { +// return fn.apply(this, args); +// } else { +// return function (...args2) { +// return curried.apply(this, args.concat(args2)); +// }; +// } +// }; +// } + +function curry(func) { + return function curried(...args) { + if (args.length >= func.length) { + return func.apply(this, args); + } else { + return function (...args2) { + return curried.apply(this, args.concat(args2)); + }; + } + }; +} + +let curriedSum = curry(sum); + +console.log(curriedSum(1, 2, 3)); // 6, still callable normally +console.log(curriedSum(1)(2, 3)); // 6, currying of 1st arg +console.log(curriedSum(1)(2)(3)); diff --git a/Snippets/Currying/sumOfNNumbers.js b/Snippets/Currying/sumOfNNumbers.js new file mode 100644 index 0000000..bdf2008 --- /dev/null +++ b/Snippets/Currying/sumOfNNumbers.js @@ -0,0 +1,11 @@ +let sum = function (a) { + return function (b) { + if (b) { + return sum(a + b); + } + return a; + }; +}; + +const total = sum(1)(2)(3)(); +console.log(total); diff --git a/Snippets/DataTypes/methodsOfPrimitives.js b/Snippets/DataTypes/methodsOfPrimitives.js new file mode 100644 index 0000000..e69de29 diff --git a/Snippets/FunctionalProgramming/General/arrayLength.js b/Snippets/FunctionalProgramming/General/arrayLength.js index bc17b1a..b1fd1e6 100644 --- a/Snippets/FunctionalProgramming/General/arrayLength.js +++ b/Snippets/FunctionalProgramming/General/arrayLength.js @@ -1,3 +1,5 @@ +// finds array length + function arrayLength(arr){ let len = 0; while(arr[len] !== undefined){ diff --git a/Snippets/Functions/bind.js b/Snippets/Functions/bind.js new file mode 100644 index 0000000..001aa36 --- /dev/null +++ b/Snippets/Functions/bind.js @@ -0,0 +1,21 @@ +let user = { + firstName: "John", + sayHi() { + alert(`Hello, ${this.firstName}!`); + }, +}; + +let sayHi = user.sayHi.bind(user); // (*) + +// can run it without an object +sayHi(); // Hello, John! + +setTimeout(sayHi, 1000); // Hello, John! + +// even if the value of user changes within 1 second +// sayHi uses the pre-bound value which is reference to the old user object +user = { + sayHi() { + alert("Another user in setTimeout!"); + }, +}; diff --git a/Snippets/Functions/bindAll.js b/Snippets/Functions/bindAll.js new file mode 100644 index 0000000..8d1acf2 --- /dev/null +++ b/Snippets/Functions/bindAll.js @@ -0,0 +1,19 @@ +// If an object has many methods and we plan to actively pass it around, then we could bind them all in a loop + +const user = { + func1() { + console.log("func 1"); + }, + func2() { + console.log("func 2"); + }, + func3() { + console.log("func 3"); + }, +}; + +for (let key in user) { + if (typeof user[key] == "function") { + user[key] = user[key].bind(user); + } +} diff --git a/Snippets/Functions/boundFunctionMethod.js b/Snippets/Functions/boundFunctionMethod.js new file mode 100644 index 0000000..4e2d792 --- /dev/null +++ b/Snippets/Functions/boundFunctionMethod.js @@ -0,0 +1,11 @@ +"use strict"; +function f() { + console.log(this); // null +} + +let user = { + g: f.bind(null), +}; + +// f(); +user.g(); diff --git a/Snippets/Functions/call.js b/Snippets/Functions/call.js new file mode 100644 index 0000000..76fc2eb --- /dev/null +++ b/Snippets/Functions/call.js @@ -0,0 +1,27 @@ +"use strict"; +let worker = { + someMethod() { + return 1; + }, + slow(x) { + console.log("Called with", x); + return x + this.someMethod(); // need a reference for this + }, +}; + +function cachingDecorator(func) { + const cache = new Map(); + return function (x) { + if (cache.has(x)) { + return cache.get(x); + } + const res = func.call(this, x); + cache.set(x, res); + return res; + }; +} + +worker.slow = cachingDecorator(worker.slow); // now make it caching + +console.log(worker.slow(2)); // works +console.log(worker.slow(2)); // works, doesn't call the original (cached) diff --git a/Snippets/Functions/customSetInterval.js b/Snippets/Functions/customSetInterval.js new file mode 100644 index 0000000..a3a7080 --- /dev/null +++ b/Snippets/Functions/customSetInterval.js @@ -0,0 +1,15 @@ +const mySetInterval = (callback, delay, ...args) => { + let timerId = setTimeout( + function tick(...args) { + callback(...args); + timerId = setTimeout(tick, delay, ...args); + }, + delay, + ...args + ); +}; + +function cb(x, y) { + console.log(x, y); +} +mySetInterval(cb, 2000, 1, 2); diff --git a/Snippets/Functions/debounceDecorator.js b/Snippets/Functions/debounceDecorator.js new file mode 100644 index 0000000..a3381c4 --- /dev/null +++ b/Snippets/Functions/debounceDecorator.js @@ -0,0 +1,18 @@ +function debounce(func, delay) { + let timeout; + return function (...args) { + if (timeout) { + clearTimeout(timeout); + } + timeout = setTimeout(() => { + func(...args); + // func.apply(this, args) + }, delay); + }; +} + +let f = debounce(console.log, 2000); + +f("a"); +setTimeout(() => f("b"), 200); +setTimeout(() => f("c"), 500); diff --git a/Snippets/Functions/delayDecorator.js b/Snippets/Functions/delayDecorator.js new file mode 100644 index 0000000..ba21328 --- /dev/null +++ b/Snippets/Functions/delayDecorator.js @@ -0,0 +1,18 @@ +function f(x) { + console.log(x); +} + +function delay(func, delay) { + return function (...args) { + setTimeout(() => { + func.call(this, ...args); + }, delay); + }; +} + +// create wrappers +let f1000 = delay(f, 1000); +let f1500 = delay(f, 1500); + +f1000("test"); // shows "test" after 1000ms +f1500("test"); // shows "test" after 1500ms diff --git a/Snippets/Functions/fixFunctionThis.js b/Snippets/Functions/fixFunctionThis.js new file mode 100644 index 0000000..4c12b1e --- /dev/null +++ b/Snippets/Functions/fixFunctionThis.js @@ -0,0 +1,21 @@ +function askPassword(ok, fail) { + let password = "rockstar"; + if (password == "rockstar") ok(); + else fail(); +} + +let user = { + name: "John", + + loginOk() { + console.log(`${this.name} logged in`); + }, + + loginFail() { + console.log(`${this.name} failed to log in`); + }, +}; + +askPassword(user.loginOk.bind(user), user.loginFail.bind(user)); + +// askPassword(() => user.loginOk(), () => user.loginFail()); diff --git a/Snippets/Functions/globalPartialFunctions.js b/Snippets/Functions/globalPartialFunctions.js new file mode 100644 index 0000000..7df8409 --- /dev/null +++ b/Snippets/Functions/globalPartialFunctions.js @@ -0,0 +1,22 @@ +function partial(func, ...argsBound) { + return function (...args) { + // (*) + return func.call(this, ...argsBound, ...args); + }; +} + +// Usage: +let user = { + firstName: "John", + say(time, phrase) { + alert(`[${time}] ${this.firstName}: ${phrase}!`); + }, +}; + +// add a partial method with fixed time +user.sayNow = partial( + user.say, + new Date().getHours() + ":" + new Date().getMinutes() +); + +user.sayNow("Hello"); diff --git a/Snippets/Functions/makeCounter.js b/Snippets/Functions/makeCounter.js new file mode 100644 index 0000000..1d90f60 --- /dev/null +++ b/Snippets/Functions/makeCounter.js @@ -0,0 +1,24 @@ +const makeCounter = () => { + const counter = () => { + return counter.count++; + }; + counter.set = (val) => { + counter.count = val; + return counter.count; + }; + + counter.decrease = () => { + counter.count--; + return counter.count; + }; + + counter.count = 0; + + return counter; +}; + +const counter = makeCounter(); + +console.log(counter()); +console.log(counter.set(10)); +console.log(counter.decrease()); diff --git a/Snippets/Functions/memoizeOne.js b/Snippets/Functions/memoizeOne.js new file mode 100644 index 0000000..db4db13 --- /dev/null +++ b/Snippets/Functions/memoizeOne.js @@ -0,0 +1,38 @@ +/** + * Do not change the function name + **/ + +function memoizeOne(fn, isEqual) { + let cache = {}; + + function hasher(args) { + return args.join(","); + } + return function (...args) { + const hash = hasher(args); + if (cache[hash]) { + return cache[hash]; + } + cache = {}; + const res = fn.apply(null, args); + cache[hash] = res; + return res; + }; +} + +function add(a, b) { + console.print("called"); + return a + b; +} + +function isEqual(a, b) { + if (a === b) { + return true; + } + return false; +} +const memoizeAdd = memoizeOne(add, isEqual); +console.print(memoizeAdd(2, 3)); +console.print(memoizeAdd(2, 3)); +console.print(memoizeAdd(3, 4)); +console.print(memoizeAdd(5, 6)); diff --git a/Snippets/Functions/multiArgumentCaching.js b/Snippets/Functions/multiArgumentCaching.js new file mode 100644 index 0000000..33618d6 --- /dev/null +++ b/Snippets/Functions/multiArgumentCaching.js @@ -0,0 +1,30 @@ +const worker = { + slow(min, max) { + console.log(`Called with ${min},${max}`); + return min + max; + }, +}; + +function cachingDecorator(func, hash) { + const cache = new Map(); + return function (...args) { + let key = hash(args); + if (cache.has(key)) { + return cache.get(key); + } + let res = func.apply(this, args); + // or let res = func.call(this, ...args) + cache.set(key, res); + return res; + }; +} + +function hash(args) { + // [].join.call(arguments) + return args.join(); +} + +worker.slow = cachingDecorator(worker.slow, hash); + +console.log(worker.slow(3, 5)); // works +console.log("Again " + worker.slow(3, 5)); // same (cached) diff --git a/Snippets/Functions/namedFunctionExpression.js b/Snippets/Functions/namedFunctionExpression.js new file mode 100644 index 0000000..1adfb70 --- /dev/null +++ b/Snippets/Functions/namedFunctionExpression.js @@ -0,0 +1,12 @@ +let sayHi = function func(who) { + if (who) { + console.log(`Hello, ${who}`); + } else { + func("Guest"); // Error: sayHi is not a function + } +}; + +let welcome = sayHi; +sayHi = null; + +welcome(); // Error, the nested sayHi call doesn't work any more! diff --git a/Snippets/Functions/nestedSetTimeout.js b/Snippets/Functions/nestedSetTimeout.js new file mode 100644 index 0000000..7bbe30d --- /dev/null +++ b/Snippets/Functions/nestedSetTimeout.js @@ -0,0 +1,12 @@ +let delay = 5000; + +let timerId = setTimeout(function request() { + console.log("Send Request to server", delay); + if (true) { + // request failed due to server overload + // increase the interval to the next run + delay *= 2; + } + + timerId = setTimeout(request, delay); +}, delay); diff --git a/Snippets/Functions/newFunctionSyntax.js b/Snippets/Functions/newFunctionSyntax.js new file mode 100644 index 0000000..217d9e2 --- /dev/null +++ b/Snippets/Functions/newFunctionSyntax.js @@ -0,0 +1,7 @@ +let sum = new Function("a", "b", "return a + b"); + +alert(sum(1, 2)); // 3 + +let sayHi = new Function('alert("Hello")'); + +sayHi(); // Hello diff --git a/Snippets/Functions/outputEverySecond.js b/Snippets/Functions/outputEverySecond.js new file mode 100644 index 0000000..50f4306 --- /dev/null +++ b/Snippets/Functions/outputEverySecond.js @@ -0,0 +1,50 @@ +function printNumbers(from, to) { + const timerId = setInterval( + function f(to) { + console.log(from); + if (to === from) { + clearInterval(timerId); + } + from = from + 1; + }, + 1000, + to + ); +} + +printNumbers(1, 10); + +// with nested setTimeout + +function printNumbers(from, to) { + let current = from; + + setTimeout(function go() { + alert(current); + if (current < to) { + setTimeout(go, 1000); + } + current++; + }, 1000); +} + +// usage: +printNumbers(5, 10); + +// runs immediately +function printNumbers(from, to) { + let current = from; + + function go() { + alert(current); + if (current == to) { + clearInterval(timerId); + } + current++; + } + + go(); + let timerId = setInterval(go, 1000); +} + +printNumbers(5, 10); diff --git a/Snippets/Functions/partialFunctions.js b/Snippets/Functions/partialFunctions.js new file mode 100644 index 0000000..fd674d6 --- /dev/null +++ b/Snippets/Functions/partialFunctions.js @@ -0,0 +1,22 @@ +function mul(a, b) { + return a * b; +} + +const triple = mul.bind(null, 3); // alternative to this is to curry functions + +console.log(triple(4)); + +const user = { + firstName: "Samyak Shah", +}; +function send(from, text, to) { + return ` + By: ${from} + To: ${to} + Message: + ${text}, ${this.firstName}`; +} + +const sendTo = send.bind(user, "soham@invact.com"); + +console.log(sendTo("Hello World", "samyak@neog.com")); diff --git a/Snippets/Functions/partialLogin.js b/Snippets/Functions/partialLogin.js new file mode 100644 index 0000000..216436d --- /dev/null +++ b/Snippets/Functions/partialLogin.js @@ -0,0 +1,15 @@ +function askPassword(ok, fail) { + let password = prompt("Password?", ""); + if (password == "rockstar") ok(); + else fail(); +} + +let user = { + name: "John", + + login(result) { + alert(this.name + (result ? " logged in" : " failed to log in")); + }, +}; + +askPassword(user.login.bind(user, true), user.login.bind(user, false)); // ? diff --git a/Snippets/Functions/polymorphism.js b/Snippets/Functions/polymorphism.js new file mode 100644 index 0000000..a218e64 --- /dev/null +++ b/Snippets/Functions/polymorphism.js @@ -0,0 +1,19 @@ +function ask(question, ...handlers) { + let isYes = confirm(question); + + for (let handler of handlers) { + if (handler.length == 0) { + if (isYes) handler(); + } else { + handler(isYes); + } + } +} + +// for positive answer, both handlers are called +// for negative answer, only the second one +ask( + "Question?", + () => alert("You said yes"), + (result) => alert(result) +); diff --git a/Snippets/Functions/properties.js b/Snippets/Functions/properties.js new file mode 100644 index 0000000..3ebcb14 --- /dev/null +++ b/Snippets/Functions/properties.js @@ -0,0 +1,48 @@ +function sayHi() { + console.log("Hi"); +} + +console.log(sayHi.name); +console.log(function () {}.name); // empty string + +function f1(a) {} +function f2(a, b) {} +function many(a, b, ...more) {} + +console.log(f1.length); // 1 +console.log(f2.length); // 2 +console.log(many.length); // 2 + +// custom property + +function sayHi() { + console.log("Hi"); + + // let's count how many times we run + sayHi.counter++; +} +sayHi.counter = 0; // initial value + +sayHi(); // Hi +sayHi(); // Hi + +console.log(`Called ${sayHi.counter} times`); // Called 2 times + +// counter with custom property + +function makeCounter() { + // instead of: + // let count = 0 + + function counter() { + return counter.count++; + } + + counter.count = 0; + + return counter; +} + +let counter = makeCounter(); +alert(counter()); // 0 +alert(counter()); // 1 diff --git a/Snippets/Functions/secondBind.js b/Snippets/Functions/secondBind.js new file mode 100644 index 0000000..4684187 --- /dev/null +++ b/Snippets/Functions/secondBind.js @@ -0,0 +1,10 @@ +// A function cannot be re-bound +function f() { + console.log(this.name); +} + +f = f.bind({ name: "John" }); +z = f.bind({ name: "Ann" }); + +f(); // John +z(); // John diff --git a/Snippets/Functions/setInterval.js b/Snippets/Functions/setInterval.js new file mode 100644 index 0000000..40e0b84 --- /dev/null +++ b/Snippets/Functions/setInterval.js @@ -0,0 +1,4 @@ +// let timerId = setInterval(func|code, [delay], [arg1], [arg2], ...) + +// repeat with the interval of 2 seconds +let timerId = setInterval(() => alert("tick"), 2000); diff --git a/Snippets/Functions/setTimeout.js b/Snippets/Functions/setTimeout.js new file mode 100644 index 0000000..7983b60 --- /dev/null +++ b/Snippets/Functions/setTimeout.js @@ -0,0 +1,20 @@ +// let timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...) + +function sayHi(phrase, who) { + alert(phrase + ", " + who); +} + +setTimeout(sayHi, 1000, "Hello", "John"); // Hello, John + +// string also works +setTimeout("alert('Hello')", 1000); + +// recommended +setTimeout(() => alert("Hello"), 1000); + +// wrong! +setTimeout(sayHi(), 1000); + +// clear timeout +let timerId = setTimeout(() => alert("Hi")); +clearTimeout(timerId); diff --git a/Snippets/Functions/spyDecorator.js b/Snippets/Functions/spyDecorator.js new file mode 100644 index 0000000..0d769cd --- /dev/null +++ b/Snippets/Functions/spyDecorator.js @@ -0,0 +1,29 @@ +function work(a, b) { + console.log(a + b); // work is an arbitrary function or method +} + +function spy(func) { + wrapper.calls = []; + function wrapper(...args) { + const key = hash(args); + func.call(this, ...args); + wrapper.calls.push(key); + } + + return wrapper; +} + +const hash = (args) => { + return args.join(); +}; + +work = spy(work); + +work(1, 2); // 3 +work(4, 5); // 9 + +console.log(work); + +for (let args of work.calls) { + console.log("call:" + args); // "call:1,2", "call:4,5" +} diff --git a/Snippets/Functions/sumWithArbitaryAmountOfBrackets.js b/Snippets/Functions/sumWithArbitaryAmountOfBrackets.js new file mode 100644 index 0000000..5ce5198 --- /dev/null +++ b/Snippets/Functions/sumWithArbitaryAmountOfBrackets.js @@ -0,0 +1,32 @@ +const sum = (x) => { + let total = x; + return function by(y) { + if (y === undefined) { + return total; + } else { + total += y; + return by; + } + }; +}; + +console.log(sum(1)(2)(5)()); + +// But what if we need exactly like this: sum(1)(2)(5) for the result +function sum(a) { + let currentSum = a; + + function f(b) { + currentSum += b; + return f; + } + + f.toString = function () { + return currentSum; + }; + + return f; +} + +const val = sum(1)(8); // 3 +alert(val); // alert calls val.toString(). We can modify this function for our use case diff --git a/Snippets/Functions/throttleDecorator.js b/Snippets/Functions/throttleDecorator.js new file mode 100644 index 0000000..fbc0daf --- /dev/null +++ b/Snippets/Functions/throttleDecorator.js @@ -0,0 +1,19 @@ +function throttle(func, delay) { + let flag = true; + return function (...args) { + if (flag) { + flag = false; + setTimeout(() => { + func.apply(this, args); + flag = true; + }); + } + }; +} + +const f = (text) => { + console.log(text); +}; + +const f1000 = throttle(f, 1000); +f1000("Hi I am 1000s throttle function"); diff --git a/Snippets/Functions/timeBomb.js b/Snippets/Functions/timeBomb.js new file mode 100644 index 0000000..f72451a --- /dev/null +++ b/Snippets/Functions/timeBomb.js @@ -0,0 +1,10 @@ +// 5,4,3,2,1, Bang! + +function timeBomb(i) { + return i === 0 ? "Bang" : i; +} +for (let i = 5; i >= 0; i--) { + setTimeout(() => { + console.log(timeBomb(i)); + }, [(5 - i) * 1000]); +} diff --git a/Snippets/Functions/transparentCaching.js b/Snippets/Functions/transparentCaching.js new file mode 100644 index 0000000..ef3f4a2 --- /dev/null +++ b/Snippets/Functions/transparentCaching.js @@ -0,0 +1,26 @@ +const delay = (data, ms) => { + setTimeout(() => console.log("computing", data), ms); +}; +let slow = async (x) => { + await delay(x, 3000); + return x; +}; + +const cachingDecorator = (func) => { + const cache = new Map(); + return function (x) { + if (cache.has(x)) { + return cache.get(x); + } + const res = func(x); + cache.set(x, res); + return res; + }; +}; + +slow = cachingDecorator(slow); + +slow(4).then(slow(5)).then(slow(4)); + +console.log(await slow(5)); +console.log(await slow(4)); diff --git a/Snippets/Objects/accumulator.js b/Snippets/Objects/accumulator.js new file mode 100644 index 0000000..5b5098b --- /dev/null +++ b/Snippets/Objects/accumulator.js @@ -0,0 +1,13 @@ +function Accumulator(initValue) { + this.value = initValue; + this.read = (payload) => { + this.value += payload; + }; +} + +let accumulator = new Accumulator(1); // initial value 1 + +accumulator.read(1); +accumulator.read(8); + +console.log(accumulator.value); diff --git a/Snippets/Objects/bindPolyFill.js b/Snippets/Objects/bindPolyFill.js new file mode 100644 index 0000000..633ecd0 --- /dev/null +++ b/Snippets/Objects/bindPolyFill.js @@ -0,0 +1,18 @@ +Function.prototype.myBind = myBind; + +function myBind(context, ...args1) { + const newContext = this; + return function (...args2) { + newContext.apply(context, [...args1, ...args2]); + }; +} + +let user = { + firstName: "John", + sayHi(area, birthdate) { + console.log(`Hello, ${this.firstName} from ${area}, born on ${birthdate}!`); + }, +}; + +let sayHi = user.sayHi.myBind(user, "Ahmedabad"); // (*) +sayHi("26/06"); diff --git a/Snippets/Objects/calculator.js b/Snippets/Objects/calculator.js new file mode 100644 index 0000000..fc4fb44 --- /dev/null +++ b/Snippets/Objects/calculator.js @@ -0,0 +1,90 @@ +class Calculator { + constructor(a, b) { + this.a = a; + this.b = b; + } + + sum() { + console.log(this.a); + console.log(this.a + this.b); + return this; + } + + mul() { + console.log(this.a * this.b); + return this; + } +} + +const calculator = new Calculator(1, 2); +calculator.sum(); +calculator.mul(); + +// alternate way + +function Calc(a, b) { + this.a = a; + this.b = b; + + this.sum = () => { + return this.a + this.b; + }; + this.mul = () => { + return this.a * this.b; + }; +} + +const calc = new Calc(5, 2); +console.log(calc.sum()); +console.log(calc.mul()); + +// approach 2: with chaining + +function Calculator(initialValue) { + this.total = initialValue; + this.sum = function (a) { + this.total += a; + return this; + }; + this.sub = function (a) { + this.total -= a; + return this; + }; + this.misc = function (cb) { + this.total = cb(this.total); + return this; + }; + this.val = function () { + return this.total; + }; +} + +const cal = new Calculator(2); +console.print( + cal + .sum(2) + .misc((val) => val + 100) + .val() +); + +// approach 3: using functions + +function cal(initialValue) { + let total = initialValue; + + function add(a) { + total += a; + return cal(total); + } + + function val() { + return total; + } + + return { + add, + val, + }; +} + +console.print(cal(2).add(2).add(2).val()); diff --git a/Snippets/Objects/cloneObject.js b/Snippets/Objects/cloneObject.js new file mode 100644 index 0000000..093088c --- /dev/null +++ b/Snippets/Objects/cloneObject.js @@ -0,0 +1,40 @@ +// using copying properties + +let user = { + name: "John", + age: 30, +}; + +let clone = {}; // the new empty object + +// let's copy all user properties into it +for (let key in user) { + clone[key] = user[key]; +} + +// now clone is a fully independent object with the same content +clone.name = "Pete"; // changed the data in it + +console.log(user.name); // still John in the original object + +// Using Object.assign + +let permissions1 = { canView: true }; +let permissions2 = { canEdit: true }; + +// copies all properties from permissions1 and permissions2 into user +Object.assign(user, permissions1, permissions2); + +// now user = { name: "John", canView: true, canEdit: true } + +Object.assign(user, { name: "Pete" }); + +console.log(user.name); // now user = { name: "Pete" } + +let clone2 = Object.assign({}, user); // shallow copy + +console.log(clone2); + +// clone in a different way + +let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj)); diff --git a/Snippets/Objects/computedProperties.js b/Snippets/Objects/computedProperties.js new file mode 100644 index 0000000..424e15f --- /dev/null +++ b/Snippets/Objects/computedProperties.js @@ -0,0 +1,12 @@ +const obj = {}; + +for (let i = 0; i < 10; i++) { + obj[`field${i}`] = 1; +} + +console.log(obj); + +let fruit = "apple"; +let bag = { + [fruit + "Computers"]: 5, // bag.appleComputers = 5 +}; diff --git a/Snippets/Objects/deepCopy.js b/Snippets/Objects/deepCopy.js new file mode 100644 index 0000000..e335bce --- /dev/null +++ b/Snippets/Objects/deepCopy.js @@ -0,0 +1,20 @@ +const x = { b: [{ a: [1, 2, 3], b: [4, 5, 6], c: [7, 8, 9] }] }; +const y = { b: [{ a: [1, 2, 3], b: [4, 5, 6], c: [7, 8, 9] }] }; + +const z = deepCopy(x); + +function deepCopy(obj) { + let target = {}; + const keys = Object.keys(obj); + keys.forEach((key) => { + if (typeof obj[key] === "object") { + target[key] = deepCopy(obj[key]); + } else { + target[key] = obj[key]; + } + }); + return target; +} + +console.log(z); +console.log(z === x); diff --git a/Snippets/Objects/immediatelyCalledConstructorFunction.js b/Snippets/Objects/immediatelyCalledConstructorFunction.js new file mode 100644 index 0000000..e974d0e --- /dev/null +++ b/Snippets/Objects/immediatelyCalledConstructorFunction.js @@ -0,0 +1,9 @@ +// create a function and immediately call it with new +let user = new (function () { + this.name = "John"; + this.isAdmin = false; + + // ...other code for user creation + // maybe complex logic and statements + // local variables etc +})(); diff --git a/Snippets/Objects/isEmpty.js b/Snippets/Objects/isEmpty.js new file mode 100644 index 0000000..75fee18 --- /dev/null +++ b/Snippets/Objects/isEmpty.js @@ -0,0 +1,11 @@ +const isEmpty = (obj) => { + return Object.keys(obj).length; +}; + +let schedule = {}; + +console.log(isEmpty(schedule)); // true + +schedule["8:30"] = "get up"; + +console.log(isEmpty(schedule)); // false diff --git a/Snippets/Objects/ladder.js b/Snippets/Objects/ladder.js new file mode 100644 index 0000000..c1482d4 --- /dev/null +++ b/Snippets/Objects/ladder.js @@ -0,0 +1,19 @@ +// chaining + +let ladder = { + step: 0, + up() { + this.step++; + return this; + }, + down() { + this.step--; + return this; + }, + showStep: function () { + // shows the current step + console.log(this.step); + }, +}; + +ladder.up().down(); diff --git a/Snippets/Objects/methodsAndthis.js b/Snippets/Objects/methodsAndthis.js new file mode 100644 index 0000000..bf2b805 --- /dev/null +++ b/Snippets/Objects/methodsAndthis.js @@ -0,0 +1,36 @@ +let user = { + name: "Soham", + age: 5, + sayHi: function () { + console.log("Hi " + this.name); + }, +}; + +let user2 = { + name: "Ashutosh", + age: 15, +}; + +function makeUser() { + return { + name: "John", + ref: this, + }; +} + +let user3 = makeUser(); + +console.log(user3.ref); // undefined in node / Error in browser + +function makeUser2() { + return { + name: "John", + ref() { + return this; + }, + }; +} + +let user4 = makeUser2(); + +console.log(user4.ref().name); // John diff --git a/Snippets/Objects/multiplyNumeric.js b/Snippets/Objects/multiplyNumeric.js new file mode 100644 index 0000000..2fdfb41 --- /dev/null +++ b/Snippets/Objects/multiplyNumeric.js @@ -0,0 +1,16 @@ +const multiplyNumeric = (obj) => { + for (key in obj) { + if (typeof obj[key] === "number") { + obj[key] *= 2; + } + } + console.log(obj); +}; +// before the call +let menu = { + width: 200, + height: 300, + title: "My menu", +}; + +multiplyNumeric(menu); diff --git a/Snippets/Objects/objectToPrimitiveConversion.js b/Snippets/Objects/objectToPrimitiveConversion.js new file mode 100644 index 0000000..e69de29 diff --git a/Snippets/Objects/orderedLikeObject.js b/Snippets/Objects/orderedLikeObject.js new file mode 100644 index 0000000..7ac6166 --- /dev/null +++ b/Snippets/Objects/orderedLikeObject.js @@ -0,0 +1,25 @@ +let codes = { + "+49": "Germany", + "+41": "Switzerland", + "+44": "Great Britain", + // .., + "+1": "USA", +}; + +for (let code in codes) { + console.log(+code); // 49, 41, 44, 1 +} + +codes = { + 49: "Germany", + 41: "Switzerland", + 44: "Great Britain", + // .., + 1: "USA", +}; + +console.log(codes); + +for (let code in codes) { + console.log(code); // 1, 41, 44, 49 +} diff --git a/Snippets/Objects/propertyDescriptors.js b/Snippets/Objects/propertyDescriptors.js new file mode 100644 index 0000000..d84689a --- /dev/null +++ b/Snippets/Objects/propertyDescriptors.js @@ -0,0 +1,30 @@ +let user = { + name: "John", +}; + +// get property +let descriptor = Object.getOwnPropertyDescriptor(user, "name"); + +console.log(descriptor); + +// define property +Object.defineProperty(user, "name", { + value: "Soham", +}); + +// non-writable +Object.defineProperty(user, "name", { + writable: false, +}); + +// non-enumerable + +Object.defineProperty(user, "toString", { + enumerable: false, +}); + +// non-configurable + +Object.defineProperty(user, "name", { configurable: false }); + +// cannot even change other ennumberable and writable diff --git a/Snippets/Objects/propertyGetterSetter.js b/Snippets/Objects/propertyGetterSetter.js new file mode 100644 index 0000000..9bd6a80 --- /dev/null +++ b/Snippets/Objects/propertyGetterSetter.js @@ -0,0 +1,31 @@ +let obj = { + name: "Soham", + surname: "Shah", + get fullName() { + return `${this.name} ${this.surname}`; + }, + set propName(value) { + [this.name, this.surname] = value.split(" "); + }, +}; + +// hiding a variable + +let user = { + get name() { + return this._name; + }, + + set name(value) { + if (value.length < 4) { + alert("Name is too short, need at least 4 characters"); + return; + } + this._name = value; + }, +}; + +user.name = "Pete"; +alert(user.name); // Pete + +user.name = ""; diff --git a/Snippets/Objects/reference.js b/Snippets/Objects/reference.js new file mode 100644 index 0000000..89ea186 --- /dev/null +++ b/Snippets/Objects/reference.js @@ -0,0 +1,17 @@ +const user = { + name: "Soham", +}; + +const admin = user; + +console.log(admin); + +admin.name = "Dhruvi"; + +console.log(admin); + +user.name = "Samyak"; + +console.log(admin); + +console.log(admin == { name: "Samyak" }); diff --git a/Snippets/Objects/restAndspread.js b/Snippets/Objects/restAndspread.js new file mode 100644 index 0000000..8c8792a --- /dev/null +++ b/Snippets/Objects/restAndspread.js @@ -0,0 +1,71 @@ +// Rest + +function sumAll(...args) { + // args is the name for the array + let sum = 0; + + for (let arg of args) sum += arg; + + return sum; +} + +console.log(sumAll(1, 2, 3)); + +function showName(firstName, lastName, ...titles) { + console.log(firstName + " " + lastName); // Julius Caesar + + // the rest go into titles array + // i.e. titles = ["Consul", "Imperator"] + console.log(titles[0]); // Consul + console.log(titles[1]); // Imperator + console.log(titles.length); // 2 +} + +showName("Julius", "Caesar", "Consul", "Imperator"); + +// arguments array + +function showName2() { + console.log(arguments.length); + console.log(arguments[0]); + console.log(arguments[1]); + + // it's iterable + // for(let arg of arguments) console.log(arg); +} + +// shows: 2, Julius, Caesar +showName2("Julius", "Caesar"); + +// shows: 1, Ilya, undefined (no second argument) +showName2("Ilya"); + +// BEWARE: Arrow Functions dont have "arguments" + +function f() { + // If we access the arguments object from an arrow function, it takes them from the outer “normal” function. + + let showArg = () => console.log(arguments[0]); + showArg(); +} + +f(1); // 1 + +// Spread + +let arr1 = [1, -2, 3, 4]; +let arr2 = [8, 3, -8, 1]; + +console.log(Math.max(...arr1, ...arr2)); // 8 + +let str = "Hello"; + +// Array.from converts an iterable and array-like object into an array +console.log(Array.from(str)); // H,e,l,l,o + +// Array-like is an object that index and length property + +let arrLike = { 0: "foo", 5: "bar", length: 6 }; +console.log(Array.from(arrLike)); + +console.log(Array.from([1, 2, 3], (x) => x + x)); diff --git a/Snippets/Polyfill/bind-polyfill.js b/Snippets/Polyfill/bind-polyfill.js new file mode 100644 index 0000000..c3d6e7b --- /dev/null +++ b/Snippets/Polyfill/bind-polyfill.js @@ -0,0 +1,10 @@ +// bind polyfill + +Function.prototype.customBind = function (...args) { + let context = this; + let params = args.slice(1); + + return function (...args2) { + context.apply(args[0], [...params, ...args2]); + }; +}; diff --git a/Snippets/Polyfill/filter-polyfill.js b/Snippets/Polyfill/filter-polyfill.js new file mode 100644 index 0000000..80fd40c --- /dev/null +++ b/Snippets/Polyfill/filter-polyfill.js @@ -0,0 +1,12 @@ +// filter polyfill + +Array.prototype.customFilter = function (callbackFn) { + const result = []; + + for (let i = 0; i < this.length; i++) { + if (callbackFn.call(this, this[i], i)) { + result.push(this[i]); + } + } + return result; +}; diff --git a/Snippets/Polyfill/forEach-polyfill.js b/Snippets/Polyfill/forEach-polyfill.js new file mode 100644 index 0000000..faeaa84 --- /dev/null +++ b/Snippets/Polyfill/forEach-polyfill.js @@ -0,0 +1,7 @@ +// forEach polyfill + +Array.prototype.customForEach = function (callbackFn) { + for (let i = 0; i < this.length; i++) { + callbackFn.call(this, this[i], i); + } +}; diff --git a/Snippets/Polyfill/map-polyfill.js b/Snippets/Polyfill/map-polyfill.js new file mode 100644 index 0000000..27326cf --- /dev/null +++ b/Snippets/Polyfill/map-polyfill.js @@ -0,0 +1,10 @@ +// map polyfill + +Array.prototype.customMap = function (callbackFn) { + const result = []; + + for (let i = 0; i < this.length; i++) { + result.push(callbackFn.call(this, this[i], i)); + } + return result; +}; diff --git a/Snippets/Polyfill/promiseAll-polyfill.js b/Snippets/Polyfill/promiseAll-polyfill.js new file mode 100644 index 0000000..9a53798 --- /dev/null +++ b/Snippets/Polyfill/promiseAll-polyfill.js @@ -0,0 +1,21 @@ +// promise polyfill + +Promise.customAll = function (promiseArray) { + return new Promise((resolve, reject) => { + let completed = 0; + const promiseResults = []; + + promiseArray.forEach((value, index) => { + Promise.resolve(value) + .then((result) => { + completed++; + promiseResults[index] = result; + + if (completed === promiseArray.length) { + resolve(promiseResults); + } + }) + .catch((err) => reject(err)); + }); + }); +}; diff --git a/Snippets/Polyfill/reduce-polyfill.js b/Snippets/Polyfill/reduce-polyfill.js new file mode 100644 index 0000000..9a31b2e --- /dev/null +++ b/Snippets/Polyfill/reduce-polyfill.js @@ -0,0 +1,14 @@ +// reduce polyfill + +Array.prototype.customReduce = function (callbackFn, initialValue) { + let accumulator = initialValue; + + for (let i = 0; i < this.length; i++) { + if (accumulator !== undefined) { + accumulator = callbackFn.call(undefined, accumulator, this[i], i, this); + } else { + accumulator = this[i]; + } + } + return accumulator; +}; diff --git a/Snippets/Promises/asyncAwait.js b/Snippets/Promises/asyncAwait.js new file mode 100644 index 0000000..82a5a24 --- /dev/null +++ b/Snippets/Promises/asyncAwait.js @@ -0,0 +1,21 @@ +function loadJson(url) { + return fetch(url).then((response) => { + if (response.status == 200) { + return response.json(); + } else { + throw new Error(response.status); + } + }); +} + +loadJson("https://javascript.info/no-such-user.json").catch(alert); // Error: 404 + +async function loadJsonAsyncAwait(url) { + const response = await fetch(url); + if (response.status === 200) { + return response.json(); + } + throw new Error(response.status); +} + +loadJsonAsyncAwait("https://javascript.info/no-such-user.json").catch(alert); // Error: 404 diff --git a/Snippets/Promises/callAsyncFromNonAsync.js b/Snippets/Promises/callAsyncFromNonAsync.js new file mode 100644 index 0000000..d06f398 --- /dev/null +++ b/Snippets/Promises/callAsyncFromNonAsync.js @@ -0,0 +1,12 @@ +async function wait() { + await new Promise((resolve) => setTimeout(resolve, 3000)); + + return 10; +} + +function f() { + // get value from the promise by calling async code in sync func + wait().then((value) => console.log(value)); +} + +f(); diff --git a/Snippets/Promises/microtask.js b/Snippets/Promises/microtask.js new file mode 100644 index 0000000..e1b2397 --- /dev/null +++ b/Snippets/Promises/microtask.js @@ -0,0 +1,11 @@ +const a = "5"; +const promise = new Promise((resolve, reject) => { + setTimeout(() => resolve(1), 3000); + console.log("Inside promise"); // 1 +}); + +promise.then((val) => console.log("Promise", val)); // 3 + +setTimeout(() => console.log("Set Timeout ran"), 3000); // 4 + +console.log(a); // 2 diff --git a/Snippets/Promises/noNeedToReturnPromiseInThenChain.js b/Snippets/Promises/noNeedToReturnPromiseInThenChain.js new file mode 100644 index 0000000..99e2144 --- /dev/null +++ b/Snippets/Promises/noNeedToReturnPromiseInThenChain.js @@ -0,0 +1,11 @@ +function first(num) { + return Promise.resolve(num); // starting the then chain by returning a promise +} + +function second(num) { + return num + 1; // no need to return promise as this function is in 'then' chain +} + +first(4) + .then((valueOne) => second(valueOne)) + .then((valueTwo) => console.log(valueTwo)); diff --git a/Snippets/Promises/promiseAPI.js b/Snippets/Promises/promiseAPI.js new file mode 100644 index 0000000..c19513b --- /dev/null +++ b/Snippets/Promises/promiseAPI.js @@ -0,0 +1,20 @@ +/* +There are 6 static methods of Promise class: + +Promise.all(promises) – waits for all promises to resolve and returns an array of their results. If any of the given promises rejects, it becomes the error of Promise.all, and all other results are ignored. +Promise.allSettled(promises) (recently added method) – waits for all promises to settle and returns their results as an array of objects with: +status: "fulfilled" or "rejected" +value (if fulfilled) or reason (if rejected). +Promise.race(promises) – waits for the first promise to settle, and its result/error becomes the outcome. +Promise.any(promises) (recently added method) – waits for the first promise to fulfill, and its result becomes the outcome. If all of the given promises are rejected, AggregateError becomes the error of Promise.any. +Promise.resolve(value) – makes a resolved promise with the given value. +Promise.reject(error) – makes a rejected promise with the given error. + +*/ +Promise.race([ + new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)), + new Promise((resolve, reject) => + setTimeout(() => reject(new Error("Whoops!")), 2000) + ), + new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)), +]).then((value) => console.log(value)); diff --git a/Snippets/Promises/promiseAll.js b/Snippets/Promises/promiseAll.js new file mode 100644 index 0000000..651e672 --- /dev/null +++ b/Snippets/Promises/promiseAll.js @@ -0,0 +1,33 @@ +Promise.myAll = function (promises) { + return new Promise((resolve, reject) => { + let results = []; + let count = 0; + let toBeRejected = false; + + promises.forEach((promise, index) => { + if (toBeRejected) return; + Promise.resolve(promise) + .then((res) => { + count += 1; + results[index] = res; // maintaining the order + if (count === promises.length) { + resolve(results); + } + }) + .catch((err) => { + reject(err); + toBeRejected = true; + }); + }); + }); +}; + +const promise1 = Promise.resolve(1); +const promise2 = new Promise((resolve, reject) => + setTimeout(() => resolve(2), 3000) +); +const promise3 = 3; + +Promise.myAll([Promise.reject("bar"), promise1, promise2, promise3]) + .then((value) => console.log(value)) + .catch((err) => console.log(err)); diff --git a/Snippets/Promises/promiseAllSettled.js b/Snippets/Promises/promiseAllSettled.js new file mode 100644 index 0000000..bdbf1f8 --- /dev/null +++ b/Snippets/Promises/promiseAllSettled.js @@ -0,0 +1,33 @@ +Promise.myAllSettled = function (promises) { + let results = []; + let count = 0; + return new Promise((resolve, reject) => { + promises.forEach((promise, index) => { + Promise.resolve(promise) + .then((value) => { + count += 1; + results[index] = value; + if (count === promises.length) { + resolve(results); + } + }) + .catch((err) => { + count += 1; + results[index] = "Error: " + err; + if (count === promises.length) { + resolve(results); + } + }); + }); + }); +}; + +const promise1 = Promise.resolve(1); +const promise2 = new Promise((resolve, reject) => + setTimeout(() => resolve(2), 3000) +); +const promise3 = 3; + +Promise.myAllSettled([Promise.reject("bar"), promise1, promise2, promise3]) + .then((value) => console.log(value)) + .catch((err) => console.log(err)); diff --git a/Snippets/Promises/promiseAny.js b/Snippets/Promises/promiseAny.js new file mode 100644 index 0000000..b163a07 --- /dev/null +++ b/Snippets/Promises/promiseAny.js @@ -0,0 +1,46 @@ +Promise.myAny = function (promises) { + let errList = []; + let errCount = 0; + return new Promise((resolve, reject) => { + promises.forEach((promise, index) => { + Promise.resolve(promise).then( + (value) => { + return resolve(value); + }, + (reason) => { + errList[index] = reason; + errCount += 1; + if (errCount === promises.length) { + return reject([ + new AggregateError([errList], "All promises were rejected"), + ]); + } + } + ); + }); + }); +}; + +const promise1 = Promise.resolve(1); +const promise2 = new Promise((resolve, reject) => + setTimeout(() => resolve(2), 3000) +); +const promise3 = Promise.resolve(3); + +Promise.myAny([ + Promise.reject("bar"), + Promise.reject("foo"), + Promise.reject("nice"), + Promise.reject("wow"), +]) + .then((value) => console.log(value)) + .catch((err) => console.log(err)); + +Promise.any([ + Promise.reject("bar"), + Promise.reject("foo"), + Promise.reject("nice"), + Promise.reject("wow"), +]) + .then((value) => console.log(value)) + .catch((err) => console.log(err)); diff --git a/Snippets/Promises/promiseRace.js b/Snippets/Promises/promiseRace.js new file mode 100644 index 0000000..94fb770 --- /dev/null +++ b/Snippets/Promises/promiseRace.js @@ -0,0 +1,24 @@ +Promise.myRace = function (promises) { + return new Promise((resolve, reject) => { + promises.forEach((promise) => { + Promise.resolve(promise).then( + (value) => resolve(value), + (reason) => reject(reason) + ); + }); + }); +}; + +const promise1 = Promise.resolve(1); +const promise2 = new Promise((resolve, reject) => + setTimeout(() => resolve(2), 3000) +); +const promise3 = Promise.resolve(3); + +Promise.myRace([Promise.reject(new Error("bar")), promise1, promise2, promise3]) + .then((value) => console.log(value)) + .catch((err) => console.log("error")); + +Promise.race([Promise.reject("bar"), promise1, promise2, promise3]) + .then((value) => console.log(value)) + .catch((err) => console.log("error")); diff --git a/Snippets/Promises/resolveVsReturn.js b/Snippets/Promises/resolveVsReturn.js new file mode 100644 index 0000000..1cf7ee7 --- /dev/null +++ b/Snippets/Promises/resolveVsReturn.js @@ -0,0 +1,17 @@ +function second() { + return new Promise((resolve, reject) => { + // resolve/reject happens only one time + resolve(1); + reject("Failed"); + resolve(2); + console.log("After resolve/reject"); // this will be printed first because aLl lines will be executed + }); +} + +function first() { + second() + .then((value) => console.log(value)) + .catch((err) => console.log(err)); +} + +first(); diff --git a/Snippets/Promises/retryNTimes.js b/Snippets/Promises/retryNTimes.js new file mode 100644 index 0000000..d244d18 --- /dev/null +++ b/Snippets/Promises/retryNTimes.js @@ -0,0 +1,51 @@ +// wait function +const wait = (ms) => { + return new Promise((resolve) => { + setTimeout(() => resolve(), ms); + }); +}; + +const retryWithDelay = ( + operation, + retries = 3, + delay = 50, + finalErr = "Retry Failed" +) => { + return new Promise((resolve, reject) => + operation() + .then(resolve) + .catch((reason) => { + console.log(retries); + if (retries > 0) { + return wait(delay) + .then( + retryWithDelay.bind(null, operation, retries - 1, delay, finalErr) + ) + .then(resolve) + .catch(reject); + } + // throw final error + return reject(finalErr); + }) + ); +}; + +const getTestFunc = () => { + let callCounter = 0; + return async () => { + callCounter += 1; + + if (callCounter) { + throw new Error("Not yet"); + } + }; +}; + +// Test the code +const test = async () => { + await retryWithDelay(getTestFunc(), 10, 1000); + console.log("success"); +}; + +// Print the result +test().catch(console.error); diff --git a/Snippets/Promises/throwingErrorOutsidePromiseChain.js b/Snippets/Promises/throwingErrorOutsidePromiseChain.js new file mode 100644 index 0000000..53327b0 --- /dev/null +++ b/Snippets/Promises/throwingErrorOutsidePromiseChain.js @@ -0,0 +1,14 @@ +function first() { + throw new Error("lol"); // error wont be caught in .catch() + return Promise.resolve(1); +} + +function second(num) { + throw new Error("lol"); // error will be caught in .catch() + return num + 2; +} + +first() + .then((valueOne) => second(valueOne)) + .then((valueTwo) => console.log(valueTwo)) + .catch((err) => console.log(err)); diff --git a/Snippets/Promises/tryCatch.js b/Snippets/Promises/tryCatch.js new file mode 100644 index 0000000..21625d8 --- /dev/null +++ b/Snippets/Promises/tryCatch.js @@ -0,0 +1,20 @@ +async function first() { + try { + // return await second(); // remains synchronous for the function if used await + return second(); //asynchronous returning the function + } catch (err) { + console.log("Try/Catch", err); + } +} + +function second() { + return Promise.reject("Something went wrong!"); +} + +first() + .then(() => { + console.log("End"); + }) + .catch((err) => { + console.log("From .catch()", err); + }); diff --git a/Snippets/Prototype/basic.js b/Snippets/Prototype/basic.js new file mode 100644 index 0000000..24921f0 --- /dev/null +++ b/Snippets/Prototype/basic.js @@ -0,0 +1,29 @@ +let arr = ["Soham", "Dhruvi"]; + +let obj = { + name: "Soham", + city: "Ahmedabad", + getIntro: function () { + console.log(this.name + "from" + this.city); + }, +}; + +function fun() { + console.log("hello"); +} + +/* +Learnings +arr.__proto__ === Array.prototype +arr.__proto__.__proto__ === Object.prototype +arr.__proto__.__proto__ === null + +obj.__proto__ === Object.prototype +obj.__proto__proto__ === null + +fun.__proto__ === Function.prototype +fun.__proto__.__proto__ === Object.prototype +fun.__proto__.__proto__.__prototype === null + +Everything is object in JavaScript +*/ diff --git a/Snippets/Prototype/forInLoop.js b/Snippets/Prototype/forInLoop.js new file mode 100644 index 0000000..2c716a9 --- /dev/null +++ b/Snippets/Prototype/forInLoop.js @@ -0,0 +1,25 @@ +let animal = { + eats: true, +}; + +let rabbit = { + jumps: true, + __proto__: animal, +}; + +// Object.keys only returns own keys +console.log(Object.keys(rabbit)); // jumps + +// for..in loops over both own and inherited keys +for (let prop in rabbit) console.log(prop); // jumps, then eats + +// only loop in own keys and ignore inherited keys +for (let prop in rabbit) { + let isOwn = rabbit.hasOwnProperty(prop); + + if (isOwn) { + alert(`Our: ${prop}`); // Our: jumps + } else { + alert(`Inherited: ${prop}`); // Inherited: eats + } +} diff --git a/Snippets/Prototype/getAndset.js b/Snippets/Prototype/getAndset.js new file mode 100644 index 0000000..8b7964b --- /dev/null +++ b/Snippets/Prototype/getAndset.js @@ -0,0 +1,25 @@ +let user = { + name: "John", + surname: "Smith", + + set fullName(value) { + [this.name, this.surname] = value.split(" "); + }, + + get fullName() { + return `${this.name} ${this.surname}`; + }, +}; + +let admin = { + __proto__: user, + isAdmin: true, +}; + +console.log(admin.fullName); // John Smith + +// setter triggers! +admin.fullName = "Alice Cooper"; + +console.log(admin.fullName); // Alice Cooper, state of admin modified +console.log(user.fullName); // John Smith, state of user protected diff --git a/Snippets/Prototype/nativePrototypes.js b/Snippets/Prototype/nativePrototypes.js new file mode 100644 index 0000000..48db9fd --- /dev/null +++ b/Snippets/Prototype/nativePrototypes.js @@ -0,0 +1,23 @@ +Function.prototype.defer = function (ms) { + setTimeout(this, ms); +}; + +function f() { + console.log("Hello!"); +} + +f.defer(1000); // shows "Hello!" after 1 sec + +Function.prototype.defer2 = function (ms) { + let k = this; + return function (...args) { + setTimeout(() => k.apply(this, args), ms); + }; +}; + +// check it +function k(a, b) { + console.log(a + b); +} + +k.defer(1000)(1, 2); // shows 3 after 1 sec diff --git a/Snippets/Prototype/prototype.js b/Snippets/Prototype/prototype.js new file mode 100644 index 0000000..14e0e12 --- /dev/null +++ b/Snippets/Prototype/prototype.js @@ -0,0 +1,35 @@ +let animal = { + eats: true, +}; + +function Rabbit(name) { + this.name = name; +} + +Rabbit.prototype = animal; + +let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal + +let rabbit2 = new rabbit.constructor("Black Rabbit"); // another way to create a new object + +// re-assigning prototype of Rabbit +Rabbit.prototype = { + jumps: true, +}; + +let rabbit3 = new Rabbit(); +console.log(rabbit.constructor === Rabbit); // false + +// Not overwrite Rabbit.prototype totally +// just add to it +Rabbit.prototype.jumps = true; +// the default Rabbit.prototype.constructor is preserved + +let rabbit4 = new Rabbit(); +console.log(rabbit.constructor === Rabbit); // false + +// Other alternative way +Rabbit.prototype = { + jumps: true, + constructor: Rabbit, +}; diff --git a/Snippets/Prototype/searchingAlgorithm.js b/Snippets/Prototype/searchingAlgorithm.js new file mode 100644 index 0000000..04b89df --- /dev/null +++ b/Snippets/Prototype/searchingAlgorithm.js @@ -0,0 +1,34 @@ +/* +Problem statement +Use __proto__ to assign prototypes in a way that any property lookup will follow the path: pockets → bed → table → head. For instance, pockets.pen should be 3 (found in table), and bed.glasses should be 1 (found in head). +Answer the question: is it faster to get glasses as pockets.glasses or head.glasses? Benchmark if needed. +*/ + +let head = { + glasses: 1, +}; + +let table = { + pen: 3, + __proto__: head, +}; + +let bed = { + sheet: 1, + pillow: 2, + __proto__: table, +}; + +let pockets = { + money: 2000, + __proto__: bed, +}; + +console.log(pockets.pen); // 3 +console.log(bed.glasses); // 1 + +/* +In modern engines, performance-wise, there’s no difference whether we take a property from an object or its prototype. They remember where the property was found and reuse it in the next request. + +For instance, for pockets.glasses they remember where they found glasses (in head), and next time will search right there. They are also smart enough to update internal caches if something changes, so that optimization is safe. +*/ diff --git a/Snippets/Prototype/this.js b/Snippets/Prototype/this.js new file mode 100644 index 0000000..2f4508f --- /dev/null +++ b/Snippets/Prototype/this.js @@ -0,0 +1,22 @@ +// animal has methods +let animal = { + walk() { + if (!this.isSleeping) { + console.log(`I walk`); + } + }, + sleep() { + this.isSleeping = true; + }, +}; + +let rabbit = { + name: "White Rabbit", + __proto__: animal, +}; + +// modifies rabbit.isSleeping +rabbit.sleep(); + +console.log(rabbit.isSleeping); // true +console.log(animal.isSleeping); // undefined (no such property in the prototype) diff --git a/Snippets/Prototype/whyAreBothHamstersFull.js b/Snippets/Prototype/whyAreBothHamstersFull.js new file mode 100644 index 0000000..dc3059f --- /dev/null +++ b/Snippets/Prototype/whyAreBothHamstersFull.js @@ -0,0 +1,45 @@ +let hamster = { + stomach: [], + eat(food) { + this.stomach.push(food); + }, +}; + +let speedy = { + __proto__: hamster, +}; + +let lazy = { + __proto__: hamster, +}; + +// This one found the food +speedy.eat("apple"); +console.log(speedy.stomach); // apple + +// This one also has it, why? +console.log(lazy.stomach); // apple + +/* +Reason: + +speedy.eat() -> couldn't find in its obj -> finds it in hamster +Now, this = speedy +In this.stomach.push() -> it searches for stomach value -> couldn't find in speedy -> found in hamster +So, this.stomach.push actually pushes food to the hamster object's stomach; which is common for both of them +*/ + +/* +Solution: + +let speedy = { + __proto__: hamster, + stomach: [] +}; + +let lazy = { + __proto__: hamster, + stomach: [] +}; + +*/ diff --git a/Snippets/Recursion/factorial.js b/Snippets/Recursion/factorial.js new file mode 100644 index 0000000..2bca7aa --- /dev/null +++ b/Snippets/Recursion/factorial.js @@ -0,0 +1,8 @@ +const factorial = (num) => { + if (num === 1) { + return 1; + } + return num * factorial(num - 1); +}; + +console.log(factorial(5)); diff --git a/Snippets/Recursion/fibonacci.js b/Snippets/Recursion/fibonacci.js new file mode 100644 index 0000000..bc17d9f --- /dev/null +++ b/Snippets/Recursion/fibonacci.js @@ -0,0 +1,26 @@ +// fibonacci + +const fibonacci = (num) => { + if (num === 1) { + return 1; + } + if (num === 0) { + return 0; + } + return fibonacci(num - 2) + fibonacci(num - 1); +}; + +console.log(fibonacci(7)); + +// DP Approach + +function fib(n) { + let a = 1; + let b = 1; + for (let i = 3; i <= n; i++) { + let c = a + b; + a = b; + b = c; + } + return b; +} diff --git a/Snippets/Recursion/linkedList.js b/Snippets/Recursion/linkedList.js new file mode 100644 index 0000000..eb13922 --- /dev/null +++ b/Snippets/Recursion/linkedList.js @@ -0,0 +1,22 @@ +let list = { + value: 1, + next: { + value: 2, + next: { + value: 3, + next: { + value: 4, + next: null, + }, + }, + }, +}; + +const printList = (obj) => { + console.log(obj.value); + if (obj.next !== null) { + printList(obj.next); + } +}; + +console.log(printList(list)); diff --git a/Snippets/Recursion/power.js b/Snippets/Recursion/power.js new file mode 100644 index 0000000..967bacf --- /dev/null +++ b/Snippets/Recursion/power.js @@ -0,0 +1,9 @@ +const power = (x, n) => { + console.log(x, n); + if (n === 1) { + return x; + } + return x * power(x, n - 1); +}; + +console.log(power(2, 3)); diff --git a/Snippets/Recursion/recursiveTraversal.js b/Snippets/Recursion/recursiveTraversal.js new file mode 100644 index 0000000..f85bc4a --- /dev/null +++ b/Snippets/Recursion/recursiveTraversal.js @@ -0,0 +1,46 @@ +let company = { + sales: [ + { + name: "John", + salary: 1000, + }, + { + name: "Alice", + salary: 1600, + }, + ], + + development: { + sites: [ + { + name: "Peter", + salary: 2000, + }, + { + name: "Alex", + salary: 1800, + }, + ], + + internals: [ + { + name: "Jack", + salary: 1300, + }, + ], + }, +}; + +const computeSalary = (department, salary) => { + if (Array.isArray(department)) { + return department.reduce((prev, current) => prev + current.salary, 0); + } else { + let sum = 0; + for (let subDep of Object.values(department)) { + sum += computeSalary(subDep); + } + return sum; + } +}; + +console.log(computeSalary(company)); diff --git a/Snippets/Recursion/sumTo.js b/Snippets/Recursion/sumTo.js new file mode 100644 index 0000000..0ab4e8e --- /dev/null +++ b/Snippets/Recursion/sumTo.js @@ -0,0 +1,10 @@ +const sumTo = (n) => { + if (n === 1) { + return 1; + } else { + return n + sumTo(n - 1); + } +}; + +console.log(sumTo(5)); // 15 +console.log(sumTo(100000)); // error diff --git a/Snippets/Strings/lowerCaseConverter.js b/Snippets/Strings/lowerCaseConverter.js new file mode 100644 index 0000000..6c88127 --- /dev/null +++ b/Snippets/Strings/lowerCaseConverter.js @@ -0,0 +1,7 @@ +// toLowerCase() method changes the string to lowercase letters. +let favLanguage = "JavasCript"; +console.log(favLanguage.toLowerCase()); // javascript +let standard = "ECMA"; +console.log(standard.toLowerCase()); // ecma +let oneMoreLangugae = "PHP"; +console.log(oneMoreLangugae.toLowerCase()); // php diff --git a/Snippets/Strings/repeat.js b/Snippets/Strings/repeat.js new file mode 100644 index 0000000..ed5cd94 --- /dev/null +++ b/Snippets/Strings/repeat.js @@ -0,0 +1,5 @@ +/** + * repeat methods creates specified n copies and concatenates together and returns it. Original string will be unaltered + */ +let stringToBeRepeated = "love-Javascript"; +console.log(stringToBeRepeated.repeat(5)); //love-Javascriptlove-Javascriptlove-Javascriptlove-Javascriptlove-Javascript diff --git a/Snippets/Strings/search.js b/Snippets/Strings/search.js new file mode 100644 index 0000000..d06cdb3 --- /dev/null +++ b/Snippets/Strings/search.js @@ -0,0 +1,7 @@ +/** + * search a word in the sentence and find out at which position it is found for first time. + */ + +let jsLove = "Javascript is awesome. I am a Javascript lover. You have to experience this awesomeness"; +let searchWord = "awesome"; +console.log(searchWord + " found at " + (jsLove.search(searchWord) + 1) + " position"); // 14 diff --git a/Snippets/Strings/trim.js b/Snippets/Strings/trim.js new file mode 100644 index 0000000..46fb77d --- /dev/null +++ b/Snippets/Strings/trim.js @@ -0,0 +1,7 @@ +/** + * trim removes whitespaces in the beginning of the string and end of the string but not in the middle + */ + +let string = " Data Structures "; +console.log(string); // Data Structures +console.log(string.trim()); //Data Structures diff --git a/Snippets/Strings/upperCaseConverter.js b/Snippets/Strings/upperCaseConverter.js new file mode 100644 index 0000000..3a6acf9 --- /dev/null +++ b/Snippets/Strings/upperCaseConverter.js @@ -0,0 +1,7 @@ +// toUpperCase() method changes the string to upper letters. +let favLanguage = "JavasCript"; +console.log(favLanguage.toUpperCase()); // JAVASCRIPT +let firstName = "ECMA"; +console.log(firstName.toUpperCase()); // ECMA +let oneMoreLangugae = "Python"; +console.log(oneMoreLangugae.toUpperCase()); // PYTHON