├── .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));
--------------------------------------------------------------------------------