From 31413d575958f45e309f37d4e4d5029d7c4de513 Mon Sep 17 00:00:00 2001 From: Max Zaitsev Date: Mon, 17 Feb 2025 15:50:19 +0300 Subject: [PATCH] leetcode-2025-02-17 --- README.md | 2 +- {5-kyu => codewars/5-kyu}/findUniqString.js | 0 {5-kyu => codewars/5-kyu}/findUniqString.ts | 0 {5-kyu => codewars/5-kyu}/sloganMaker.js | 0 {5-kyu => codewars/5-kyu}/sloganMaker.ts | 0 {6-kyu => codewars/6-kyu}/longestWord.js | 0 {6-kyu => codewars/6-kyu}/longestWord.ts | 0 {7-kyu => codewars/7-kyu}/commonGround.js | 0 {7-kyu => codewars/7-kyu}/commonGround.ts | 0 {7-kyu => codewars/7-kyu}/getFirstPython.js | 0 {7-kyu => codewars/7-kyu}/getFirstPython.ts | 0 leetcode/easy/arrayPrototypeLast.js | 13 +++++ leetcode/easy/arrayPrototypeLast.ts | 38 +++++++++++++ leetcode/medium/cacheWithTimeLimit.js | 49 ++++++++++++++++ leetcode/medium/cacheWithTimeLimit.ts | 53 +++++++++++++++++ .../medium/callFunctionWithCustomContext.js | 26 +++++++++ .../medium/callFunctionWithCustomContext.ts | 34 +++++++++++ leetcode/medium/debounce.js | 30 ++++++++++ leetcode/medium/debounce.ts | 36 ++++++++++++ leetcode/medium/eventEmitter.js | 55 ++++++++++++++++++ leetcode/medium/eventEmitter.ts | 57 +++++++++++++++++++ .../executeAsynchronousFunctionsInParallel.js | 33 +++++++++++ .../executeAsynchronousFunctionsInParallel.ts | 37 ++++++++++++ leetcode/medium/groupBy.js | 26 +++++++++ leetcode/medium/groupBy.ts | 29 ++++++++++ leetcode/medium/promiseTimeLimit.js | 26 +++++++++ leetcode/medium/promiseTimeLimit.ts | 30 ++++++++++ 27 files changed, 573 insertions(+), 1 deletion(-) rename {5-kyu => codewars/5-kyu}/findUniqString.js (100%) rename {5-kyu => codewars/5-kyu}/findUniqString.ts (100%) rename {5-kyu => codewars/5-kyu}/sloganMaker.js (100%) rename {5-kyu => codewars/5-kyu}/sloganMaker.ts (100%) rename {6-kyu => codewars/6-kyu}/longestWord.js (100%) rename {6-kyu => codewars/6-kyu}/longestWord.ts (100%) rename {7-kyu => codewars/7-kyu}/commonGround.js (100%) rename {7-kyu => codewars/7-kyu}/commonGround.ts (100%) rename {7-kyu => codewars/7-kyu}/getFirstPython.js (100%) rename {7-kyu => codewars/7-kyu}/getFirstPython.ts (100%) create mode 100644 leetcode/easy/arrayPrototypeLast.js create mode 100644 leetcode/easy/arrayPrototypeLast.ts create mode 100644 leetcode/medium/cacheWithTimeLimit.js create mode 100644 leetcode/medium/cacheWithTimeLimit.ts create mode 100644 leetcode/medium/callFunctionWithCustomContext.js create mode 100644 leetcode/medium/callFunctionWithCustomContext.ts create mode 100644 leetcode/medium/debounce.js create mode 100644 leetcode/medium/debounce.ts create mode 100644 leetcode/medium/eventEmitter.js create mode 100644 leetcode/medium/eventEmitter.ts create mode 100644 leetcode/medium/executeAsynchronousFunctionsInParallel.js create mode 100644 leetcode/medium/executeAsynchronousFunctionsInParallel.ts create mode 100644 leetcode/medium/groupBy.js create mode 100644 leetcode/medium/groupBy.ts create mode 100644 leetcode/medium/promiseTimeLimit.js create mode 100644 leetcode/medium/promiseTimeLimit.ts diff --git a/README.md b/README.md index ddb58d3..393ca41 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# codewars-solutions +# Code problems solutions diff --git a/5-kyu/findUniqString.js b/codewars/5-kyu/findUniqString.js similarity index 100% rename from 5-kyu/findUniqString.js rename to codewars/5-kyu/findUniqString.js diff --git a/5-kyu/findUniqString.ts b/codewars/5-kyu/findUniqString.ts similarity index 100% rename from 5-kyu/findUniqString.ts rename to codewars/5-kyu/findUniqString.ts diff --git a/5-kyu/sloganMaker.js b/codewars/5-kyu/sloganMaker.js similarity index 100% rename from 5-kyu/sloganMaker.js rename to codewars/5-kyu/sloganMaker.js diff --git a/5-kyu/sloganMaker.ts b/codewars/5-kyu/sloganMaker.ts similarity index 100% rename from 5-kyu/sloganMaker.ts rename to codewars/5-kyu/sloganMaker.ts diff --git a/6-kyu/longestWord.js b/codewars/6-kyu/longestWord.js similarity index 100% rename from 6-kyu/longestWord.js rename to codewars/6-kyu/longestWord.js diff --git a/6-kyu/longestWord.ts b/codewars/6-kyu/longestWord.ts similarity index 100% rename from 6-kyu/longestWord.ts rename to codewars/6-kyu/longestWord.ts diff --git a/7-kyu/commonGround.js b/codewars/7-kyu/commonGround.js similarity index 100% rename from 7-kyu/commonGround.js rename to codewars/7-kyu/commonGround.js diff --git a/7-kyu/commonGround.ts b/codewars/7-kyu/commonGround.ts similarity index 100% rename from 7-kyu/commonGround.ts rename to codewars/7-kyu/commonGround.ts diff --git a/7-kyu/getFirstPython.js b/codewars/7-kyu/getFirstPython.js similarity index 100% rename from 7-kyu/getFirstPython.js rename to codewars/7-kyu/getFirstPython.js diff --git a/7-kyu/getFirstPython.ts b/codewars/7-kyu/getFirstPython.ts similarity index 100% rename from 7-kyu/getFirstPython.ts rename to codewars/7-kyu/getFirstPython.ts diff --git a/leetcode/easy/arrayPrototypeLast.js b/leetcode/easy/arrayPrototypeLast.js new file mode 100644 index 0000000..750a3d9 --- /dev/null +++ b/leetcode/easy/arrayPrototypeLast.js @@ -0,0 +1,13 @@ +"use strict"; +// 2619. Array Prototype Last +// https://leetcode.com/problems/array-prototype-last/ +/** + * @return {null|boolean|number|string|Array|Object} + */ +Array.prototype.last = function () { + return this.length === 0 ? -1 : this.at(-1); +}; +/** + * const arr = [1, 2, 3]; + * arr.last(); // 3 + */ diff --git a/leetcode/easy/arrayPrototypeLast.ts b/leetcode/easy/arrayPrototypeLast.ts new file mode 100644 index 0000000..edd71b8 --- /dev/null +++ b/leetcode/easy/arrayPrototypeLast.ts @@ -0,0 +1,38 @@ +// 2619. Array Prototype Last +// https://leetcode.com/problems/array-prototype-last/ + +// Write code that enhances all arrays such that you can call the array.last() method on any array and it will return the last element. If there are no elements in the array, it should return -1. + +// You may assume the array is the output of JSON.parse. + +// Example 1: + +// Input: nums = [null, {}, 3] +// Output: 3 +// Explanation: Calling nums.last() should return the last element: 3. +// Example 2: + +// Input: nums = [] +// Output: -1 +// Explanation: Because there are no elements, return -1. + +// Constraints: + +// arr is a valid JSON array +// 0 <= arr.length <= 1000 + +interface Array { + last(): T; +} + +/** + * @return {null|boolean|number|string|Array|Object} + */ +Array.prototype.last = function () { + return this.length === 0 ? -1 : this.at(-1); +}; + +/** + * const arr = [1, 2, 3]; + * arr.last(); // 3 + */ diff --git a/leetcode/medium/cacheWithTimeLimit.js b/leetcode/medium/cacheWithTimeLimit.js new file mode 100644 index 0000000..d4894c4 --- /dev/null +++ b/leetcode/medium/cacheWithTimeLimit.js @@ -0,0 +1,49 @@ +"use strict"; +// Write a class that allows getting and setting key-value pairs, however a time until expiration is associated with each key. +// The class has three public methods: +// set(key, value, duration): accepts an integer key, an integer value, and a duration in milliseconds. Once the duration has elapsed, the key should be inaccessible. The method should return true if the same un-expired key already exists and false otherwise. Both the value and duration should be overwritten if the key already exists. +// get(key): if an un-expired key exists, it should return the associated value. Otherwise it should return -1. +// count(): returns the count of un-expired keys. +class TimeLimitedCache { + cache = {}; + /** + * @param {number} key + * @param {number} value + * @param {number} duration time until expiration in ms + * @return {boolean} if un-expired key already existed + */ + set(key, value, duration) { + const currentTime = new Date().getTime(); + let oldNotExpired = false; + if (this.cache[key] && this.cache[key].expiresAt > currentTime) + oldNotExpired = true; + this.cache[key] = { value, expiresAt: currentTime + duration }; + return oldNotExpired; + } + /** + * @param {number} key + * @return {number} value associated with key + */ + get(key) { + const currentTime = new Date().getTime(); + if (this.cache[key] && this.cache[key].expiresAt > currentTime) + return this.cache[key].value; + return -1; + } + /** + * @return {number} count of non-expired keys + */ + count() { + return Object.entries(this.cache).reduce((acc, [key, value]) => { + if (value.expiresAt > new Date().getTime()) + acc += 1; + return acc; + }, 0); + } +} +/** + * const timeLimitedCache = new TimeLimitedCache() + * timeLimitedCache.set(1, 42, 1000); // false + * timeLimitedCache.get(1) // 42 + * timeLimitedCache.count() // 1 + */ diff --git a/leetcode/medium/cacheWithTimeLimit.ts b/leetcode/medium/cacheWithTimeLimit.ts new file mode 100644 index 0000000..ba3dcb8 --- /dev/null +++ b/leetcode/medium/cacheWithTimeLimit.ts @@ -0,0 +1,53 @@ +// Write a class that allows getting and setting key-value pairs, however a time until expiration is associated with each key. + +// The class has three public methods: + +// set(key, value, duration): accepts an integer key, an integer value, and a duration in milliseconds. Once the duration has elapsed, the key should be inaccessible. The method should return true if the same un-expired key already exists and false otherwise. Both the value and duration should be overwritten if the key already exists. + +// get(key): if an un-expired key exists, it should return the associated value. Otherwise it should return -1. + +// count(): returns the count of un-expired keys. + +class TimeLimitedCache { + private cache: Record = {}; + + /** + * @param {number} key + * @param {number} value + * @param {number} duration time until expiration in ms + * @return {boolean} if un-expired key already existed + */ + set(key: number, value: number, duration: number): boolean { + const currentTime = new Date().getTime(); + let oldNotExpired = false; + if (this.cache[key] && this.cache[key].expiresAt > currentTime) oldNotExpired = true; + this.cache[key] = { value, expiresAt: currentTime + duration }; + return oldNotExpired; + } + + /** + * @param {number} key + * @return {number} value associated with key + */ + get(key: number): number { + const currentTime = new Date().getTime(); + if (this.cache[key] && this.cache[key].expiresAt > currentTime) return this.cache[key].value; + return -1; + } + + /** + * @return {number} count of non-expired keys + */ + count(): number { + return Object.entries(this.cache).reduce((acc, [key, value]) => { + if (value.expiresAt > new Date().getTime()) acc += 1; + return acc; + }, 0); + } +} +/** + * const timeLimitedCache = new TimeLimitedCache() + * timeLimitedCache.set(1, 42, 1000); // false + * timeLimitedCache.get(1) // 42 + * timeLimitedCache.count() // 1 + */ diff --git a/leetcode/medium/callFunctionWithCustomContext.js b/leetcode/medium/callFunctionWithCustomContext.js new file mode 100644 index 0000000..a7ad1c6 --- /dev/null +++ b/leetcode/medium/callFunctionWithCustomContext.js @@ -0,0 +1,26 @@ +"use strict"; +// 2693. Call Function with Custom Context +// https://leetcode.com/problems/call-function-with-custom-context/description/ +// Enhance all functions to have the callPolyfill method. The method accepts an object obj as its first parameter and any number of additional arguments. The obj becomes the this context for the function. The additional arguments are passed to the function (that the callPolyfill method belongs on). +// For example if you had the function: +// function tax(price, taxRate) { +// const totalCost = price * (1 + taxRate); +// console.log(`The cost of ${this.item} is ${totalCost}`); +// } +// Calling this function like tax(10, 0.1) will log "The cost of undefined is 11". This is because the this context was not defined. +// However, calling the function like tax.callPolyfill({item: "salad"}, 10, 0.1) will log "The cost of salad is 11". The this context was appropriately set, and the function logged an appropriate output. +// Please solve this without using the built-in Function.call method. +/** + * @param {Object} context + * @param {Array} args + * @return {null|boolean|number|string|Array|Object} + */ +// В целом, решение подходит под условие, но конечно читерское) Посмотрел потом правильное решение через прототипы и понял, что это ужас) +//@ts-ignore - тут ошибка из-за назначения нового свойства, когда его быть не должно в типе. Не понимаю, как решить (?) +Function.prototype.callPolyfill = function (context, ...args) { + return this.apply(context, [this, ...args]); +}; +/** + * function increment() { this.count++; return this.count; } + * increment.callPolyfill({count: 1}); // 2 + */ diff --git a/leetcode/medium/callFunctionWithCustomContext.ts b/leetcode/medium/callFunctionWithCustomContext.ts new file mode 100644 index 0000000..d8534d7 --- /dev/null +++ b/leetcode/medium/callFunctionWithCustomContext.ts @@ -0,0 +1,34 @@ +// 2693. Call Function with Custom Context +// https://leetcode.com/problems/call-function-with-custom-context/description/ + +// Enhance all functions to have the callPolyfill method. The method accepts an object obj as its first parameter and any number of additional arguments. The obj becomes the this context for the function. The additional arguments are passed to the function (that the callPolyfill method belongs on). + +// For example if you had the function: + +// function tax(price, taxRate) { +// const totalCost = price * (1 + taxRate); +// console.log(`The cost of ${this.item} is ${totalCost}`); +// } +// Calling this function like tax(10, 0.1) will log "The cost of undefined is 11". This is because the this context was not defined. + +// However, calling the function like tax.callPolyfill({item: "salad"}, 10, 0.1) will log "The cost of salad is 11". The this context was appropriately set, and the function logged an appropriate output. + +// Please solve this without using the built-in Function.call method. + +/** + * @param {Object} context + * @param {Array} args + * @return {null|boolean|number|string|Array|Object} + */ + +// В целом, решение подходит под условие, но конечно читерское) Посмотрел потом правильное решение через прототипы и понял, что это ужас) + +//@ts-ignore - тут ошибка из-за назначения нового свойства, когда его быть не должно в типе. Не понимаю, как решить (?) +Function.prototype.callPolyfill = function (context: Object, ...args: any[]) { + return this.apply(context, [this, ...args]); +}; + +/** + * function increment() { this.count++; return this.count; } + * increment.callPolyfill({count: 1}); // 2 + */ diff --git a/leetcode/medium/debounce.js b/leetcode/medium/debounce.js new file mode 100644 index 0000000..70706a5 --- /dev/null +++ b/leetcode/medium/debounce.js @@ -0,0 +1,30 @@ +"use strict"; +// // Given a function fn and a time in milliseconds t, return a debounced version of that function. +// // A debounced function is a function whose execution is delayed by t milliseconds and whose execution is cancelled if it is called again within that window of time. The debounced function should also receive the passed parameters. +// // For example, let's say t = 50ms, and the function was called at 30ms, 60ms, and 100ms. +// // The first 2 function calls would be cancelled, and the 3rd function call would be executed at 150ms. +// // If instead t = 35ms, The 1st call would be cancelled, the 2nd would be executed at 95ms, and the 3rd would be executed at 135ms. +// Constraints: 0 <= t <= 1000; +// 1 <= calls.length <= 10; +// 0 <= calls[i].t <= 1000; +// 0 <= calls[i].inputs.length <= 10; +/** + * @param {Function} fn + * @param {number} t milliseconds + * @return {Function} + */ +var debounce = function (fn, t) { + let timeout; + return function (...args) { + clearTimeout(timeout); + timeout = setTimeout(() => { + fn(...args); + }, t); + }; +}; +/** + * const log = debounce(console.log, 100); + * log('Hello'); // cancelled + * log('Hello'); // cancelled + * log('Hello'); // Logged at t=100ms + */ diff --git a/leetcode/medium/debounce.ts b/leetcode/medium/debounce.ts new file mode 100644 index 0000000..a91771a --- /dev/null +++ b/leetcode/medium/debounce.ts @@ -0,0 +1,36 @@ +// // Given a function fn and a time in milliseconds t, return a debounced version of that function. + +// // A debounced function is a function whose execution is delayed by t milliseconds and whose execution is cancelled if it is called again within that window of time. The debounced function should also receive the passed parameters. + +// // For example, let's say t = 50ms, and the function was called at 30ms, 60ms, and 100ms. + +// // The first 2 function calls would be cancelled, and the 3rd function call would be executed at 150ms. + +// // If instead t = 35ms, The 1st call would be cancelled, the 2nd would be executed at 95ms, and the 3rd would be executed at 135ms. +// Constraints: 0 <= t <= 1000; +// 1 <= calls.length <= 10; +// 0 <= calls[i].t <= 1000; +// 0 <= calls[i].inputs.length <= 10; + +/** + * @param {Function} fn + * @param {number} t milliseconds + * @return {Function} + */ + +var debounce = function (fn: (...args: Array) => unknown, t: number): (...args: Array) => void { + let timeout: ReturnType; + return function (...args: any[]) { + clearTimeout(timeout); + timeout = setTimeout(() => { + fn(...args); + }, t); + }; +}; + +/** + * const log = debounce(console.log, 100); + * log('Hello'); // cancelled + * log('Hello'); // cancelled + * log('Hello'); // Logged at t=100ms + */ diff --git a/leetcode/medium/eventEmitter.js b/leetcode/medium/eventEmitter.js new file mode 100644 index 0000000..6671347 --- /dev/null +++ b/leetcode/medium/eventEmitter.js @@ -0,0 +1,55 @@ +"use strict"; +// 2694. Event Emitter +// https://leetcode.com/problems/event-emitter/description/ +// Design an EventEmitter class. This interface is similar (but with some differences) to the one found in Node.js or the Event Target interface of the DOM. The EventEmitter should allow for subscribing to events and emitting them. +// Your EventEmitter class should have the following two methods: +// subscribe - This method takes in two arguments: the name of an event as a string and a callback function. This callback function will later be called when the event is emitted. +// An event should be able to have multiple listeners for the same event. When emitting an event with multiple callbacks, each should be called in the order in which they were subscribed. An array of results should be returned. You can assume no callbacks passed to subscribe are referentially identical. +// The subscribe method should also return an object with an unsubscribe method that enables the user to unsubscribe. When it is called, the callback should be removed from the list of subscriptions and undefined should be returned. +// emit - This method takes in two arguments: the name of an event as a string and an optional array of arguments that will be passed to the callback(s). If there are no callbacks subscribed to the given event, return an empty array. Otherwise, return an array of the results of all callback calls in the order they were subscribed. +class EventEmitter { + eventsCallbacksMap = new Map(); + /** + * @param {string} eventName + * @param {Function} callback + * @return {Object} + */ + subscribe(eventName, callback) { + const eventCallbacks = this.eventsCallbacksMap.get(eventName); + if (!eventCallbacks?.length) + this.eventsCallbacksMap.set(eventName, [callback]); + else + eventCallbacks.push(callback); + return { + unsubscribe: () => { + const eventCallbacks = this.eventsCallbacksMap.get(eventName); + if (!eventCallbacks) + return; + const filteredEventCallbacks = eventCallbacks.filter((_callback) => _callback !== callback); + this.eventsCallbacksMap.set(eventName, filteredEventCallbacks); + }, + }; + } + /** + * @param {string} eventName + * @param {Array} args + * @return {Array} + */ + emit(eventName, args = []) { + const eventCallbacks = this.eventsCallbacksMap.get(eventName); + if (!eventCallbacks?.length) + return []; + return eventCallbacks.map((callback) => callback(...args)); + } +} +/** + * const emitter = new EventEmitter(); + * + * // Subscribe to the onClick event with onClickCallback + * function onClickCallback() { return 99 } + * const sub = emitter.subscribe('onClick', onClickCallback); + * + * emitter.emit('onClick'); // [99] + * sub.unsubscribe(); // undefined + * emitter.emit('onClick'); // [] + */ diff --git a/leetcode/medium/eventEmitter.ts b/leetcode/medium/eventEmitter.ts new file mode 100644 index 0000000..ec9b381 --- /dev/null +++ b/leetcode/medium/eventEmitter.ts @@ -0,0 +1,57 @@ +// 2694. Event Emitter +// https://leetcode.com/problems/event-emitter/description/ + +// Design an EventEmitter class. This interface is similar (but with some differences) to the one found in Node.js or the Event Target interface of the DOM. The EventEmitter should allow for subscribing to events and emitting them. + +// Your EventEmitter class should have the following two methods: + +// subscribe - This method takes in two arguments: the name of an event as a string and a callback function. This callback function will later be called when the event is emitted. +// An event should be able to have multiple listeners for the same event. When emitting an event with multiple callbacks, each should be called in the order in which they were subscribed. An array of results should be returned. You can assume no callbacks passed to subscribe are referentially identical. +// The subscribe method should also return an object with an unsubscribe method that enables the user to unsubscribe. When it is called, the callback should be removed from the list of subscriptions and undefined should be returned. +// emit - This method takes in two arguments: the name of an event as a string and an optional array of arguments that will be passed to the callback(s). If there are no callbacks subscribed to the given event, return an empty array. Otherwise, return an array of the results of all callback calls in the order they were subscribed. + +class EventEmitter { + private eventsCallbacksMap = new Map(); + /** + * @param {string} eventName + * @param {Function} callback + * @return {Object} + */ + subscribe(eventName: string, callback: Function) { + const eventCallbacks = this.eventsCallbacksMap.get(eventName); + if (!eventCallbacks?.length) this.eventsCallbacksMap.set(eventName, [callback]); + else eventCallbacks.push(callback); + + return { + unsubscribe: () => { + const eventCallbacks = this.eventsCallbacksMap.get(eventName); + if (!eventCallbacks) return; + const filteredEventCallbacks = eventCallbacks.filter((_callback) => _callback !== callback); + this.eventsCallbacksMap.set(eventName, filteredEventCallbacks); + }, + }; + } + + /** + * @param {string} eventName + * @param {Array} args + * @return {Array} + */ + emit(eventName: string, args: Array = []) { + const eventCallbacks = this.eventsCallbacksMap.get(eventName); + if (!eventCallbacks?.length) return []; + return eventCallbacks.map((callback) => callback(...args)); + } +} + +/** + * const emitter = new EventEmitter(); + * + * // Subscribe to the onClick event with onClickCallback + * function onClickCallback() { return 99 } + * const sub = emitter.subscribe('onClick', onClickCallback); + * + * emitter.emit('onClick'); // [99] + * sub.unsubscribe(); // undefined + * emitter.emit('onClick'); // [] + */ diff --git a/leetcode/medium/executeAsynchronousFunctionsInParallel.js b/leetcode/medium/executeAsynchronousFunctionsInParallel.js new file mode 100644 index 0000000..b9a5373 --- /dev/null +++ b/leetcode/medium/executeAsynchronousFunctionsInParallel.js @@ -0,0 +1,33 @@ +"use strict"; +// 2721. Execute Asynchronous Functions in Parallel +// https://leetcode.com/problems/execute-asynchronous-functions-in-parallel/description/ +// Given an array of asynchronous functions functions, return a new promise promise. Each function in the array accepts no arguments and returns a promise. All the promises should be executed in parallel. +// promise resolves: +// When all the promises returned from functions were resolved successfully in parallel. The resolved value of promise should be an array of all the resolved values of promises in the same order as they were in the functions. The promise should resolve when all the asynchronous functions in the array have completed execution in parallel. +// promise rejects: +// When any of the promises returned from functions were rejected. promise should also reject with the reason of the first rejection. +// Please solve it without using the built-in Promise.all function. +/** + * @param {Array} functions + * @return {Promise} + */ +const promiseAll = function (functions) { + return new Promise((finalResolve, finalReject) => { + const results = []; + let resolvedPromises = 0; + functions.forEach((func, index) => { + func() + .then((res) => { + results[index] = res; + resolvedPromises++; + if (resolvedPromises === functions.length) + finalResolve(results); + }) + .catch((err) => finalReject(err)); + }); + }); +}; +/** + * const promise = promiseAll([() => new Promise(res => res(42))]) + * promise.then(console.log); // [42] + */ diff --git a/leetcode/medium/executeAsynchronousFunctionsInParallel.ts b/leetcode/medium/executeAsynchronousFunctionsInParallel.ts new file mode 100644 index 0000000..c406e62 --- /dev/null +++ b/leetcode/medium/executeAsynchronousFunctionsInParallel.ts @@ -0,0 +1,37 @@ +// 2721. Execute Asynchronous Functions in Parallel +// https://leetcode.com/problems/execute-asynchronous-functions-in-parallel/description/ + +// Given an array of asynchronous functions functions, return a new promise promise. Each function in the array accepts no arguments and returns a promise. All the promises should be executed in parallel. + +// promise resolves: + +// When all the promises returned from functions were resolved successfully in parallel. The resolved value of promise should be an array of all the resolved values of promises in the same order as they were in the functions. The promise should resolve when all the asynchronous functions in the array have completed execution in parallel. +// promise rejects: + +// When any of the promises returned from functions were rejected. promise should also reject with the reason of the first rejection. +// Please solve it without using the built-in Promise.all function. + +/** + * @param {Array} functions + * @return {Promise} + */ +const promiseAll = function (functions: Array<() => Promise>): Promise { + return new Promise((finalResolve, finalReject) => { + const results: Array = []; + let resolvedPromises = 0; + functions.forEach((func, index) => { + func() + .then((res) => { + results[index] = res; + resolvedPromises++; + if (resolvedPromises === functions.length) finalResolve(results); + }) + .catch((err) => finalReject(err)); + }); + }); +}; + +/** + * const promise = promiseAll([() => new Promise(res => res(42))]) + * promise.then(console.log); // [42] + */ diff --git a/leetcode/medium/groupBy.js b/leetcode/medium/groupBy.js new file mode 100644 index 0000000..d6d97da --- /dev/null +++ b/leetcode/medium/groupBy.js @@ -0,0 +1,26 @@ +"use strict"; +// 2631. Group By +// https://leetcode.com/problems/group-by/description/ +// Write code that enhances all arrays such that you can call the array.groupBy(fn) method on any array and it will return a grouped version of the array. +// A grouped array is an object where each key is the output of fn(arr[i]) and each value is an array containing all items in the original array which generate that key. +// The provided callback fn will accept an item in the array and return a string key. +// The order of each value list should be the order the items appear in the array. Any order of keys is acceptable. +// Please solve it without lodash's _.groupBy function. +/** + * @param {Function} fn + * @return {Object} + */ +//@ts-ignore - тут ошибка из-за назначения нового свойства, когда его быть не должно в типе. Не понимаю, как решить (?) +Array.prototype.groupBy = function (fn) { + return this.reduce((acc, current) => { + const res = fn(current); + if (acc[res]?.length) + acc[res].push(current); + else + acc[res] = [current]; + return acc; + }, {}); +}; +/** + * [1,2,3].groupBy(String) // {"1":[1],"2":[2],"3":[3]} + */ diff --git a/leetcode/medium/groupBy.ts b/leetcode/medium/groupBy.ts new file mode 100644 index 0000000..8d1ba54 --- /dev/null +++ b/leetcode/medium/groupBy.ts @@ -0,0 +1,29 @@ +// 2631. Group By +// https://leetcode.com/problems/group-by/description/ + +// Write code that enhances all arrays such that you can call the array.groupBy(fn) method on any array and it will return a grouped version of the array. + +// A grouped array is an object where each key is the output of fn(arr[i]) and each value is an array containing all items in the original array which generate that key. + +// The provided callback fn will accept an item in the array and return a string key. + +// The order of each value list should be the order the items appear in the array. Any order of keys is acceptable. + +// Please solve it without lodash's _.groupBy function. + +/** + * @param {Function} fn + * @return {Object} + */ +//@ts-ignore - тут ошибка из-за назначения нового свойства, когда его быть не должно в типе. Не понимаю, как решить (?) +(Array as Array).prototype.groupBy = function (fn: (item: T) => string) { + return this.reduce>>((acc: Record>, current: T) => { + const res = fn(current); + if (acc[res]?.length) acc[res].push(current); + else acc[res] = [current]; + return acc; + }, {}); +}; +/** + * [1,2,3].groupBy(String) // {"1":[1],"2":[2],"3":[3]} + */ diff --git a/leetcode/medium/promiseTimeLimit.js b/leetcode/medium/promiseTimeLimit.js new file mode 100644 index 0000000..c1c4f79 --- /dev/null +++ b/leetcode/medium/promiseTimeLimit.js @@ -0,0 +1,26 @@ +"use strict"; +// 2637. Promise Time Limit +// https://leetcode.com/problems/promise-time-limit/description/ +// Given an asynchronous function fn and a time t in milliseconds, return a new time limited version of the input function. fn takes arguments provided to the time limited function. +// The time limited function should follow these rules: +// If the fn completes within the time limit of t milliseconds, the time limited function should resolve with the result. +// If the execution of the fn exceeds the time limit, the time limited function should reject with the string "Time Limit Exceeded". +/** + * @param {Function} fn + * @param {number} t + * @return {Function} + */ +const timeLimit = function (fn, t) { + return async function (...args) { + return new Promise((resolve, reject) => { + fn(...args) + .then((res) => resolve(res)) + .catch((e) => reject(e)); + setTimeout(() => reject("Time Limit Exceeded"), t); + }); + }; +}; +/** + * const limited = timeLimit((t) => new Promise(res => setTimeout(res, t)), 100); + * limited(150).catch(console.log) // "Time Limit Exceeded" at t=100ms + */ diff --git a/leetcode/medium/promiseTimeLimit.ts b/leetcode/medium/promiseTimeLimit.ts new file mode 100644 index 0000000..b8b0e78 --- /dev/null +++ b/leetcode/medium/promiseTimeLimit.ts @@ -0,0 +1,30 @@ +// 2637. Promise Time Limit +// https://leetcode.com/problems/promise-time-limit/description/ + +// Given an asynchronous function fn and a time t in milliseconds, return a new time limited version of the input function. fn takes arguments provided to the time limited function. + +// The time limited function should follow these rules: + +// If the fn completes within the time limit of t milliseconds, the time limited function should resolve with the result. +// If the execution of the fn exceeds the time limit, the time limited function should reject with the string "Time Limit Exceeded". + +/** + * @param {Function} fn + * @param {number} t + * @return {Function} + */ +const timeLimit = function (fn: (...args: any[]) => Promise, t: number): () => Promise { + return async function (...args: any[]) { + return new Promise((resolve, reject) => { + fn(...args) + .then((res: any) => resolve(res)) + .catch((e) => reject(e)); + setTimeout(() => reject("Time Limit Exceeded"), t); + }); + }; +}; + +/** + * const limited = timeLimit((t) => new Promise(res => setTimeout(res, t)), 100); + * limited(150).catch(console.log) // "Time Limit Exceeded" at t=100ms + */