├── .idea ├── .gitignore ├── javascript-coding-challenge.iml ├── modules.xml └── vcs.xml ├── README.md ├── common-utilities ├── 1.deepClone │ ├── deepClone.js │ └── deepClone.md ├── 2.deepClone2 │ ├── deepClone2.js │ └── deepClone2.md ├── 3.eventEmitter │ ├── eventEmitter.js │ └── eventEmitter.md ├── promiseReject │ └── promiseReject.js ├── singleton │ ├── singleton1.mjs │ ├── singleton2.mjs │ ├── singleton3.mjs │ └── usage.mjs └── sleep │ ├── async-sleep.js │ └── sync-sleep.js ├── lodash ├── array │ └── square.js ├── chunk │ └── chunk.js ├── clamping │ └── clamping.js ├── compact │ └── compact.js ├── debounce │ ├── debounce.js │ └── debounce2.js ├── difference │ └── difference.js ├── dropRightWhile │ └── dropRightWhile.js ├── dropWhile │ └── dropWhile.js ├── fill │ └── fill.js ├── findIndex │ └── findIndex.js ├── findLastIndex │ └── findLastIndex.js ├── fromPairs │ └── fromPairs.js ├── get │ └── get.js ├── inRange │ └── inRange.js ├── intersection │ └── intersection.js ├── maxBy │ └── maxBy.js ├── minBy │ └── minBy.js ├── once │ └── once.js ├── range │ └── range.js ├── rangeRight │ └── rangeRight.js └── throttle │ └── throttle.js └── polyfill ├── array └── at │ └── at.js └── function ├── apply.js ├── bind.js └── call.js /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/javascript-coding-challenge.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # javascript-coding-challenge -------------------------------------------------------------------------------- /common-utilities/1.deepClone/deepClone.js: -------------------------------------------------------------------------------- 1 | //Approach1: Recursive 2 | function deepClone(value){ 3 | //Handle primitives 4 | if(typeof value !== 'object' || value === null) { 5 | return value; 6 | } 7 | 8 | //Handle array type 9 | if(Array.isArray(value)) { 10 | let arrCopy = []; 11 | for(let i=0; i < value.length; i++){ 12 | arrCopy[i] = deepClone(value[i]); 13 | } 14 | return arrCopy; 15 | } 16 | 17 | //Handle object types 18 | let objCopy = {}; 19 | for (const key in value) { 20 | if(value.hasOwnProperty(key)) { 21 | objCopy[key] = deepClone(value[key]); 22 | } 23 | } 24 | 25 | return objCopy; 26 | } 27 | 28 | //Approach2: JSON Stringify(Serialize and Deserialize) with downsides 29 | 30 | function deepClone1(value) { 31 | return JSON.parse(JSON.stringify(value)); 32 | } 33 | 34 | //Approach3: Native browser `structuredClone`API support 35 | 36 | function deepClone2(value) { 37 | return structuredClone(value); 38 | } 39 | 40 | const obj1 = { employee: { name: 'John', age: 30 } }; 41 | const clonedObj1 = deepClone(obj1); 42 | 43 | clonedObj1.employee.age = 35; 44 | console.log(clonedObj1.employee.age); // 35 45 | console.log(obj1.employee.age); // 30 46 | 47 | const obj2 = { friuts: [{ name: 'banana' }, { name: 'apple' }] }; 48 | const clonedObj2 = deepClone(obj2); 49 | 50 | obj2.friuts[1].name = 'orange'; 51 | console.log(obj2.friuts[1].name); // 'orange' 52 | console.log(clonedObj2.friuts[1].name); // 'apple' 53 | 54 | console.log(deepClone1(obj1)); 55 | console.log(deepClone2(obj1)); 56 | 57 | -------------------------------------------------------------------------------- /common-utilities/1.deepClone/deepClone.md: -------------------------------------------------------------------------------- 1 | **Problem:** Implement a `deepClone` function that performs a deep clone operation on JavaScript objects or primitives. 2 | 3 | **Note:** Consider that input contains JSON-serializable values only (null, boolean, number, string, Array, Object) but not for other objects like Date, Regex, Map, Set, Symbols etc. -------------------------------------------------------------------------------- /common-utilities/2.deepClone2/deepClone2.js: -------------------------------------------------------------------------------- 1 | function isPrimitiveOrFunctionType(inputVal){ 2 | return inputVal === null || typeof inputVal !== 'object' || typeof inputVal === 'function'; 3 | } 4 | 5 | function getType(inputVal){ 6 | return Object.prototype.toString.call(inputVal).slice(8,-1).toLowerCase(); 7 | } 8 | 9 | function deepClone2(inputVal){ 10 | return deepCloneWithCache(inputVal, new Map()); 11 | } 12 | 13 | function deepCloneWithCache(inputVal, cache) { 14 | if(isPrimitiveOrFunctionType(inputVal)) { 15 | return inputVal; 16 | } 17 | 18 | if(cache.has(inputVal)) { 19 | return cache.get(inputVal); 20 | } 21 | 22 | const type = getType(inputVal); 23 | let cloned; 24 | 25 | switch(type) { 26 | case 'set': 27 | cloned = new Set(); 28 | inputVal.forEach(item => cloned.add(deepCloneWithCache(item, cache))); 29 | break; 30 | case 'map': 31 | cloned = new Map(); 32 | inputVal.forEach((val, key) => cloned.set(deepCloneWithCache(key, cache), deepCloneWithCache(val, cache))); 33 | break; 34 | case 'array': 35 | cloned = inputVal.map(item => deepCloneWithCache(item, cache)); 36 | break; 37 | case 'date': 38 | cloned = new Date(inputVal.getTime()); 39 | break; 40 | case 'regex': 41 | cloned = new RegExp(inputVal.source, inputVal.flags); 42 | break; 43 | default: 44 | cloned = Object.create(Object.getPrototypeOf(inputVal)); 45 | cache.set(inputVal, cloned); 46 | for (const key of Reflect.ownKeys(inputVal)) { 47 | cloned[key] = deepCloneWithCache(inputVal[key], cache); 48 | } 49 | } 50 | return cloned; 51 | } 52 | 53 | //All possible types 54 | const obj1 = { 55 | str: '', 56 | num: 0, 57 | bool: true, 58 | undef: undefined, 59 | null: null, 60 | map: new Map(), 61 | set: new Set(), 62 | obj: { name: 'John', age: 37 }, 63 | arr: [1, 2, 3], 64 | date: new Date(), 65 | reg: new RegExp('/xyz/g'), 66 | [Symbol('x')]: 'abc', 67 | }; 68 | 69 | const clonedObj1 = deepClone2(obj1); 70 | clonedObj1.num = 10; 71 | console.log(clonedObj1); 72 | console.log(obj1.num); 73 | 74 | //Circular reference 75 | const obj2 = { x: {y : {}}}; 76 | obj2.x.y.z = obj2; 77 | const clonedObj2 = deepClone2(obj2); 78 | console.log(clonedObj2); 79 | -------------------------------------------------------------------------------- /common-utilities/2.deepClone2/deepClone2.md: -------------------------------------------------------------------------------- 1 | **Problem:** Implement a `deepClone2` function that performs a deep clone operation on JavaScript objects which contains any data type and clone circular references. 2 | 3 | **Note:** Consider that input contains JSON-serializable values only (null, boolean, number, string, Array, Object) but not for other objects like Date, Regex, Map, Set, Symbols etc. -------------------------------------------------------------------------------- /common-utilities/3.eventEmitter/eventEmitter.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/javascript-coding-challenge/18a92e6344d314b761a0b2d83b06c8ab99c2eeb2/common-utilities/3.eventEmitter/eventEmitter.js -------------------------------------------------------------------------------- /common-utilities/3.eventEmitter/eventEmitter.md: -------------------------------------------------------------------------------- 1 | **Problem:** Implement a `deepClone` function that performs a deep clone operation on JavaScript objects or primitives. 2 | 3 | **Note:** Consider that input contains JSON-serializable values only (null, boolean, number, string, Array, Object) but not for other objects like Date, Regex, Map, Set, Symbols etc. -------------------------------------------------------------------------------- /common-utilities/promiseReject/promiseReject.js: -------------------------------------------------------------------------------- 1 | function promiseReject(reason){ 2 | return new Promise((_, reject) => reject(reason)); 3 | } 4 | 5 | //---------------------------- 6 | 7 | try{ 8 | promiseReject('failed') 9 | } catch(err) { 10 | console.log(err); 11 | } -------------------------------------------------------------------------------- /common-utilities/singleton/singleton1.mjs: -------------------------------------------------------------------------------- 1 | const globalMap = new Map(); 2 | 3 | export default { 4 | getInstance: function() { 5 | return globalMap; 6 | } 7 | } -------------------------------------------------------------------------------- /common-utilities/singleton/singleton2.mjs: -------------------------------------------------------------------------------- 1 | let globalMap; 2 | 3 | export default { 4 | getInstance: function() { 5 | if(globalMap === undefined) { 6 | globalMap = new Map(); 7 | } 8 | 9 | return globalMap; 10 | } 11 | } -------------------------------------------------------------------------------- /common-utilities/singleton/singleton3.mjs: -------------------------------------------------------------------------------- 1 | const GlobalMap = (function(){ 2 | const _internalMap = new Map(); 3 | 4 | return { 5 | getInstance: function() { 6 | return _internalMap; 7 | } 8 | } 9 | })(); 10 | 11 | export default GlobalMap; -------------------------------------------------------------------------------- /common-utilities/singleton/usage.mjs: -------------------------------------------------------------------------------- 1 | 2 | import GlobalMap from "./singleton3.mjs"; 3 | import GlobalMap1 from "./singleton1.mjs"; 4 | import GlobalMap2 from "./singleton2.mjs"; 5 | 6 | const map1 = GlobalMap.getInstance(); 7 | map1.set('age', 28); 8 | const map2 = GlobalMap1.getInstance(); 9 | map2.set('age', 30); 10 | const map3 = GlobalMap2.getInstance(); 11 | map3.set('age', 40); 12 | console.log(map1); 13 | console.log(map2); 14 | console.log(map3); 15 | console.log(GlobalMap.getInstance().get('age')); 16 | 17 | -------------------------------------------------------------------------------- /common-utilities/sleep/async-sleep.js: -------------------------------------------------------------------------------- 1 | async function sleep(duration) { 2 | return new Promise((resolve) => setTimeout(resolve, duration)); 3 | } 4 | 5 | async function test() { 6 | console.log("start"); 7 | await sleep(3000); 8 | console.log("end") 9 | } 10 | 11 | setInterval(()=> { 12 | console.log("wait"); 13 | }, 500); 14 | 15 | test(); 16 | 17 | /** 18 | * start 19 | * wait 20 | * wait 21 | * wait 22 | * wait 23 | * wait 24 | * end 25 | * wait 26 | * ..... 27 | */ -------------------------------------------------------------------------------- /common-utilities/sleep/sync-sleep.js: -------------------------------------------------------------------------------- 1 | function sleep(duration){ 2 | const startTime = new Date().getTime(); 3 | 4 | while(new Date().getTime() - startTime < duration) { 5 | //pause 6 | } 7 | } 8 | 9 | function test() { 10 | console.log("start"); 11 | sleep(3000); 12 | console.log("end") 13 | } 14 | 15 | setInterval(()=> { 16 | console.log("wait"); 17 | }, 500); 18 | 19 | test(); 20 | 21 | /** 22 | * start 23 | * end 24 | * wait 25 | * wait 26 | * wait 27 | * wait 28 | * wait 29 | * wait 30 | * ..... 31 | */ 32 | -------------------------------------------------------------------------------- /lodash/array/square.js: -------------------------------------------------------------------------------- 1 | Array.prototype.square1 = function() { 2 | return this.map((el) => el * el); 3 | } 4 | 5 | Array.prototype.square2 = function() { 6 | const len = this.length; 7 | let newArr = new Array(len); 8 | 9 | for (let i = 0; i < len; i++) { 10 | newArr[i] = this[i] * this[i]; 11 | } 12 | 13 | return newArr; 14 | } 15 | 16 | const arr1 = [-2, 4, 6]; 17 | const arr2 = []; 18 | console.log(arr1.square1(arr1)); 19 | console.log(arr1.square1(arr2)); 20 | console.log(arr1.square2(arr1)); 21 | console.log(arr1.square2(arr2)); -------------------------------------------------------------------------------- /lodash/chunk/chunk.js: -------------------------------------------------------------------------------- 1 | function chunk1(arr, size) { 2 | let splitArr = []; 3 | 4 | if(!Array.isArray(arr) || size < 1) { 5 | return []; 6 | } 7 | 8 | for (let i = 0; i < arr.length; i += size) { 9 | let chunk = arr.slice(i, i+size); 10 | splitArr.push(chunk); 11 | } 12 | 13 | return splitArr; 14 | } 15 | 16 | function chunk2(arr, size) { 17 | let splitArr = []; 18 | let chunk = []; 19 | 20 | if(!Array.isArray(arr) || size < 1) { 21 | return []; 22 | } 23 | 24 | for (let i = 0; i < arr.length; i++) { 25 | chunk.push(arr[i]); 26 | if(chunk.length === size || i === arr.length-1){ 27 | splitArr.push(chunk); 28 | chunk = []; 29 | } 30 | } 31 | 32 | return splitArr; 33 | } 34 | 35 | const arr1 = [1, 2, 3, 4, 5, 6, 7]; 36 | const arr2 = ['a', 'b', 'c', 'd', 'e']; 37 | console.log(chunk1(arr1, 3)); 38 | console.log(chunk1(arr2, 2)); 39 | console.log(chunk2(arr1, 3)); 40 | console.log(chunk2(arr2, 2)); -------------------------------------------------------------------------------- /lodash/clamping/clamping.js: -------------------------------------------------------------------------------- 1 | function clamp1(num, min, max){ 2 | if(num < min) { 3 | return min; 4 | } else if(num > max) { 5 | return max; 6 | } 7 | return num; 8 | } 9 | 10 | function clamp2(num, min, max){ 11 | return Math.min(max, Math.max(num, min)); 12 | } 13 | 14 | 15 | console.log(clamp1(4, 0, 6)); // Within boundaries range 16 | console.log(clamp1(-5, -1, 8)); // Smaller than lower bound 17 | console.log(clamp1(15, -1, 10)); // Bigger than upper bound 18 | -------------------------------------------------------------------------------- /lodash/compact/compact.js: -------------------------------------------------------------------------------- 1 | function compact1(arr){ 2 | return arr.filter(Boolean) 3 | } 4 | 5 | function compact2(arr){ 6 | let compactArr = []; 7 | 8 | for (let i = 0; i < arr.length; i++) { 9 | if(arr[i]) { 10 | compactArr.push(arr[i]); 11 | } 12 | } 13 | 14 | return compactArr; 15 | } 16 | 17 | const arr1 =[1, 'hi', false, 0, 'a', null, undefined, NaN, {}, [], () => {}, '']; 18 | console.log(compact1(arr1)); 19 | console.log(compact2(arr1)); -------------------------------------------------------------------------------- /lodash/debounce/debounce.js: -------------------------------------------------------------------------------- 1 | function debounce(func, wait=0) { 2 | let timeoutId = null; 3 | return function(...args) { 4 | clearTimeout(timeoutId); 5 | timeoutId = setTimeout(() => { 6 | timeoutId = null; 7 | func.apply(this, args) 8 | }, wait); 9 | } 10 | } 11 | 12 | let counter = 0; 13 | function increment() { 14 | counter++; 15 | console.log(counter); 16 | } 17 | 18 | const debouncedCounter = debounce(increment, 100); 19 | setTimeout(() => debouncedCounter(), 50); 20 | setTimeout(() => debouncedCounter(), 100); 21 | setTimeout(() => debouncedCounter(), 200); 22 | setTimeout(() => debouncedCounter(), 300); 23 | 24 | -------------------------------------------------------------------------------- /lodash/debounce/debounce2.js: -------------------------------------------------------------------------------- 1 | // Debounce function: delays invoking 'func' until after 'wait' ms have elapsed since the last call. 2 | // Options: 3 | // immediate: if true, trigger on the leading edge instead of the trailing. 4 | function debounce(func, wait=0, immediate = false) { 5 | let timeoutId = null; 6 | let result; 7 | let lastArgs; 8 | let lastThis; 9 | 10 | const debounced = function (...args) { 11 | lastArgs = args; 12 | lastThis = this; 13 | const callNow = immediate && !timeoutId; 14 | clearTimeout(timeoutId); 15 | timeoutId = setTimeout(() => { 16 | timeoutId = null; 17 | if (!immediate) { 18 | result = func.apply(lastThis, lastArgs); 19 | } 20 | }, wait); 21 | if (callNow) { 22 | result = func.apply(lastThis, lastArgs); 23 | } 24 | return result; 25 | }; 26 | 27 | // Cancel any pending execution 28 | debounced.cancel = function () { 29 | clearTimeout(timeoutId); 30 | timeoutId = null; 31 | }; 32 | 33 | // Immediately invoke if pending, and clear timeout 34 | debounced.flush = function () { 35 | if (timeoutId) { 36 | clearTimeout(timeoutId); 37 | timeoutId = null; 38 | result = func.apply(lastThis, lastArgs); 39 | } 40 | return result; 41 | }; 42 | 43 | return debounced; 44 | } 45 | 46 | let counter = 0; 47 | function increment() { 48 | counter++; 49 | console.log(counter); 50 | } 51 | 52 | const debouncedCounter = debounce(increment, 100); 53 | setTimeout(() => debouncedCounter(), 50); 54 | setTimeout(() => debouncedCounter(), 100); 55 | setTimeout(() => debouncedCounter(), 200); 56 | setTimeout(() => debouncedCounter(), 300); 57 | 58 | 59 | /** 60 | Timeline of Events 61 | At 50ms: debouncedCounter() is called. 62 | Schedules increment to run at 150ms (50ms + 100ms). 63 | At 100ms: debouncedCounter() is called again. 64 | Cancels previous timeout, schedules increment to run at 200ms (100ms + 100ms). 65 | At 200ms: debouncedCounter() is called again. 66 | Cancels previous timeout, schedules increment to run at 300ms (200ms + 100ms). 67 | At 300ms: debouncedCounter() is called again. 68 | Cancels previous timeout, schedules increment to run at 400ms (300ms + 100ms). 69 | */ 70 | -------------------------------------------------------------------------------- /lodash/difference/difference.js: -------------------------------------------------------------------------------- 1 | function difference1(arr1, arr2){ 2 | if(arr1.length === 0) return []; 3 | if(arr2.length === 0) return arr1; 4 | 5 | return arr1.filter(val => !arr2.includes(val)); 6 | } 7 | 8 | function difference2(arr1, arr2){ 9 | if(arr1.length === 0) return []; 10 | if(arr2.length === 0) return arr1; 11 | 12 | const excludeSet = new Set(arr2); 13 | 14 | return arr1.filter(val => !excludeSet.has(val)); 15 | } 16 | 17 | function difference3(arr1, arr2){ 18 | if(arr1.length === 0) return []; 19 | if(arr2.length === 0) return arr1; 20 | 21 | const excludeSet = new Set(arr2); 22 | let result = []; 23 | 24 | for (let i = 0; i < arr1.length; i++) { 25 | const value = arr1[i]; 26 | if(!excludeSet.has(value) && ! (value === undefined && !(i in arr1))) { 27 | result.push(arr1[i]); 28 | } 29 | } 30 | 31 | return result; 32 | } 33 | 34 | const arr1 = [1, 2, 2, 3, 2], arr2 = [2]; 35 | const arr3 = [1, 2, 3], arr4 = []; 36 | const arr5 = [], arr6 = []; 37 | console.log(difference1(arr1, arr2)); 38 | console.log(difference2(arr1, arr2)); 39 | console.log(difference3(arr1, arr2)); 40 | console.log(difference1(arr3, arr4)); 41 | console.log(difference1(arr5, arr6)); -------------------------------------------------------------------------------- /lodash/dropRightWhile/dropRightWhile.js: -------------------------------------------------------------------------------- 1 | function dropRightWhile(arr, predicate){ 2 | let endIndex = arr.length-1; 3 | 4 | while(endIndex >=0 && predicate(arr[endIndex], endIndex, arr)){ 5 | endIndex--; 6 | } 7 | 8 | return arr.slice(0, endIndex+1); 9 | } 10 | 11 | 12 | const arr1 = [{ 13 | name: 'banana', type: 'fruit'}, 14 | {name: 'tomato', type: 'veggie'}, 15 | {name: 'apple', type: 'fruit'} 16 | ]; 17 | console.log(dropRightWhile(arr1, (el) => el.name === 'apple')); -------------------------------------------------------------------------------- /lodash/dropWhile/dropWhile.js: -------------------------------------------------------------------------------- 1 | function dropWhile(arr, predicate){ 2 | let index = 0; 3 | 4 | while(index < arr.length && predicate(arr[index], index, arr)) { 5 | index++; 6 | } 7 | 8 | return arr.slice(index); 9 | } 10 | 11 | const arr1 = [{ 12 | name: 'banana', type: 'fruit'}, 13 | {name: 'tomato', type: 'veggie'}, 14 | {name: 'apple', type: 'fruit'} 15 | ]; 16 | console.log(dropWhile(arr1, (el) => el.name === 'banana')); -------------------------------------------------------------------------------- /lodash/fill/fill.js: -------------------------------------------------------------------------------- 1 | function fill(arr, value, start=0, end= arr.length) { 2 | let length = arr.length; 3 | 4 | if(end <= start) { 5 | return arr; 6 | } 7 | 8 | if(start < 0) { 9 | start = -start < length ? start+length : 0; 10 | } 11 | 12 | if(end < 0) { 13 | end = end+length; 14 | } 15 | 16 | for (let i = start; i < Math.min(end, length); i++) { 17 | arr[i] = value; 18 | } 19 | 20 | return arr; 21 | } 22 | 23 | const arr1 = [1, 2, 3, 4, 5]; 24 | const arr2 = [1, 2, 3, 4, 5]; 25 | const arr3 = [1, 2, 3, 4, 5]; 26 | console.log(fill(arr1, '*', 2, 4)); 27 | console.log(fill(arr2, '*', -4, -1)); 28 | console.log(fill(arr3, '*', -1, -4)); -------------------------------------------------------------------------------- /lodash/findIndex/findIndex.js: -------------------------------------------------------------------------------- 1 | function findIndex(arr, predicate, fromIndex=0){ 2 | const length = arr.length; 3 | 4 | if(fromIndex <0) { 5 | fromIndex = -fromIndex > length ? 0 : fromIndex+length; 6 | } 7 | 8 | for (let index = fromIndex; index < length; index++) { 9 | if(predicate(arr[index], index, arr)) { 10 | return index; 11 | } 12 | } 13 | 14 | return -1; 15 | } 16 | 17 | const arr1 = [1,2,3,4,5], predicate1 = (value) => value > 2; 18 | const arr2 = [1,2,3,4,5], predicate2 = (value) => value > 8; 19 | const arr3 = [1,2,3,4,5], predicate3 = (value) => value > 3; 20 | console.log(findIndex(arr1, predicate1, 3)); //3 21 | console.log(findIndex(arr2, predicate2, 4)); //-1 22 | console.log(findIndex(arr3, predicate3, -2)); //3 -------------------------------------------------------------------------------- /lodash/findLastIndex/findLastIndex.js: -------------------------------------------------------------------------------- 1 | function findLastIndex(arr, predicate, fromIndex=arr.length-1){ 2 | const length = arr.length; 3 | fromIndex = fromIndex < 0 ? Math.max(fromIndex+length, 0) : Math.min(fromIndex, length-1); 4 | 5 | let index = fromIndex; 6 | 7 | while(index >= 0) { 8 | if(predicate(arr[index], index, arr)) { 9 | return index; 10 | } 11 | index--; 12 | } 13 | 14 | return -1; 15 | } 16 | 17 | const arr1 = [6, 5, 4, 3, 2, 1], predicate1 = (value) => value > 2; 18 | const arr2 = [6, 5, 4, 3, 2, 1], predicate2 = (value) => value > 3; 19 | const arr3 = [6, 5, 4, 3, 2, 1], predicate3 = (value) => value > 3; 20 | console.log(findLastIndex(arr1, predicate1, 3)); //3 21 | console.log(findLastIndex(arr2, predicate2, -9)); //0 22 | console.log(findLastIndex(arr3, predicate3, 7)); //3 -------------------------------------------------------------------------------- /lodash/fromPairs/fromPairs.js: -------------------------------------------------------------------------------- 1 | function fromPairs1(pairs){ 2 | let pairsObj = {}; 3 | for (const [key, value] of pairs) { 4 | pairsObj[key] = value; 5 | } 6 | 7 | return pairsObj; 8 | } 9 | 10 | function fromPairs2(pairs){ 11 | return Object.fromEntries(pairs); 12 | } 13 | 14 | const arr1 = [['x', 1], ['y', 2], ['z', 3]]; 15 | console.log(fromPairs1(arr1)); 16 | console.log(fromPairs2(arr1)); -------------------------------------------------------------------------------- /lodash/get/get.js: -------------------------------------------------------------------------------- 1 | function get(obj, path, defaultVal) { 2 | let index = 0; 3 | let pathArr = Array.isArray(path) ? path : path.split('.'); 4 | const length = pathArr.length; 5 | 6 | while(obj != null && index < length) { 7 | obj = obj[String(pathArr[index])]; 8 | index++; 9 | } 10 | 11 | return index === length ? obj : defaultVal; 12 | } 13 | 14 | let object1 = { 'a': { 'b': { 'c': 3 } } }; 15 | let object2 = { 'a': [{ 'b': { 'c': 3 } }] }; 16 | console.log(get(object1, 'a.b.c')); 17 | console.log(get(object2, ['a', '0', 'b', 'c'])); 18 | console.log(get(object2, 'a.b.c', 'default')); -------------------------------------------------------------------------------- /lodash/inRange/inRange.js: -------------------------------------------------------------------------------- 1 | function inRange1(value, start, end=0){ 2 | return Math.min(start, end) <= value && value < Math.max(start, end); 3 | } 4 | 5 | function inRange2(value, startParam=0, endParam){ 6 | const [start, end] = endParam !== undefined ? [startParam, endParam] : [0, startParam] 7 | return Math.min(start, end) <= value && value < Math.max(start, end); 8 | } 9 | 10 | function inRange3(value, start=0, end){ 11 | if(end === undefined){ 12 | end = start; 13 | start = 0; 14 | } 15 | 16 | if(start < end) { 17 | return (start <= value && value < end); 18 | } 19 | return end <= value && value < start; 20 | } 21 | 22 | console.log(inRange1(3, 2, 4)); 23 | console.log(inRange1(4, 8)); 24 | console.log(inRange1(4, 2)); 25 | console.log(inRange1(2, 2)); 26 | console.log(inRange1(1.2, 2)); 27 | console.log(inRange1(5.2, 4)); 28 | console.log(inRange1(-3, -2, -6)); 29 | 30 | console.log(inRange2(3, 2, 4)); 31 | console.log(inRange2(4, 8)); 32 | console.log(inRange2(4, 2)); 33 | console.log(inRange2(2, 2)); 34 | console.log(inRange2(1.2, 2)); 35 | console.log(inRange2(5.2, 4)); 36 | console.log(inRange2(-3, -2, -6)); 37 | 38 | console.log(inRange3(3, 2, 4)); 39 | console.log(inRange3(4, 8)); 40 | console.log(inRange3(4, 2)); 41 | console.log(inRange3(2, 2)); 42 | console.log(inRange3(1.2, 2)); 43 | console.log(inRange3(5.2, 4)); 44 | console.log(inRange3(-3, -2, -6)); -------------------------------------------------------------------------------- /lodash/intersection/intersection.js: -------------------------------------------------------------------------------- 1 | function intersection(...arrays){ 2 | let firstArrSet = new Set(arrays[0]); 3 | 4 | for (let i = 1; i < arrays.length; i++) { 5 | firstArrSet.forEach((element) => { 6 | if(!arrays[i].includes(element)){ 7 | firstArrSet.delete(element); 8 | } 9 | } 10 | ) 11 | } 12 | 13 | return Array.from(firstArrSet); 14 | } 15 | 16 | console.log(intersection([1,2,3,4],[2,3,4,5],[3,4,5,6])); 17 | console.log(intersection([1,2],[3,4,5])); -------------------------------------------------------------------------------- /lodash/maxBy/maxBy.js: -------------------------------------------------------------------------------- 1 | function minBy(array, iteratee) { 2 | let maxElement, maxValue; 3 | 4 | array.forEach((value) => { 5 | let current = iteratee(value); 6 | 7 | if (current != null && (maxValue === undefined || current > maxValue)) { 8 | maxValue = current; 9 | maxElement = value; 10 | } 11 | }); 12 | 13 | return maxElement; 14 | } 15 | 16 | console.log(minBy([{ n: 1 }, { n: 2 }], (o) => o.n)); 17 | console.log(minBy([1, 2, 3], (n) => -n)); 18 | -------------------------------------------------------------------------------- /lodash/minBy/minBy.js: -------------------------------------------------------------------------------- 1 | function minBy(array, iteratee) { 2 | let maxElement, maxValue; 3 | 4 | array.forEach((value) => { 5 | let current = iteratee(value); 6 | 7 | if (current != null && (maxValue === undefined || current < maxValue)) { 8 | maxValue = current; 9 | maxElement = value; 10 | } 11 | }); 12 | 13 | return maxElement; 14 | } 15 | 16 | console.log(minBy([{ n: 1 }, { n: 2 }], (o) => o.n)); 17 | console.log(minBy([1, 2, 3], (n) => -n)); 18 | -------------------------------------------------------------------------------- /lodash/once/once.js: -------------------------------------------------------------------------------- 1 | function once(func){ 2 | let ranOnce = false; 3 | let value; 4 | 5 | return function(...args) { 6 | if(!ranOnce) { 7 | value = func.apply(this, args); 8 | ranOnce = true; 9 | } 10 | 11 | return value; 12 | } 13 | } 14 | // -------------------------------------------------------------- 15 | let index = 1; 16 | 17 | function incrementBy(value) { 18 | index += value; 19 | return index; 20 | } 21 | 22 | const incrementByOnce = once(incrementBy); 23 | console.log(incrementByOnce(2)); 24 | console.log(incrementByOnce(3)); 25 | index = 4; 26 | console.log(incrementByOnce(5)); -------------------------------------------------------------------------------- /lodash/range/range.js: -------------------------------------------------------------------------------- 1 | function rangeRight(start = 0, end, step = 1) { 2 | let resultArr = []; 3 | 4 | if (!end) { 5 | end = start; 6 | start = 0; 7 | } 8 | 9 | if (end < start && step === 1) { 10 | step = -1; 11 | } 12 | 13 | let length = (end - start) / (step || 1); 14 | 15 | for (let i = 0; i < length; i++) { 16 | resultArr.push(start + step * i); 17 | } 18 | 19 | return resultArr; 20 | } 21 | 22 | //------------------------------------------------------- 23 | console.log(rangeRight(4)); 24 | console.log(rangeRight(-4)); 25 | console.log(rangeRight(1, 5)); 26 | console.log(rangeRight(0, 20, 5)); 27 | console.log(rangeRight(0, -4, -1)); 28 | console.log(rangeRight(1, 4, 0)); 29 | console.log(rangeRight(0)); 30 | console.log(rangeRight(-1, -4, 2)); 31 | -------------------------------------------------------------------------------- /lodash/rangeRight/rangeRight.js: -------------------------------------------------------------------------------- 1 | function rangeRight(start = 0, end, step = 1) { 2 | let resultArr = []; 3 | 4 | if (!end) { 5 | end = start; 6 | start = 0; 7 | } 8 | 9 | if (end < start && step === 1) { 10 | step = -1; 11 | } 12 | 13 | let length = (end - start) / (step || 1); 14 | 15 | for (let i = length - 1; i >= 0; i--) { 16 | resultArr.push(start + step * i); 17 | } 18 | 19 | return resultArr; 20 | } 21 | 22 | //------------------------------------------------------- 23 | console.log(rangeRight(4)); 24 | console.log(rangeRight(-4)); 25 | console.log(rangeRight(1, 5)); 26 | console.log(rangeRight(0, 20, 5)); 27 | console.log(rangeRight(0, -4, -1)); 28 | console.log(rangeRight(1, 4, 0)); 29 | console.log(rangeRight(0)); 30 | console.log(rangeRight(-1, -4, 2)); 31 | -------------------------------------------------------------------------------- /lodash/throttle/throttle.js: -------------------------------------------------------------------------------- 1 | function throttle(func, wait) { 2 | let isThrottle = false; 3 | 4 | return function (...args) { 5 | if (isThrottle) { 6 | return; 7 | } 8 | 9 | isThrottle = true; 10 | setTimeout(() => (isThrottle = false), wait); 11 | func.apply(this, args); 12 | }; 13 | } 14 | 15 | let counter = 0; 16 | function increment() { 17 | counter++; 18 | console.log(counter); 19 | } 20 | 21 | const throttledCounter = throttle(increment, 100); 22 | setTimeout(() => throttledCounter(), 50); 23 | setTimeout(() => throttledCounter(), 160); 24 | setTimeout(() => throttledCounter(), 200); 25 | 26 | /** 27 | Timeline of Events 28 | At 50ms: 29 | throttledCounter() is called. 30 | → increment() runs (counter becomes 1). 31 | → Throttle period starts (next allowed call after 150ms). 32 | At 160ms: 33 | throttledCounter() is called (throttle period has ended). 34 | → increment() runs (counter becomes 2). 35 | → Throttle period starts (next allowed call after 260ms). 36 | At 200ms: 37 | throttledCounter() is called (still in throttle period). 38 | → Call is ignored, counter remains 2. 39 | */ 40 | -------------------------------------------------------------------------------- /polyfill/array/at/at.js: -------------------------------------------------------------------------------- 1 | Array.prototype.myAt = function(index) { 2 | let len = this.length; 3 | let relativeIndex = Number(index); 4 | let boundedIndex = relativeIndex >=0 ? relativeIndex : len+relativeIndex; 5 | 6 | if(boundedIndex < 0 && boundedIndex >= len) { 7 | return; 8 | } 9 | 10 | return this[boundedIndex]; 11 | } 12 | 13 | 14 | //---------------------------------------- 15 | const arr = [1, 2, 3]; 16 | console.log(arr.myAt(0)); 17 | console.log(arr.myAt(1)); 18 | console.log(arr.myAt(2)); 19 | console.log(arr.myAt(3)); 20 | console.log(arr.myAt(-1)); 21 | console.log(arr.myAt(-2)); 22 | console.log(arr.myAt(-3)); 23 | console.log(arr.myAt(-4)); -------------------------------------------------------------------------------- /polyfill/function/apply.js: -------------------------------------------------------------------------------- 1 | Function.prototype.myApply = function(thisArg, args=[]) { 2 | thisArg = (thisArg === null || thisArg === undefined) ? globalThis : Object(thisArg); 3 | 4 | const uniqueId = Symbol('fn'); 5 | thisArg[uniqueId] = this; 6 | 7 | const result = thisArg[uniqueId](...args); 8 | delete thisArg[uniqueId]; 9 | 10 | return result; 11 | } 12 | 13 | Function.prototype.myApply1 = function(thisArg, args=[]) { 14 | thisArg = (thisArg === null || thisArg === undefined) ? globalThis : Object(thisArg); 15 | 16 | const uniqueId = Symbol('fn'); 17 | Object.defineProperty(thisArg, uniqueId, { 18 | enumerable: false, 19 | value: this 20 | }); 21 | const result = thisArg[uniqueId](...args); 22 | delete thisArg[uniqueId]; 23 | 24 | return result; 25 | } 26 | 27 | Function.prototype.myApply2 = function(thisArg, args=[]) { 28 | return this.call(thisArg, ...args) 29 | } 30 | 31 | Function.prototype.myApply3 = function(thisArg, args=[]) { 32 | return this.bind(thisArg, ...args)(); 33 | } 34 | 35 | const person1 = { 36 | firstName: 'Sudheer', 37 | lastName: 'Jonna' 38 | } 39 | 40 | function details(age = 30) { 41 | return this.firstName + ' '+this.lastName + ' is ' + age + ' years old'; 42 | } 43 | 44 | console.log(details.myApply(person1, [35])); 45 | console.log(details.myApply1(person1, [35])); 46 | console.log(details.myApply2(person1, [35])); 47 | console.log(details.myApply3(person1, [35])); -------------------------------------------------------------------------------- /polyfill/function/bind.js: -------------------------------------------------------------------------------- 1 | Function.prototype.myBind = function(thisArg, ...args){ 2 | 3 | if(typeof this !== 'function') { 4 | return new Error('Bind must be called on a function'); 5 | } 6 | 7 | const originalFunc = this; 8 | 9 | function boundFunction(...innerArgs) { 10 | 11 | if(this instanceof boundFunction) { 12 | return new originalFunc(args.concat(innerArgs)); 13 | } 14 | 15 | return originalFunc.apply(thisArg, args.concat(innerArgs)); 16 | } 17 | 18 | if(originalFunc.prototype) { 19 | boundFunction.prototype = originalFunc.prototype; 20 | } 21 | 22 | return boundFunction; 23 | } 24 | 25 | Function.prototype.myBind1 = function(thisArg, ...args){ 26 | if(typeof this !== 'function') { 27 | return new Error("Bind must be called on a function"); 28 | } 29 | 30 | const uniqueId = Symbol('fn'); 31 | const wrappedObj = Object(thisArg); 32 | 33 | Object.defineProperty(wrappedObj, uniqueId, { 34 | enumerable: false, 35 | value: this 36 | }); 37 | 38 | return function(...innerArgs){ 39 | return wrappedObj[uniqueId](...args, ...innerArgs) 40 | } 41 | } 42 | 43 | Function.prototype.myBind2 = function(thisArg, ...args){ 44 | const originalFunc = this; 45 | 46 | if(typeof originalFunc !== 'function') { 47 | return new Error("Bind must be called on a function"); 48 | } 49 | 50 | return function(...innerArgs){ 51 | return Reflect.apply(originalFunc, thisArg, [...args, ...innerArgs]) 52 | } 53 | } 54 | 55 | // ------------------------------------------------------ 56 | 57 | function fullName(greeting) { 58 | console.log(`${greeting}, I'm ${this.firstName} ${this.lastName}`); 59 | } 60 | 61 | const person1 = { 62 | firstName: 'Sudheer', 63 | lastName: 'Jonna' 64 | }; 65 | 66 | const person2 = { 67 | firstName: "John", 68 | lastName: "Smith" 69 | }; 70 | 71 | const boundFullName1 = fullName.myBind(person1, "Hello"); 72 | const boundFullName2 = fullName.myBind1(person2, "Hey"); 73 | const boundFullName3 = fullName.myBind2(person2, "Hi"); 74 | 75 | boundFullName1(); 76 | boundFullName2(); 77 | boundFullName3(); -------------------------------------------------------------------------------- /polyfill/function/call.js: -------------------------------------------------------------------------------- 1 | Function.prototype.myCall = function(thisArg, ...args) { 2 | // Properly handle null/undefined as globalThis, and convert primitives to objects 3 | thisArg = (thisArg === null || thisArg === undefined) ? globalThis : Object(thisArg); 4 | 5 | const uniqueId = Symbol('fn'); 6 | thisArg[uniqueId] = this; 7 | 8 | const result = thisArg[uniqueId](...args); 9 | delete thisArg[uniqueId]; 10 | 11 | return result; 12 | } 13 | 14 | Function.prototype.myCall1 = function(thisArg, ...args) { 15 | // Properly handle null/undefined as globalThis, and convert primitives to objects 16 | thisArg = (thisArg === null || thisArg === undefined) ? globalThis : Object(thisArg); 17 | 18 | const uniqueId = Symbol('fn'); 19 | Object.defineProperty(thisArg, uniqueId, { 20 | enumerable: false, 21 | value: this 22 | }); 23 | 24 | const result = thisArg[uniqueId](...args); 25 | delete thisArg[uniqueId]; 26 | 27 | return result; 28 | } 29 | 30 | Function.prototype.myCall2 = function(thisArg, ...args) { 31 | return this.apply(thisArg, [...args]); 32 | } 33 | 34 | Function.prototype.myCall3 = function(thisArg, ...args) { 35 | return this.bind(thisArg, ...args)(); 36 | } 37 | 38 | const person1 = { 39 | firstName: 'Sudheer', 40 | lastName: 'Jonna' 41 | } 42 | 43 | function details(age = 30) { 44 | return this.firstName + ' ' + this.lastName + ' is ' + age + ' years old'; 45 | } 46 | 47 | console.log(details.myCall(person1, 35)); 48 | console.log(details.myCall1(person1, 35)); 49 | console.log(details.myCall2(person1, 35)); 50 | console.log(details.myCall3(person1, 35)); --------------------------------------------------------------------------------