From 161f949374a1df72ad435455b99f39f38306cbee Mon Sep 17 00:00:00 2001 From: Eugene Maslovich Date: Tue, 29 Jul 2025 15:00:37 +0000 Subject: [PATCH 01/52] Remove outdated tasks and code implementations from days 6 to 9, including skills sorting, tree traversal, and debounce functions. Add new tasks for days 10 and 3, focusing on range reduction, async semaphore, and binary search with callbacks. Implement LRU cache and string compression functionalities. --- .idea/.gitignore | 5 - day_1/1_solution.js | 105 ------------ day_1/2.js | 129 --------------- ...0\320\264\320\260\321\207\320\270 (10).md" | 51 ++++++ day_11/1.js | 23 --- ...0\320\264\320\260\321\207\320\270 (11).md" | 50 +++--- ...0\320\264\320\260\321\207\320\270 (12).md" | 59 ++++--- ...0\320\264\320\260\321\207\320\270 (13).md" | 49 +++--- ...0\320\264\320\260\321\207\320\270 (14).md" | 69 ++++---- ...0\320\264\320\260\321\207\320\270 (15).md" | 43 ----- ...0\320\264\320\260\321\207\320\270 (16).md" | 61 ------- day_2/1.js | 13 -- day_2/2.js | 24 --- day_2/3.js | 35 ---- day_2/4.js | 27 --- day_2/mapSet.js | 30 ---- ...60\320\264\320\260\321\207\320\270 (3).md" | 66 ++++++++ day_4/1.js | 19 --- day_4/2.js | 34 ---- day_4/3.js | 45 ----- day_4/4.js | 35 ---- ...60\320\264\320\260\321\207\320\270 (4).md" | 83 +++++----- day_5/1.js | 31 ---- day_5/2.js | 34 ---- day_5/3.js | 24 --- day_5/4.js | 20 --- ...60\320\264\320\260\321\207\320\270 (5).md" | 0 ...20\260\320\264\320\260\321\207\320\270.md" | 59 ------- day_6/1.js | 40 ----- day_6/2.js | 68 -------- day_6/3.js | 43 ----- day_6/4.js | 90 ---------- ...60\320\264\320\260\321\207\320\270 (6).md" | 0 day_7/1.js | 28 ---- day_7/2.js | 26 --- day_7/3.js | 40 ----- ...60\320\264\320\260\321\207\320\270 (7).md" | 0 day_8/0.js | 156 ------------------ day_8/1.js | 7 - day_8/2.js | 25 --- day_8/3.js | 50 ------ ...60\320\264\320\260\321\207\320\270 (8).md" | 0 day_9/1.js | 25 --- day_9/2.js | 52 ------ day_9/3.js | 23 --- day_9/4.js | 19 --- ...60\320\264\320\260\321\207\320\270 (9).md" | 46 ++++++ 47 files changed, 318 insertions(+), 1643 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 day_1/1_solution.js delete mode 100644 day_1/2.js create mode 100644 "day_10/\320\227\320\260\320\264\320\260\321\207\320\270 (10).md" delete mode 100644 day_11/1.js delete mode 100644 "day_15/\320\227\320\260\320\264\320\260\321\207\320\270 (15).md" delete mode 100644 "day_16/\320\227\320\260\320\264\320\260\321\207\320\270 (16).md" delete mode 100644 day_2/1.js delete mode 100644 day_2/2.js delete mode 100644 day_2/3.js delete mode 100644 day_2/4.js delete mode 100644 day_2/mapSet.js create mode 100644 "day_3/\320\227\320\260\320\264\320\260\321\207\320\270 (3).md" delete mode 100644 day_4/1.js delete mode 100644 day_4/2.js delete mode 100644 day_4/3.js delete mode 100644 day_4/4.js delete mode 100644 day_5/1.js delete mode 100644 day_5/2.js delete mode 100644 day_5/3.js delete mode 100644 day_5/4.js rename "day_6/\320\227\320\260\320\264\320\260\321\207\320\270.md" => "day_5/\320\227\320\260\320\264\320\260\321\207\320\270 (5).md" (100%) delete mode 100644 "day_5/\320\227\320\260\320\264\320\260\321\207\320\270.md" delete mode 100644 day_6/1.js delete mode 100644 day_6/2.js delete mode 100644 day_6/3.js delete mode 100644 day_6/4.js rename "day_7/\320\227\320\260\320\264\320\260\321\207\320\270.md" => "day_6/\320\227\320\260\320\264\320\260\321\207\320\270 (6).md" (100%) delete mode 100644 day_7/1.js delete mode 100644 day_7/2.js delete mode 100644 day_7/3.js rename "day_8/\320\227\320\260\320\264\320\260\321\207\320\270.md" => "day_7/\320\227\320\260\320\264\320\260\321\207\320\270 (7).md" (100%) delete mode 100644 day_8/0.js delete mode 100644 day_8/1.js delete mode 100644 day_8/2.js delete mode 100644 day_8/3.js rename "day_9/\320\227\320\260\320\264\320\260\321\207\320\270 (10).md" => "day_8/\320\227\320\260\320\264\320\260\321\207\320\270 (8).md" (100%) delete mode 100644 day_9/1.js delete mode 100644 day_9/2.js delete mode 100644 day_9/3.js delete mode 100644 day_9/4.js create mode 100644 "day_9/\320\227\320\260\320\264\320\260\321\207\320\270 (9).md" diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index b58b603..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/day_1/1_solution.js b/day_1/1_solution.js deleted file mode 100644 index 8e9add8..0000000 --- a/day_1/1_solution.js +++ /dev/null @@ -1,105 +0,0 @@ -"use strict" -class Foo { - bar = 1; - - bla = () => console.log(this.bar); - - baz = function () { console.log(this.bar); }; -} - -new Foo().bla(); // day_1 -new Foo().baz(); // undefined // why day_1 - I don't understand - -///////////////////////////////////////////// - -class Parent { - foo() { - console.log('It works!'); - } -} - -class Example extends Parent {} - -function partial(classObj, mixin) { - // classObj.prototype // adding any fields or methods to class.prototype - Object.setPrototypeOf(mixin, Object.getPrototypeOf(classObj.prototype)) // setting prototype to mixin obj - Object.defineProperties(classObj.prototype, Object.getOwnPropertyDescriptors(mixin)) // define the properties from mixin to classObj -} - -partial(Example, { - foo() { - super.foo(); - console.log('Yeaaah'); - }, - - get bar() { - return Math.random(); - } -}); - -const example = new Example(); - -example.foo(); // It works! Yeaaah - -console.log(example.bar); // Случайное число -console.log(example.bar); // Случайное число -console.log(example.bar); // Случайное число - -//////////////////////// - -const format = (template, data) => { - return template.replace(/\${(.*?)}/g, (_, exportBlock) => { - return Function(...Object.keys(data), `return ${exportBlock}`)(...Object.values(data)); - }) -} - - -console.log(format('Hello ${name}! May age is ${age * day_2}.', {name: 'Bob', age: 12})); // 'Hello Bob! My age is 24.' - - -///////////// - -function allSettled(iter) { - const promises = [...iter].map((el) => Promise.resolve(el)); - - const res = new Array(promises.length); - - let total = 0; - - return new Promise((resolve) => { - promises.forEach((promise, i) => { - promise - .then((value) => { - res[i] = { - status: 'fulfilled', - value - } - - total++; - - if (total >= res.length) { - resolve(res); - } - }) - .catch((reason) => { - res[i] = { - status: 'rejected', - reason - } - - total++; - - if (total >= res.length) { - resolve(res); - } - }) - }) - }) -} - - -allSettled([1, Promise.resolve(2), Promise.reject(3)]).then(([v1, v2, v3]) => { - console.log(v1); // {status: 'fulfilled', value: day_1} - console.log(v2); // {status: 'fulfilled', value: day_2} - console.log(v3); // {status: 'rejected', reason: 3} -}); \ No newline at end of file diff --git a/day_1/2.js b/day_1/2.js deleted file mode 100644 index 0f4ad92..0000000 --- a/day_1/2.js +++ /dev/null @@ -1,129 +0,0 @@ -// allSettled([1, Promise.resolve(2), Promise.reject(3)]).then(([v1, v2, v3]) => { -// console.log(v1); // {status: 'fulfilled', value: day_1} -// console.log(v2); // {status: 'fulfilled', value: day_2} -// console.log(v3); // {status: 'rejected', reason: 3} -// }); - -// const promise1 = Promise.resolve(3); -// const promise2 = 42; -// const promise3 = new Promise((resolve, reject) => { -// setTimeout(resolve, 100, 'foo'); -// }); -// -// promiseAll([promise1, promise2, promise3]).then((values) => { -// console.log(values); -// }); -// Expected output: Array [3, 42, "foo"] - -// const promise1 = Promise.reject(0); -// const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'quick')); -// const promise3 = new Promise((resolve) => setTimeout(resolve, 500, 'slow')); -// -// const promises = [promise1, promise2, promise3]; -// -// promiseAny(promises).then((value) => console.log(value)); - -// Expected output: "quick" - -const promise1 = new Promise((resolve, reject) => { - setTimeout(resolve, 500, 'one'); -}); - -const promise2 = new Promise((resolve, reject) => { - setTimeout(resolve, 100, 'two'); -}); - -promiseRace([promise1, promise2]).then((value) => { - console.log(value); - // Both resolve, but promise2 is faster -}); -// Expected output: "two" - - -function promiseAll(promises) { - const result = new Array(promises.length); - let resolvedPromisesCount = 0; - - return new Promise((resolve, reject) => { - if (!promises || !Array.isArray(promises) || !promises?.length) { - resolve(result); - } - - promises.forEach(async (promise, index) => { - try { - const res = await Promise.resolve(promise); - result[index] = res; - resolvedPromisesCount += 1; - - if (resolvedPromisesCount === promises.length) { - resolve(result); - } - } catch (e) { - reject(e); - } - }); - }); -} - -function allSettled(promises) { - const result = new Array(promises.length); - let total = 0; - - - return new Promise((resolve) => { - promises.forEach((promise, index) => { - Promise.resolve(promise) - .then((value) => { - result[index] = {status: 'fulfilled', value} - total += 1; - - if (total === promises.length) { - resolve(result); - } - }) - .catch((reason) => { - result[index] = {status: 'rejected', reason} - total += 1; - - if (total === promises.length) { - resolve(result); - } - }) - }); - }); -} - -function promiseAny(iters) { - const promises = [...iters].map((iter) => Promise.resolve(iter)); - const rejectsArray = new Array(promises.length); - let totalRejectedCount = 0; - - return new Promise((resolve, reject) => { - if (!promises.length) { - reject(rejectsArray); - } - - promises.forEach((promise, index) => { - promise - .then((value) => resolve(value)) - .catch((reason) => { - rejectsArray[index] = reason; - totalRejectedCount += 1; - - if (totalRejectedCount === promises.length) { - reject(rejectsArray) - } - }) - }); - }); -} - -function promiseRace(iters) { - const promises = [...iters].map((iter) => Promise.resolve(iter)); - - return new Promise((resolve, reject) => { - promises.forEach((promise) => { - promise.then((value) => resolve(value)); - }) - }); -} \ No newline at end of file diff --git "a/day_10/\320\227\320\260\320\264\320\260\321\207\320\270 (10).md" "b/day_10/\320\227\320\260\320\264\320\260\321\207\320\270 (10).md" new file mode 100644 index 0000000..89af763 --- /dev/null +++ "b/day_10/\320\227\320\260\320\264\320\260\321\207\320\270 (10).md" @@ -0,0 +1,51 @@ +# День 10 + +## Реализовать функцию свертки списка в диапазоны + +Необходимо написать функцию, которая бы принимала неотсортированный массив чисел и возвращала бы строку, где подряд идущие числа свернуты в диапазоны. + +```js +console.log(reduce([1, 3, 6, 8, 7, 11, 45, 46, 2])); // 1-3, 6-8, 11, 45-46 +``` + +## Параллельные асинхронные запросы + +Необходимо написать функцию, которая бы принимала Iterable функций и возвращала результат аналогичный Promise.allSettled. +Каждая из переданных функций может вернуть Promise. Одновременно может быть запущено не более заданного количества Promise, но при этом максимально возможное. + +```js +allSettledLimit([ + () => fetch('//some-data-1'), + () => fetch('//some-data-2'), + () => fetch('//some-data-3'), + () => fetch('//some-data-4') +], 2).then(console.log); +``` + +## Написать итератор для дерева + +Необходимо написать итератор для заданной структуры дерева. + +```js +const i = iterate({ + value: 1, + children: [{value: 2}, {value: 3, children: [{value: 4}]}] +}); + +console.log(i.next()); // {value: 1, done: false} +console.log(i.next()); // {value: 2, done: false} +console.log(i.next()); // {value: 3, done: false} +console.log(i.next()); // {value: 4, done: false} +console.log(i.next()); // {value: undefined, done: true} +``` + +## Реализация функции аналогичной parseFloat + +Необходимо написать функцию, которая бы повторяло поведение parseFloat. + +```js +console.log(myParseFloat('10')); // 10 +console.log(myParseFloat('-10.2')); // -10.2 +console.log(myParseFloat('6e-2')); // 0.06 +console.log(myParseFloat('--20')); // NaN +``` diff --git a/day_11/1.js b/day_11/1.js deleted file mode 100644 index 6b7efd1..0000000 --- a/day_11/1.js +++ /dev/null @@ -1,23 +0,0 @@ -console.log(bisect([1, 2, 3, 4, 5, 6, 7], (val) => 4 - val)); // 3 -console.log(bisect([1, 2, 3, 4, 5, 6, 7], (val) => 234 - val)); // -1 - -function bisect(arr, compator) { - let start = 0; - let end = arr.length - 1; - - while (start <= end) { - let mid = end - start + 1; - const result = compator(arr[mid]); - - if (result === 0) { - return mid; - } - if (result > 0) { - start = mid + 1; - } else { - end = mid - 1; - } - } - - return undefined; -} \ No newline at end of file diff --git "a/day_11/\320\227\320\260\320\264\320\260\321\207\320\270 (11).md" "b/day_11/\320\227\320\260\320\264\320\260\321\207\320\270 (11).md" index f4da58f..509829f 100644 --- "a/day_11/\320\227\320\260\320\264\320\260\321\207\320\270 (11).md" +++ "b/day_11/\320\227\320\260\320\264\320\260\321\207\320\270 (11).md" @@ -1,46 +1,44 @@ -# День 9 +# День 11 -## Реализовать функция бинарного поиска с callback +## Определить век по году -Необходимо написать функцию, которая бы принимала отсортированный массив и функцию-компаратор и возвращала бы индекс найденного элемента (если компаратор вернул 0). -Для поиска должен использоваться алгоритм бинарного поиска. +Необходимо написать функцию, которая определяет век по переданному году. ```js -console.log(bisect([1, 2, 3, 4, 5, 6, 7], (val) => 4 - val)); // 3 -console.log(bisect([1, 2, 3, 4, 5, 6, 7], (val) => 234 - val)); // -1 +console.log(getCentury(1901)); // 20 ``` -## Реализовать функцию для амортизируемого повторного запроса +## Пересечение диапазонов -Необходимо написать функцию retry, которая бы принимала бы функцию, возвращающую Promise и объект настроек для указания повторений в случае ошибки. -Параметр `retry` задает максимальное количество повторения, а `delay` - задержку в мс между повторами (задается в виде функции, которая принимает номер попытки). +Необходимо написать функцию, которая бы принимала бы две строки с числовыми диапазонами и возвращала бы новую строку, где отображены пересечения этих интервалов. ```js -retry(() => fetch('//some-data'), {retry: 3, delay: (n) => n * 1000}).then(console.log, console.error); +console.log(intersectRanges('1-2; 4-6; 9-11', '1-5; 10-14; 15')); // 1-2; 4-5; 10-11 ``` -## Реализовать класс LRU кеша +## Найти два элемента массива сумма которых дает заданное число -Необходимо реализовать класс кеширования с алгоритмом LRU. +Необходимо написать функцию, которая бы принимала массив чисел и число и возвращала бы индексы двух элементов массива сумма которых даёт заданное число. ```js -const cache = new LRUCache(3); // Размер кеша - -cache.set('key1', 1); -cache.set('key2', 2); -cache.set('key3', 3); - -console.log(cache.get('key1')); // 1 - -cache.set('key4', 4); - -console.log(cache.has('key2')); // false +console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1] +console.log(twoSum([3, 2, 4], 6)); // [1, 2] ``` -## Сжатие строки +## Реализовать итератор на основе EventEmitter -Необходимо написать функцию, которая бы принимала бы строку и "схлопывала" бы все подряд идущие повторения. +Необходимо написать функцию, которая бы создавала асинхронный итератор на основе заданного EventEmitter и события. ```js -console.log(zipStr('abbaabbafffbezza')); // abafbeza +const ee = new EventEmitter(); + +(async () => { + for await (const e of stream(ee, 'foo')) { + console.log(e); // 1 2 3 + } +})(); + +ee.emit('foo', 1); +ee.emit('foo', 2); +ee.emit('foo', 3); ``` diff --git "a/day_12/\320\227\320\260\320\264\320\260\321\207\320\270 (12).md" "b/day_12/\320\227\320\260\320\264\320\260\321\207\320\270 (12).md" index 89af763..e8f11d5 100644 --- "a/day_12/\320\227\320\260\320\264\320\260\321\207\320\270 (12).md" +++ "b/day_12/\320\227\320\260\320\264\320\260\321\207\320\270 (12).md" @@ -1,51 +1,48 @@ -# День 10 +# День 12 -## Реализовать функцию свертки списка в диапазоны +## Определение палиндрома -Необходимо написать функцию, которая бы принимала неотсортированный массив чисел и возвращала бы строку, где подряд идущие числа свернуты в диапазоны. +Необходимо определить, является ли строка палиндромом или нет. ```js -console.log(reduce([1, 3, 6, 8, 7, 11, 45, 46, 2])); // 1-3, 6-8, 11, 45-46 +console.log(isPalindrome('bob')); // true +console.log(isPalindrome('abba')); // true +console.log(isPalindrome('a')); // false +console.log(isPalindrome('azt')); // false ``` -## Параллельные асинхронные запросы +## Извлечение подстрок в кавычках -Необходимо написать функцию, которая бы принимала Iterable функций и возвращала результат аналогичный Promise.allSettled. -Каждая из переданных функций может вернуть Promise. Одновременно может быть запущено не более заданного количества Promise, но при этом максимально возможное. +Необходимо написать функцию, которая принимает строку и возвращает массив подстроку исходной. +Подстроки выбираются по признаку нахождения в тех или иных кавычках. ```js -allSettledLimit([ - () => fetch('//some-data-1'), - () => fetch('//some-data-2'), - () => fetch('//some-data-3'), - () => fetch('//some-data-4') -], 2).then(console.log); +console.log(extractQuotes('Это строка в "кавычках\'" и `"эта"` тоже, а это "хитрая строка\\""')); // ["кавычках'", '"эта"', 'хитрая строка\\"'] ``` -## Написать итератор для дерева +## Реализовать zip для асинхронно итерируемых объектов -Необходимо написать итератор для заданной структуры дерева. +Необходимо реализовать функцию zip для множества асинхронно итерируемых объектов. ```js -const i = iterate({ - value: 1, - children: [{value: 2}, {value: 3, children: [{value: 4}]}] -}); - -console.log(i.next()); // {value: 1, done: false} -console.log(i.next()); // {value: 2, done: false} -console.log(i.next()); // {value: 3, done: false} -console.log(i.next()); // {value: 4, done: false} -console.log(i.next()); // {value: undefined, done: true} +async function* makeAsync(iter) { + yield* iter; +} + +// [1, 'a', '.'] [2, 'b', '.'] +(async () => { + for await (const el of zip(makeAsync(new Set([1, 2])), makeAsync(['a', 'b', 'z']), makeAsync('...'))) { + console.log(el); + } +})(); ``` -## Реализация функции аналогичной parseFloat +## Вычислить разницу двух строк -Необходимо написать функцию, которая бы повторяло поведение parseFloat. +Необходимо написать функцию, которая бы вычисляла разницу между двумя заданными строками. +Разница вычисляется из суммы всех добавлений, удалений и замен символов так, чтобы строки стали равны. ```js -console.log(myParseFloat('10')); // 10 -console.log(myParseFloat('-10.2')); // -10.2 -console.log(myParseFloat('6e-2')); // 0.06 -console.log(myParseFloat('--20')); // NaN +console.log(diff('bob', 'rob')); // 1 (одна замена) +console.log(diff('австрия', 'австралия')); // 2 (два удаления) ``` diff --git "a/day_13/\320\227\320\260\320\264\320\260\321\207\320\270 (13).md" "b/day_13/\320\227\320\260\320\264\320\260\321\207\320\270 (13).md" index 509829f..8680005 100644 --- "a/day_13/\320\227\320\260\320\264\320\260\321\207\320\270 (13).md" +++ "b/day_13/\320\227\320\260\320\264\320\260\321\207\320\270 (13).md" @@ -1,44 +1,43 @@ -# День 11 +# День 13 -## Определить век по году +## Поиск подстроки в строке -Необходимо написать функцию, которая определяет век по переданному году. +Необходимо написать функцию, которая принимала бы строку и подстроку и возвращала бы true, если заданная подстрока присутствует в строке. +Регулярные выражения использовать нельзя. ```js -console.log(getCentury(1901)); // 20 +console.log(includes('hello bob!', 'bob')); // true +console.log(includes('abba', 'aba')); // false ``` -## Пересечение диапазонов +## Ломающий коммит -Необходимо написать функцию, которая бы принимала бы две строки с числовыми диапазонами и возвращала бы новую строку, где отображены пересечения этих интервалов. +Имеется множество коммитов и функция проверки работы программы. На одном из коммитов программа начинает ломаться. +Необходимо выяснить за минимальное время этот коммит. ```js -console.log(intersectRanges('1-2; 4-6; 9-11', '1-5; 10-14; 15')); // 1-2; 4-5; 10-11 +const commits = ['good', 'good', 'good', 'bad', 'bad', 'bad', 'bad', 'bad', 'bad']; + +const test = (commit) => commit === 'good'; + +console.log(findFirstBadCommit(commits, test)); // 3 ``` -## Найти два элемента массива сумма которых дает заданное число +## Максимальная подстрока без повторений -Необходимо написать функцию, которая бы принимала массив чисел и число и возвращала бы индексы двух элементов массива сумма которых даёт заданное число. +Необходимо написать функцию, которая принимала бы строку и возвращала бы максимальную подстроку без повторяющихся символов. ```js -console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1] -console.log(twoSum([3, 2, 4], 6)); // [1, 2] +console.log(maxUniqueSubstr('aab')); // ab +console.log(maxUniqueSubstr('abcabcbb')); // abc +console.log(maxUniqueSubstr('bbbbb')); // b +console.log(maxUniqueSubstr('pwwkew')); // wke ``` -## Реализовать итератор на основе EventEmitter +## Генерация анаграмм -Необходимо написать функцию, которая бы создавала асинхронный итератор на основе заданного EventEmitter и события. +Необходимо создать функцию, которая бы принимала строку и возвращала список всех анаграмм этого слова. ```js -const ee = new EventEmitter(); - -(async () => { - for await (const e of stream(ee, 'foo')) { - console.log(e); // 1 2 3 - } -})(); - -ee.emit('foo', 1); -ee.emit('foo', 2); -ee.emit('foo', 3); -``` +console.log(getAnagram('cat')); // ['cta', 'atc', 'act', 'tca', 'tac'] +``` \ No newline at end of file diff --git "a/day_14/\320\227\320\260\320\264\320\260\321\207\320\270 (14).md" "b/day_14/\320\227\320\260\320\264\320\260\321\207\320\270 (14).md" index e8f11d5..723f51f 100644 --- "a/day_14/\320\227\320\260\320\264\320\260\321\207\320\270 (14).md" +++ "b/day_14/\320\227\320\260\320\264\320\260\321\207\320\270 (14).md" @@ -1,48 +1,61 @@ -# День 12 +# День 14 -## Определение палиндрома +## Сжатие глубокого объекта -Необходимо определить, является ли строка палиндромом или нет. +Необходимо написать функцию, которая бы сжимала некоторый глубокий объект в плоский вид. ```js -console.log(isPalindrome('bob')); // true -console.log(isPalindrome('abba')); // true -console.log(isPalindrome('a')); // false -console.log(isPalindrome('azt')); // false +const obj = { + a: { + b: [1, 2], + '': {c: 2} + } +}; + +/* {'a.b.0': 1, 'a.b.1': 2, 'a..c': 2} */ +console.log(collapse(obj)); ``` -## Извлечение подстрок в кавычках +## Валидация скобочных групп -Необходимо написать функцию, которая принимает строку и возвращает массив подстроку исходной. -Подстроки выбираются по признаку нахождения в тех или иных кавычках. +Необходимо написать функцию, которая бы принимала строку и возвращала true, если у каждого из символов `{`, `[` и `(` есть своя закрывающая пара и они стоят в правильной последовательности. ```js -console.log(extractQuotes('Это строка в "кавычках\'" и `"эта"` тоже, а это "хитрая строка\\""')); // ["кавычках'", '"эта"', 'хитрая строка\\"'] +console.log(isValid('(hello{world} and [me])')); // true +console.log(isValid('(hello{world)} and [me])')); // false +console.log(isValid(')')); // false ``` -## Реализовать zip для асинхронно итерируемых объектов +## Самый большой палиндром в строке -Необходимо реализовать функцию zip для множества асинхронно итерируемых объектов. +Необходимо написать функцию, которая бы принимала строку и возвращала подстроку с самым большим палиндромом в строке. ```js -async function* makeAsync(iter) { - yield* iter; -} - -// [1, 'a', '.'] [2, 'b', '.'] -(async () => { - for await (const el of zip(makeAsync(new Set([1, 2])), makeAsync(['a', 'b', 'z']), makeAsync('...'))) { - console.log(el); - } -})(); +console.log(findPalindromicSubstring('adaabbabla')); // 'abba' +console.log(findPalindromicSubstring('blablur')); // null ``` -## Вычислить разницу двух строк +## Реализация очереди с приоритетом -Необходимо написать функцию, которая бы вычисляла разницу между двумя заданными строками. -Разница вычисляется из суммы всех добавлений, удалений и замен символов так, чтобы строки стали равны. +Необходимо написать класс, который бы предоставлял API очереди, но с возможности задания функции-компаратора для сортировки на вставке. ```js -console.log(diff('bob', 'rob')); // 1 (одна замена) -console.log(diff('австрия', 'австралия')); // 2 (два удаления) +const + queue = new OrderedQueue((a, b) => a - b); + +queue.push(1); +queue.push(5); +queue.push(2); +queue.push(-1); +queue.push(5); +queue.push(2); +queue.push(-1); +queue.push(5); + +console.log(queue.pop()); // 5 +console.log(queue.pop()); // 5 + +console.log(queue.pop()); // 5 +console.log(queue.pop()); // 2 +console.log(queue.pop()); // 2 ``` diff --git "a/day_15/\320\227\320\260\320\264\320\260\321\207\320\270 (15).md" "b/day_15/\320\227\320\260\320\264\320\260\321\207\320\270 (15).md" deleted file mode 100644 index 8680005..0000000 --- "a/day_15/\320\227\320\260\320\264\320\260\321\207\320\270 (15).md" +++ /dev/null @@ -1,43 +0,0 @@ -# День 13 - -## Поиск подстроки в строке - -Необходимо написать функцию, которая принимала бы строку и подстроку и возвращала бы true, если заданная подстрока присутствует в строке. -Регулярные выражения использовать нельзя. - -```js -console.log(includes('hello bob!', 'bob')); // true -console.log(includes('abba', 'aba')); // false -``` - -## Ломающий коммит - -Имеется множество коммитов и функция проверки работы программы. На одном из коммитов программа начинает ломаться. -Необходимо выяснить за минимальное время этот коммит. - -```js -const commits = ['good', 'good', 'good', 'bad', 'bad', 'bad', 'bad', 'bad', 'bad']; - -const test = (commit) => commit === 'good'; - -console.log(findFirstBadCommit(commits, test)); // 3 -``` - -## Максимальная подстрока без повторений - -Необходимо написать функцию, которая принимала бы строку и возвращала бы максимальную подстроку без повторяющихся символов. - -```js -console.log(maxUniqueSubstr('aab')); // ab -console.log(maxUniqueSubstr('abcabcbb')); // abc -console.log(maxUniqueSubstr('bbbbb')); // b -console.log(maxUniqueSubstr('pwwkew')); // wke -``` - -## Генерация анаграмм - -Необходимо создать функцию, которая бы принимала строку и возвращала список всех анаграмм этого слова. - -```js -console.log(getAnagram('cat')); // ['cta', 'atc', 'act', 'tca', 'tac'] -``` \ No newline at end of file diff --git "a/day_16/\320\227\320\260\320\264\320\260\321\207\320\270 (16).md" "b/day_16/\320\227\320\260\320\264\320\260\321\207\320\270 (16).md" deleted file mode 100644 index 723f51f..0000000 --- "a/day_16/\320\227\320\260\320\264\320\260\321\207\320\270 (16).md" +++ /dev/null @@ -1,61 +0,0 @@ -# День 14 - -## Сжатие глубокого объекта - -Необходимо написать функцию, которая бы сжимала некоторый глубокий объект в плоский вид. - -```js -const obj = { - a: { - b: [1, 2], - '': {c: 2} - } -}; - -/* {'a.b.0': 1, 'a.b.1': 2, 'a..c': 2} */ -console.log(collapse(obj)); -``` - -## Валидация скобочных групп - -Необходимо написать функцию, которая бы принимала строку и возвращала true, если у каждого из символов `{`, `[` и `(` есть своя закрывающая пара и они стоят в правильной последовательности. - -```js -console.log(isValid('(hello{world} and [me])')); // true -console.log(isValid('(hello{world)} and [me])')); // false -console.log(isValid(')')); // false -``` - -## Самый большой палиндром в строке - -Необходимо написать функцию, которая бы принимала строку и возвращала подстроку с самым большим палиндромом в строке. - -```js -console.log(findPalindromicSubstring('adaabbabla')); // 'abba' -console.log(findPalindromicSubstring('blablur')); // null -``` - -## Реализация очереди с приоритетом - -Необходимо написать класс, который бы предоставлял API очереди, но с возможности задания функции-компаратора для сортировки на вставке. - -```js -const - queue = new OrderedQueue((a, b) => a - b); - -queue.push(1); -queue.push(5); -queue.push(2); -queue.push(-1); -queue.push(5); -queue.push(2); -queue.push(-1); -queue.push(5); - -console.log(queue.pop()); // 5 -console.log(queue.pop()); // 5 - -console.log(queue.pop()); // 5 -console.log(queue.pop()); // 2 -console.log(queue.pop()); // 2 -``` diff --git a/day_2/1.js b/day_2/1.js deleted file mode 100644 index f8fb104..0000000 --- a/day_2/1.js +++ /dev/null @@ -1,13 +0,0 @@ -var val = Promise.resolve(1); - -var arr = [1, 2, 3]; - -for (var i = 0; i < arr.length; ++i) { - (function(i) { - val = val.then((val) => val + arr[i]); - })(i) -} - -val.then(console.log); // NaN - -let o = 0 diff --git a/day_2/2.js b/day_2/2.js deleted file mode 100644 index 586a48a..0000000 --- a/day_2/2.js +++ /dev/null @@ -1,24 +0,0 @@ -const obj = {}; - -function setByPath(obj, keyString, value) { - if (!obj) return; - - const keys = keyString.split('.'); - - if (!keys) return; - - keys.forEach((key, index) => { - if (index === keys.length - 1) { - obj[key] = value; - } else { - obj[key] ??= {}; - obj = obj[key]; - } - }) -} - -setByPath(obj, 'foo.bar', 1); -setByPath(obj, 'foo.bla', 2); -setByPath(obj, 'foo.baz.bla', 3); - -console.log(obj); // {foo: {bar: day_1, bla: day_2}} \ No newline at end of file diff --git a/day_2/3.js b/day_2/3.js deleted file mode 100644 index 27cd817..0000000 --- a/day_2/3.js +++ /dev/null @@ -1,35 +0,0 @@ -const obj = { - value: 'foo', - children: [ - { - value: 'bar' - }, - - { - value: 'bla', - children: [{value: 'baz'}] - } - ] -}; - -console.log(maxDepth(obj)); // day_2 - -function maxDepth(root) { - let max = 0; - - function calculateMaxDepth(root, currentDepth = 0) { - root?.children?.forEach((node) => { - const depth = calculateMaxDepth(node, currentDepth + 1); - - if (depth > max) { - max = depth; - } - }); - - return currentDepth; - } - - calculateMaxDepth(root, 0); - - return max; -} \ No newline at end of file diff --git a/day_2/4.js b/day_2/4.js deleted file mode 100644 index 4290591..0000000 --- a/day_2/4.js +++ /dev/null @@ -1,27 +0,0 @@ -// 101 -// day_1 * day_2 ** day_2 + 0 * day_2 ** day_1 + day_1 * day_2 ** 0 - -const alphabet = new Map(); -const alphabets = { - 36: alphabet -}; - -for (let i = '0'.codePointAt(0), j = 0; i <= '9'.codePointAt(9); i + 1, j + 1) { - alphabet.set(String.fromCodePoint(i), j); -} - -for (let i = 'A'.codePointAt(0), j = 10; i <= 'Z'.codePointAt(0); i + 1, j + 1) { - alphabet.set(String.fromCodePoint(i), j); -} - - - - -function parseInt() { - -} - -console.log(myParseInt('10')); // 10 -console.log(myParseInt('-10', 2)); // -day_2 -console.log(myParseInt('FFP', 16)); // 255 -console.log(myParseInt('--20')); // NaN \ No newline at end of file diff --git a/day_2/mapSet.js b/day_2/mapSet.js deleted file mode 100644 index f4dbdd6..0000000 --- a/day_2/mapSet.js +++ /dev/null @@ -1,30 +0,0 @@ -function unique(arr) { - return [...new Set(arr).values()].join(','); -} - -let values = ["Hare", "Krishna", "Hare", "Krishna", - "Krishna", "Krishna", "Hare", "Hare", ":-O" -]; - -// console.log( unique(values) ); // Hare,Krishna,:-O - -let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"]; - -function aclean(arr) { - const map = new Map(); - - arr.toReversed().forEach((el) => { - const sortedElement = el - .toLowerCase() - .split('') - .sort() - .join(''); - - map.set(sortedElement, el); - } - ); - - return [...map.values()].join(','); -} - -console.log(aclean(arr)); // "nap,teachers,ear" или "PAN,cheaters,era" \ No newline at end of file diff --git "a/day_3/\320\227\320\260\320\264\320\260\321\207\320\270 (3).md" "b/day_3/\320\227\320\260\320\264\320\260\321\207\320\270 (3).md" new file mode 100644 index 0000000..944fad1 --- /dev/null +++ "b/day_3/\320\227\320\260\320\264\320\260\321\207\320\270 (3).md" @@ -0,0 +1,66 @@ +# День 3 + +## Что и в каком порядке выведется в консоль? + +И почему? + +```js +console.log('foo'); + +setTimeout(() => { + console.log('bar'); +}, 0); + +queueMicrotask(() => { + console.log('baz'); + Promise.resolve().then().then(() => console.log('ban')); +}); + +new Promise((resolve) => { + console.log('bla'); + resolve('baf'); +}).then(console.log); + +console.log('bak'); +``` + +## Реализовать функцию setImmediate + +Необходимо функцию, которая бы предоставляла API схожее с setTimeout, но создавала бы микротаску. + +```js +setTimeout(() => { + console.log(3); +}, 0); + +setImmediate(() => { + console.log(1); +}); + +const timer = setImmediate(() => { + console.log(2); +}); + +clearImmediate(timer); +``` + +## Реализация функции каррирования + +Необходимо написать функцию, которая бы принимала другую функцию и возвращала её каррированную версию. + +```js +const sum = curry((a, b, c, z) => a + b + c + z); + +console.log(sum(1)(2)(3)(4)); // 10; +console.log(sum(1)(2)(3, 4)); // 10; +console.log(sum(1)(2, 3, 4)); // 10; +``` + +## Реализовать zip для синхронных Iterable объектов + +Общее количество кортежей берется по минимальному значению. +Функция должна возвращать IterableIterator. + +```js +console.log(...zip(new Set([1, 2]), ['a', 'b', 'z'], '...')); // [day_1, 'a', '.'] [day_2, 'b', '.'] +``` diff --git a/day_4/1.js b/day_4/1.js deleted file mode 100644 index 15c95fc..0000000 --- a/day_4/1.js +++ /dev/null @@ -1,19 +0,0 @@ -console.log('foo'); - -setTimeout(() => { - console.log('bar'); -}, 0); - -queueMicrotask(() => { - console.log('baz'); - Promise.resolve().then().then(() => console.log('ban')); -}); - -new Promise((resolve) => { - console.log('bla'); - resolve('baf'); -}).then(console.log); - -console.log('bak'); - -// foo bla bak baz baf ban bar \ No newline at end of file diff --git a/day_4/2.js b/day_4/2.js deleted file mode 100644 index 888211b..0000000 --- a/day_4/2.js +++ /dev/null @@ -1,34 +0,0 @@ -setTimeout(() => { - console.log(3); -}, 0); - -const setImmediateTimers= new Map(); - -function setImmediate(cb) { - const timer = setImmediateTimers.size; - setImmediateTimers.set(timer, true); - - queueMicrotask(() => { - if (setImmediateTimers.get(timer) === false) { - return; - } - - cb(); - }); - - return timer; -} - -function clearImmediate(timer) { - setImmediateTimers.set(timer, false); -} - -setImmediate(() => { - console.log(1); -}); - -const timer = setImmediate(() => { - console.log(2); -}); - -clearImmediate(timer); \ No newline at end of file diff --git a/day_4/3.js b/day_4/3.js deleted file mode 100644 index 625deae..0000000 --- a/day_4/3.js +++ /dev/null @@ -1,45 +0,0 @@ -// const sum = curry((a, b, c, z) => a + b + c + z); -// -// console.log(sum(1)(2)(3)(4)); // 10; -// console.log(sum(1)(2)(3, 4)); // 10; -// console.log(sum(1)(2, 3, 4)); // 10; - -function curry(fn) { - return function curriedWrapper(...args) { - if (args.length >= fn.length) { - return fn.apply(this, args); - } - - return function (...args2) { - return curriedWrapper.apply(this, args.concat(args2)); - } - } -} - -function myCurry(fn) { - return function wrapper(...args) { - if (args.length >= fn.length) { - return fn.apply(this, args); - } - - return function (...args2) { - return wrapper.apply(this, [...args, ...args2]); - } - } -} - -function twoSum(array, sum) { - const set = new Set(array); - - array.forEach((number) => { - const candidate = sum - number; - - if (set.has(candidate)) { - return [number, candidate]; - } - }); - - return []; -} - -console.log(twoSum([3,5,-4,8,11,1,-1,6], 10)); \ No newline at end of file diff --git a/day_4/4.js b/day_4/4.js deleted file mode 100644 index 0198dfc..0000000 --- a/day_4/4.js +++ /dev/null @@ -1,35 +0,0 @@ -console.log(...zip(new Set([1, 2]), ['a', 'b', 'z'], '...')); // [day_1, 'a', '.'] [day_2, 'b', '.'] - -function zip(...iterables) { - const iters = [...iterables].map((iter) => iter[Symbol.iterator]()); - - let - done = false; - - return { - [Symbol.iterator]() { - return this; - }, - - next() { - if (done) { - return {done, value: undefined}; - } - - const value = new Array(iters.length); - - for (const [i, iter] of iters.entries()) { - const current = iter.next(); - - if (current.done) { - done = true; - return {done, value: undefined}; - } - - value[i] = current.value; - } - - return {done: false, value}; - } - } -} \ No newline at end of file diff --git "a/day_4/\320\227\320\260\320\264\320\260\321\207\320\270 (4).md" "b/day_4/\320\227\320\260\320\264\320\260\321\207\320\270 (4).md" index 944fad1..d19476f 100644 --- "a/day_4/\320\227\320\260\320\264\320\260\321\207\320\270 (4).md" +++ "b/day_4/\320\227\320\260\320\264\320\260\321\207\320\270 (4).md" @@ -1,66 +1,59 @@ -# День 3 +# День 4 -## Что и в каком порядке выведется в консоль? +## Реализация функции promisify -И почему? +Необходимо написать функцию, которая бы создавал функции с Promise API на основе функций с callback API. +Формат callback функции ожидается в стиле Node.js, где первый аргумент - это объект ошибки. ```js -console.log('foo'); - -setTimeout(() => { - console.log('bar'); -}, 0); - -queueMicrotask(() => { - console.log('baz'); - Promise.resolve().then().then(() => console.log('ban')); -}); - -new Promise((resolve) => { - console.log('bla'); - resolve('baf'); -}).then(console.log); - -console.log('bak'); +function cbDiv(a, b, cb) { + if (b === 0) { + cb(new TypeError('Нельзя делить на 0')); + + } else { + cb(null, a / b); + } +} + +const promiseDiv = promisify(cbDiv); + +promiseDiv(1, 2).then(console.log); // 0.day_5 +promiseDiv(1, 0).catch(console.log); // TypeError('Нельзя делить на 0') ``` -## Реализовать функцию setImmediate +## Написать класс числа с рекурсивным API для арифметических операций -Необходимо функцию, которая бы предоставляла API схожее с setTimeout, но создавала бы микротаску. +Необходимо проанализировать пример ниже и реализовать нужный API, где -```js -setTimeout(() => { - console.log(3); -}, 0); - -setImmediate(() => { - console.log(1); -}); +1. add - сложение +2. sub - вычитание +3. mult - умножение +4. div - деление -const timer = setImmediate(() => { - console.log(2); -}); +```js +const num = new MyNumber(10); -clearImmediate(timer); +console.log(num.add(2).mult(2).sub(1) - 5); // 18 ``` -## Реализация функции каррирования +## Выборочная сортировка массива -Необходимо написать функцию, которая бы принимала другую функцию и возвращала её каррированную версию. +Необходимо написать функцию sort, которая бы сортировала элементы со значением кратным двум. ```js -const sum = curry((a, b, c, z) => a + b + c + z); - -console.log(sum(1)(2)(3)(4)); // 10; -console.log(sum(1)(2)(3, 4)); // 10; -console.log(sum(1)(2, 3, 4)); // 10; +console.log(sort([7, 1, 4, 2, 9, 8])); // [7, day_1, day_2, day_4, 9, 8] ``` -## Реализовать zip для синхронных Iterable объектов +## Поиск в массиве строк по заданной подстроке с пропуском символов -Общее количество кортежей берется по минимальному значению. -Функция должна возвращать IterableIterator. +Необходимо написать функцию, которая бы принимала массив строк и строку и возвращала бы новый массив, +состоящий только из элементов с содержанием заданной подстроки. Алгоритм должен учитывать, что подстрока может быть найдена в строке при помощи пропуска части символов в строке (нечеткий поиск). ```js -console.log(...zip(new Set([1, 2]), ['a', 'b', 'z'], '...')); // [day_1, 'a', '.'] [day_2, 'b', '.'] +console.log(find('kbza', [ + 'kobezzza', + 'bob', + 'kibiza', + 'kobea' +])); // ['kobezzza', 'kibiza'] ``` diff --git a/day_5/1.js b/day_5/1.js deleted file mode 100644 index 06d5fe1..0000000 --- a/day_5/1.js +++ /dev/null @@ -1,31 +0,0 @@ -function cbDiv(a, b, cb) { - if (b === 0) { - cb(new TypeError('Cannot devide by 0')); - - } else { - cb(null, a / b); - } -} - -function promisify(fn) { - return function(...args) { - return new Promise((resolve, reject) => { - if (args.length !== fn.length - 1) { - throw new Error('The fn arguments number is wrong!'); - } - - fn(...args, (err, data) => { - if (err) { - reject(err); - } - - resolve(data); - }) - }); - } -} - -const promiseDiv = promisify(cbDiv); - -promiseDiv(1, 2).then(console.log); // 0.day_5 -promiseDiv(1, 0).catch(console.log); // TypeError('Нельзя делить на 0') \ No newline at end of file diff --git a/day_5/2.js b/day_5/2.js deleted file mode 100644 index 89a2382..0000000 --- a/day_5/2.js +++ /dev/null @@ -1,34 +0,0 @@ -class MyNumber { - constructor(num = 0) { - this.num = num; - } - - add(int) { - this.num += int; - return this; - } - - sub(int) { - this.num -= int; - return this; - } - - mult(int) { - this.num *= int; - return this; - } - - div(int) { - this.num /= int; - return this; - } - - [Symbol.toPrimitive](hint) { - return hint === "number" ? this.num : `${this.num}`; - } -} - -const num = new MyNumber(10); -console.log(num) - -console.log(num.add(2).mult(2).sub(1) - 5); // 18 \ No newline at end of file diff --git a/day_5/3.js b/day_5/3.js deleted file mode 100644 index bd51d17..0000000 --- a/day_5/3.js +++ /dev/null @@ -1,24 +0,0 @@ -console.log(sort([7, 1, 4, 2, 9, 8])); // [7, day_1, day_2, day_4, 9, 8] - -function sort(arr) { - if (!arr || !Array.isArray(arr)) throw new Error('expect type Array as an argument'); - if (!arr.length) return []; - - const positions = []; - const values = []; - - arr.forEach((el, i) => { - if (el % 2 === 0) { - positions.push(i); - values.push(el); - } - }); - - values.sort((a, b) => a - b); - - values.forEach((el, i) => { - arr[positions[i]] = el; - }) - - return arr; -} \ No newline at end of file diff --git a/day_5/4.js b/day_5/4.js deleted file mode 100644 index d12f6b2..0000000 --- a/day_5/4.js +++ /dev/null @@ -1,20 +0,0 @@ -console.log(find('kbza', [ - 'kobezzza', - 'bob', - 'kibiza', - 'kobea', -])); // ['kobezzza', 'kibiza'] - -function find(input, arr) { - const inputChars = input.split(''); - - return arr.reduce((acc,str) => { - const charSet = new Set(str.split('')); - - if (inputChars.every((char) => charSet.has(char))) { - acc.push(str); - } - - return acc; - },[]); -} \ No newline at end of file diff --git "a/day_6/\320\227\320\260\320\264\320\260\321\207\320\270.md" "b/day_5/\320\227\320\260\320\264\320\260\321\207\320\270 (5).md" similarity index 100% rename from "day_6/\320\227\320\260\320\264\320\260\321\207\320\270.md" rename to "day_5/\320\227\320\260\320\264\320\260\321\207\320\270 (5).md" diff --git "a/day_5/\320\227\320\260\320\264\320\260\321\207\320\270.md" "b/day_5/\320\227\320\260\320\264\320\260\321\207\320\270.md" deleted file mode 100644 index d19476f..0000000 --- "a/day_5/\320\227\320\260\320\264\320\260\321\207\320\270.md" +++ /dev/null @@ -1,59 +0,0 @@ -# День 4 - -## Реализация функции promisify - -Необходимо написать функцию, которая бы создавал функции с Promise API на основе функций с callback API. -Формат callback функции ожидается в стиле Node.js, где первый аргумент - это объект ошибки. - -```js -function cbDiv(a, b, cb) { - if (b === 0) { - cb(new TypeError('Нельзя делить на 0')); - - } else { - cb(null, a / b); - } -} - -const promiseDiv = promisify(cbDiv); - -promiseDiv(1, 2).then(console.log); // 0.day_5 -promiseDiv(1, 0).catch(console.log); // TypeError('Нельзя делить на 0') -``` - -## Написать класс числа с рекурсивным API для арифметических операций - -Необходимо проанализировать пример ниже и реализовать нужный API, где - -1. add - сложение -2. sub - вычитание -3. mult - умножение -4. div - деление - -```js -const num = new MyNumber(10); - -console.log(num.add(2).mult(2).sub(1) - 5); // 18 -``` - -## Выборочная сортировка массива - -Необходимо написать функцию sort, которая бы сортировала элементы со значением кратным двум. - -```js -console.log(sort([7, 1, 4, 2, 9, 8])); // [7, day_1, day_2, day_4, 9, 8] -``` - -## Поиск в массиве строк по заданной подстроке с пропуском символов - -Необходимо написать функцию, которая бы принимала массив строк и строку и возвращала бы новый массив, -состоящий только из элементов с содержанием заданной подстроки. Алгоритм должен учитывать, что подстрока может быть найдена в строке при помощи пропуска части символов в строке (нечеткий поиск). - -```js -console.log(find('kbza', [ - 'kobezzza', - 'bob', - 'kibiza', - 'kobea' -])); // ['kobezzza', 'kibiza'] -``` diff --git a/day_6/1.js b/day_6/1.js deleted file mode 100644 index e5b77b5..0000000 --- a/day_6/1.js +++ /dev/null @@ -1,40 +0,0 @@ -function laugh() { - console.log('Ha-ha!') -} - -const debouncedLaugh = debounce(laugh, 300); - -debouncedLaugh(); -debouncedLaugh(); -debouncedLaugh(); -debouncedLaugh(); -debouncedLaugh(); - -function debounce(fn, delay) { - let timerId; - - return function (...args) { - if (timerId != null) { - clearTimeout(timerId); - } - - timerId = setTimeout(() => { - fn.apply(this, args); - }, delay); - } -} - -function throttle(fn, delay) { - let isRunning; - - return function(...args) { - if (!isRunning) { - isRunning = true; - fn.apply(this, args); - - setTimeout(() => { - isRunning = false; - }, delay); - } - } -} \ No newline at end of file diff --git a/day_6/2.js b/day_6/2.js deleted file mode 100644 index 9f53df4..0000000 --- a/day_6/2.js +++ /dev/null @@ -1,68 +0,0 @@ - // Отмена всех обработчиков этого события - -class EventEmitter { - - #handlers = new Map(); - - on(event, cb) { - const store = this.#getHandlersStore(event); - store.add(cb); - - return cb; - } - - #getHandlersStore(event) { - let store = this.#handlers.get(event); - - if (store == null) { - store = new Set(); - this.#handlers.set(event, store); - } - - return store; - } - - once(event, cb) { - const wrapper = (...args) => { - try { - cb(...args); - } finally { - this.off(event, wrapper); - } - } - - return this.on(event, wrapper); - } - - emit(event, ...payload) { - const store = this.#getHandlersStore(event); - - store.forEach((cb) => cb(...payload)); - } - - off(event, cb) { - if (event == null) { - this.#handlers.clear(); - return; - } - - const store = this.#getHandlersStore(event); - - if (cb == null) { - store.clear(); - return; - } - - store.delete(cb); - } -} - -const ee = new EventEmitter(); - -ee.once('foo', console.log); // Сработает только один раз - -ee.emit('foo', 1); -ee.emit('foo', 2); - -ee.off('foo', console.log); // Отмена конкретного обработчика события по ссылке -ee.off('foo'); \ No newline at end of file diff --git a/day_6/3.js b/day_6/3.js deleted file mode 100644 index 61361c4..0000000 --- a/day_6/3.js +++ /dev/null @@ -1,43 +0,0 @@ -console.log(flat([[1, 2], [[1]], 2])); // [1, 2, [1], 2] -console.log(flat([[1, 2], [[1]], 2], 2)); // [1, 2, 1, 2] - -// function flat(arr, depth = 1) { -// const result = []; -// const wrapper = (arr, depth) => { -// arr.forEach((el) => { -// if (Array.isArray(el) && depth > 0) { -// return wrapper(el, depth - 1); -// } -// -// result.push(el); -// }); -// } -// -// wrapper(arr, depth); -// -// return result; -// } - -function flat(arr, depth = 1) { - const result = []; - - const stack = [ - [depth, arr[Symbol.iterator]()] - ]; - - while (stack.length) { - const [depth, iter] = stack.pop(); - - for (const el of iter) { - if (!Array.isArray(el) || depth <= 0) { - result.push(el); - } else { - stack.push([depth, iter]); - stack.push([depth - 1, el[Symbol.iterator]()]); - break; - } - } - } - - return result; -} \ No newline at end of file diff --git a/day_6/4.js b/day_6/4.js deleted file mode 100644 index 189b969..0000000 --- a/day_6/4.js +++ /dev/null @@ -1,90 +0,0 @@ -const skills = [ - { - name: 'fireball', - need: ['firehands', 'magicspell'] - }, - - { - name: 'firehands' - }, - - { - name: 'magicspell' - }, - - { - name: 'inferno', - need: ['fireball', 'crazymind'] - }, - - { - name: 'crazymind', - need: ['magicspell'] - } -]; - -/* -[ - { - name: 'firehands' - }, - - { - name: 'magicspell' - }, - - { - name: 'crazymind', - need: ['magicspell'] - } - - { - name: 'fireball', - need: ['firehands', 'magicspell'] - }, - - { - name: 'inferno', - need: ['fireball', 'crazymind'] - } -] -*/ -console.log(sort(skills, ({name, need}) => [name, need])); - -function sort(skills, fn) { - const map = new Map(skills.map((value) => { - const [key, dependencies = []] = fn(value); - - return [ - key, - { - value, dependencies, marked: false - } - ] - })); - - const queue = []; - - function traverse(mapInstance) { - mapInstance.forEach((el) => { - if (el.marked) { - return; - } - - if (el.dependencies.length) { - traverse(new Map(el.dependencies.map((key) => [key, map.get(key)]))); - } - - el.marked = true; - queue.push(el.value); - }) - } - - traverse(map); - - skills.forEach((_, i) => { - skills[i] = queue[i]; - }); - - return skills; -} \ No newline at end of file diff --git "a/day_7/\320\227\320\260\320\264\320\260\321\207\320\270.md" "b/day_6/\320\227\320\260\320\264\320\260\321\207\320\270 (6).md" similarity index 100% rename from "day_7/\320\227\320\260\320\264\320\260\321\207\320\270.md" rename to "day_6/\320\227\320\260\320\264\320\260\321\207\320\270 (6).md" diff --git a/day_7/1.js b/day_7/1.js deleted file mode 100644 index ab49f6e..0000000 --- a/day_7/1.js +++ /dev/null @@ -1,28 +0,0 @@ -class User { - constructor(params) { - this.name = params.name; - this.age = params.age; - this.skills = params.skills; - } - - static name(name) { - this.name = name; - return this; - } - - static age(age) { - this.age = age; - return this; - } - - static skills(skills) { - this.skills = skills; - return this; - } - - static create() { - return new this({name: this.name, age: this.age, skills: this.skills}); - } -} - -console.log(User.name('Bob').age(47).skills(['Coding']).create()); // User({name: 'Bob', age: 47, skills: ['Coding']}) diff --git a/day_7/2.js b/day_7/2.js deleted file mode 100644 index 33db5f7..0000000 --- a/day_7/2.js +++ /dev/null @@ -1,26 +0,0 @@ -const tree = { - value: 1, - children: [ - { - value: 2, - children: [{value: 4}] - }, - { - value: 3 - } - ] -}; - -console.log(log(tree)); // 1 2 3 4 - -// tree width traverse -function log(tree) { - const queue = [tree]; - - while (queue.length) { - const head = queue.shift(); - console.log(head.value); - - head.children?.forEach((child) => queue.push(child)); - } -} \ No newline at end of file diff --git a/day_7/3.js b/day_7/3.js deleted file mode 100644 index 3043057..0000000 --- a/day_7/3.js +++ /dev/null @@ -1,40 +0,0 @@ -console.log(dasherize('createDocumentFragment')); // 'create-document-fragment' -console.log(dasherize('SuperMAN')); // 'super-man' -console.log(dasherize('VirtualDOMFragment')); // 'virtual-dom-fragment' -console.log(dasherize('VirtualDOM123Fragment')); // 'virtual-dom-fragment' - - -function dasherize(str) { - const isUpperCase = (char) => char === char.toUpperCase(); - const queue = []; - - return str.split('').reduce((result, char, index, array) => { - if (index === 0) { - result += char.toLowerCase(); - return result; - } - - if (isUpperCase(char)) { - if (!queue.length && !isUpperCase(array[index + 1])) { - result += `-${char.toLowerCase()}`; - return result; - } - - if (array[index + 1] === undefined) { - result += `-${queue.join('')}${char.toLowerCase()}` - return result; - } - - if (queue.length && !isUpperCase(array[index + 1])) { - result += `-${queue.join('')}-${char.toLowerCase()}` - return result; - } - - queue.push(char.toLowerCase()); - return result; - } - - result += char; - return result; - }, ''); -} \ No newline at end of file diff --git "a/day_8/\320\227\320\260\320\264\320\260\321\207\320\270.md" "b/day_7/\320\227\320\260\320\264\320\260\321\207\320\270 (7).md" similarity index 100% rename from "day_8/\320\227\320\260\320\264\320\260\321\207\320\270.md" rename to "day_7/\320\227\320\260\320\264\320\260\321\207\320\270 (7).md" diff --git a/day_8/0.js b/day_8/0.js deleted file mode 100644 index 25e8d43..0000000 --- a/day_8/0.js +++ /dev/null @@ -1,156 +0,0 @@ -// allSettled([1, Promise.resolve(2), Promise.reject(3)]).then(([v1, v2, v3]) => { -// console.log(v1); // {status: 'fulfilled', value: day_1} -// console.log(v2); // {status: 'fulfilled', value: day_2} -// console.log(v3); // {status: 'rejected', reason: 3} -// }); - -// const promise1 = Promise.resolve(3); -// const promise2 = 42; -// const promise3 = new Promise((resolve, reject) => { -// setTimeout(resolve, 100, 'foo'); -// }); -// -// promiseAll([promise1, promise2, promise3]).then((values) => { -// console.log(values); -// }); -// Expected output: Array [3, 42, "foo"] - -// const promise1 = Promise.reject(0); -// const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'quick')); -// const promise3 = new Promise((resolve) => setTimeout(resolve, 500, 'slow')); -// -// const promises = [promise1, promise2, promise3]; -// -// promiseAny(promises).then((value) => console.log(value)); - -// Expected output: "quick" - -// const promise1 = new Promise((resolve, reject) => { -// setTimeout(resolve, 500, 'one'); -// }); -// -// const promise2 = new Promise((resolve, reject) => { -// setTimeout(resolve, 100, 'two'); -// }); -// -// promiseRace([promise1, promise2]).then((value) => { -// console.log(value); -// // Both resolve, but promise2 is faster -// }); -// // Expected output: "two" - - -function promiseAll(promises) { - const result = new Array(promises.length); - let resolvedPromisesCount = 0; - - return new Promise((resolve, reject) => { - if (!promises || !Array.isArray(promises) || !promises?.length) { - resolve(result); - } - - promises.forEach(async (promise, index) => { - try { - const res = await Promise.resolve(promise); - result[index] = res; - resolvedPromisesCount += 1; - - if (resolvedPromisesCount === promises.length) { - resolve(result); - } - } catch (e) { - reject(e); - } - }); - }); -} - -function allSettled(promises) { - const result = new Array(promises.length); - let total = 0; - - - return new Promise((resolve) => { - promises.forEach((promise, index) => { - Promise.resolve(promise) - .then((value) => { - result[index] = {status: 'fulfilled', value} - total += 1; - - if (total === promises.length) { - resolve(result); - } - }) - .catch((reason) => { - result[index] = {status: 'rejected', reason} - total += 1; - - if (total === promises.length) { - resolve(result); - } - }) - }); - }); -} - -function promiseAny(iters) { - const promises = [...iters].map((iter) => Promise.resolve(iter)); - const rejectsArray = new Array(promises.length); - let totalRejectedCount = 0; - - return new Promise((resolve, reject) => { - if (!promises.length) { - reject(rejectsArray); - } - - promises.forEach((promise, index) => { - promise - .then((value) => resolve(value)) - .catch((reason) => { - rejectsArray[index] = reason; - totalRejectedCount += 1; - - if (totalRejectedCount === promises.length) { - reject(rejectsArray) - } - }) - }); - }); -} - -function promiseRace(iters) { - const promises = [...iters].map((iter) => Promise.resolve(iter)); - - return new Promise((resolve, reject) => { - promises.forEach((promise) => { - promise.then((value) => resolve(value)); - }) - }); -} - -console.log(checkBrackets('[')) // false -console.log(checkBrackets('((()))[]{}')) // true -console.log(checkBrackets('([()])[]()')) // true -console.log(checkBrackets('([()][]()')) // false - -function checkBrackets(str) { - const map = { - '}': '{', - ')': '(', - ']': '[' - }; - - const stack = []; - - str.split('').forEach((char) => { - if (stack.length === 0) { - stack.push(char); - } else if (stack.at(-1) === map[char]) { - stack.pop(); - } else { - stack.push(char); - } - }); - - return stack.length === 0; -} \ No newline at end of file diff --git a/day_8/1.js b/day_8/1.js deleted file mode 100644 index 7074c92..0000000 --- a/day_8/1.js +++ /dev/null @@ -1,7 +0,0 @@ -console.log(diff([1, 2, 3, 4, 5], [3, 4, 1])); // [2, 5] - -function diff(firstArray, secondArray) { - const set = new Set(secondArray); - - return firstArray.filter((el) => !set.has(el)); -} \ No newline at end of file diff --git a/day_8/2.js b/day_8/2.js deleted file mode 100644 index 0c540d9..0000000 --- a/day_8/2.js +++ /dev/null @@ -1,25 +0,0 @@ -const semaphore = createsAsyncSemaphore(() => { - console.log('Boom!'); -}, 'foo', 'bar'); - -semaphore('foo'); -semaphore('bar'); // 'Boom!' - -// will not invoke anymore -semaphore(); - -function createsAsyncSemaphore(cb, ...flags) { - const conditions = new Set(flags); - let isInvoked = false; - - return function (flag) { - conditions.delete(flag); - - if (isInvoked || conditions.size) { - return; - } - - isInvoked = true; - cb.apply(this); - } -} \ No newline at end of file diff --git a/day_8/3.js b/day_8/3.js deleted file mode 100644 index f4bdb57..0000000 --- a/day_8/3.js +++ /dev/null @@ -1,50 +0,0 @@ -const original = { - myDate: new Date(), - mySet: new Set([1, 2, 3]), - myMap: new Map([ - [new Date(), {a: 1}] - ]) -}; - -const str = serialize(original); - -// Объект должен иметь аналогичную структуру с original -parse(str); - -function serialize(obj) { - const toJSON = Date.prototype.toJSON; - delete Date.prototype.toJSON; - - try { - return JSON.stringify(obj, (key, value) => { - if (value instanceof Date) { - return `[[DATA]]:Date;${value.valueOf()}`; - } - - if (value instanceof Set || value instanceof Set) { - return `[[DATA]]:${value.constructor.name};${serialize([...value])}`; - } - - return value; - }) - } finally { - Object.defineProperty(Date.prototype, 'toJSON', { - configurable: true, - enumerable: false, - writable: true, - value: toJSON, - }) - } -} - -function parse(str) { - return JSON.parse(str, (key, value) => { - if (typeof value === 'string' && value.startsWith('[[DATA]]')) { - const [_, type, data] = /^\[\[DATA]]:(.*?);(.*)/.exec(value); - - return Function('data', `return new ${type}(data)`)(parse(data)); - } - - return value; - }); -} \ No newline at end of file diff --git "a/day_9/\320\227\320\260\320\264\320\260\321\207\320\270 (10).md" "b/day_8/\320\227\320\260\320\264\320\260\321\207\320\270 (8).md" similarity index 100% rename from "day_9/\320\227\320\260\320\264\320\260\321\207\320\270 (10).md" rename to "day_8/\320\227\320\260\320\264\320\260\321\207\320\270 (8).md" diff --git a/day_9/1.js b/day_9/1.js deleted file mode 100644 index 00715f0..0000000 --- a/day_9/1.js +++ /dev/null @@ -1,25 +0,0 @@ -function throttle(cb, delay) { - let timer; - - return function (...args) { - if (!timer) { - cb.apply(this, args); - - timer = setTimeout(() => { - timer = undefined; - }, delay); - } - } -} - -function laugh() { - console.log('Ha-ha!') -} - -const throttledLaugh = throttle(laugh, 300); - -throttledLaugh(); -throttledLaugh(); -throttledLaugh(); -throttledLaugh(); -setTimeout(() => throttledLaugh(), 500); \ No newline at end of file diff --git a/day_9/2.js b/day_9/2.js deleted file mode 100644 index e98a151..0000000 --- a/day_9/2.js +++ /dev/null @@ -1,52 +0,0 @@ -console.log(compare({a: 1, b: [1, 2, 3]}, {a: 1, b: [1, 2, 3]})); // true -console.log(compare({a: 1, b: [1, 2]}, {a: 1, b: [1, 2, 3]})); // false - -function compare(a, b) { - function isDeepComparable(value) { - return value?.constructor == null || value?.constructor === Object || Array.isArray(value); - } - - function sort([aKey], [bKey]) { - return aKey.localeCompare(bKey); - } - - if (!isDeepComparable(a) || !isDeepComparable(b)) { - return a === b; - } - - if (a.constructor !== b.constructor) { - return false; - } - - if (Array.isArray(a)) { - if (a.length !== b.length) { - return false; - } - - for (let i = 0; i < a.length; i + 1) { - if (!compare(a[i], b[i])) { - return false; - } - } - - return true; - } - - const aEntries = Object.entries(a); - const bEntries = Object.entries(b); - - if (aEntries.length !== bEntries.length) { - return false; - } - - aEntries.sort(sort); - bEntries.sort(sort); - - for (let i = 0; i < a.length; i + 1) { - if (!compare(aEntries[i], bEntries[i])) { - return false; - } - } - - return true; -} \ No newline at end of file diff --git a/day_9/3.js b/day_9/3.js deleted file mode 100644 index 3a464f5..0000000 --- a/day_9/3.js +++ /dev/null @@ -1,23 +0,0 @@ -console.log(getUniqueStrs(['atoe', 'otea', 'ben', 'enb', 'baz', 'foo'])); // ['baz', 'foo'] - -function getUniqueStrs(arr) { - const map = new Map(); - - arr.forEach((word) => { - const normalizedStr = word.split('').sort((a, b) => a.localeCompare(b)).join(''); - - if (map.has(normalizedStr)) { - map.get(normalizedStr).count++; - } else { - map.set(normalizedStr, {count: 1, word}) - } - }); - - return [...map.values()].reduce((acc, {count, word}) => { - if (count === 1) { - acc.push(word); - } - - return acc; - }, []) -} \ No newline at end of file diff --git a/day_9/4.js b/day_9/4.js deleted file mode 100644 index 2743da0..0000000 --- a/day_9/4.js +++ /dev/null @@ -1,19 +0,0 @@ -const queue = new Queue(); - -queue.push(1); -queue.push(2); -queue.push(3); - -console.log(queue.pop()); // 1 -console.log(queue.pop()); // 2 -console.log(queue.pop()); // 3 -console.log(queue.pop()); // undefined - - -class Queue { - head = {value: undefined, next: null}; - - push(value) { - - } -} \ No newline at end of file diff --git "a/day_9/\320\227\320\260\320\264\320\260\321\207\320\270 (9).md" "b/day_9/\320\227\320\260\320\264\320\260\321\207\320\270 (9).md" new file mode 100644 index 0000000..f4da58f --- /dev/null +++ "b/day_9/\320\227\320\260\320\264\320\260\321\207\320\270 (9).md" @@ -0,0 +1,46 @@ +# День 9 + +## Реализовать функция бинарного поиска с callback + +Необходимо написать функцию, которая бы принимала отсортированный массив и функцию-компаратор и возвращала бы индекс найденного элемента (если компаратор вернул 0). +Для поиска должен использоваться алгоритм бинарного поиска. + +```js +console.log(bisect([1, 2, 3, 4, 5, 6, 7], (val) => 4 - val)); // 3 +console.log(bisect([1, 2, 3, 4, 5, 6, 7], (val) => 234 - val)); // -1 +``` + +## Реализовать функцию для амортизируемого повторного запроса + +Необходимо написать функцию retry, которая бы принимала бы функцию, возвращающую Promise и объект настроек для указания повторений в случае ошибки. +Параметр `retry` задает максимальное количество повторения, а `delay` - задержку в мс между повторами (задается в виде функции, которая принимает номер попытки). + +```js +retry(() => fetch('//some-data'), {retry: 3, delay: (n) => n * 1000}).then(console.log, console.error); +``` + +## Реализовать класс LRU кеша + +Необходимо реализовать класс кеширования с алгоритмом LRU. + +```js +const cache = new LRUCache(3); // Размер кеша + +cache.set('key1', 1); +cache.set('key2', 2); +cache.set('key3', 3); + +console.log(cache.get('key1')); // 1 + +cache.set('key4', 4); + +console.log(cache.has('key2')); // false +``` + +## Сжатие строки + +Необходимо написать функцию, которая бы принимала бы строку и "схлопывала" бы все подряд идущие повторения. + +```js +console.log(zipStr('abbaabbafffbezza')); // abafbeza +``` From 0377fc655f8efa892eba4c18eb60016a33f6dbae Mon Sep 17 00:00:00 2001 From: Eugene Maslovich Date: Tue, 29 Jul 2025 15:06:21 +0000 Subject: [PATCH 02/52] Remove outdated task descriptions for days 4 to 14 and add new tasks for days 1 to 12, including various programming challenges such as implementing debounce, throttle, LRU cache, and more. --- README.md | 17 +++++++++++++++++ .../day_1.md | 0 .../day_10.md | 0 .../day_11.md | 0 .../day_12.md | 0 .../day_13.md | 0 .../day_14.md | 0 .../day_2.md | 0 .../day_3.md | 0 .../day_4.md | 0 .../day_5.md | 0 .../day_6.md | 0 .../day_7.md | 0 .../day_8.md | 0 .../day_9.md | 0 15 files changed, 17 insertions(+) rename "day_1/\320\227\320\260\320\264\320\260\321\207\320\270 (1).md" => day_1/day_1.md (100%) rename "day_10/\320\227\320\260\320\264\320\260\321\207\320\270 (10).md" => day_10/day_10.md (100%) rename "day_11/\320\227\320\260\320\264\320\260\321\207\320\270 (11).md" => day_11/day_11.md (100%) rename "day_12/\320\227\320\260\320\264\320\260\321\207\320\270 (12).md" => day_12/day_12.md (100%) rename "day_13/\320\227\320\260\320\264\320\260\321\207\320\270 (13).md" => day_13/day_13.md (100%) rename "day_14/\320\227\320\260\320\264\320\260\321\207\320\270 (14).md" => day_14/day_14.md (100%) rename "day_2/\320\227\320\260\320\264\320\260\321\207\320\270 (2).md" => day_2/day_2.md (100%) rename "day_3/\320\227\320\260\320\264\320\260\321\207\320\270 (3).md" => day_3/day_3.md (100%) rename "day_4/\320\227\320\260\320\264\320\260\321\207\320\270 (4).md" => day_4/day_4.md (100%) rename "day_5/\320\227\320\260\320\264\320\260\321\207\320\270 (5).md" => day_5/day_5.md (100%) rename "day_6/\320\227\320\260\320\264\320\260\321\207\320\270 (6).md" => day_6/day_6.md (100%) rename "day_7/\320\227\320\260\320\264\320\260\321\207\320\270 (7).md" => day_7/day_7.md (100%) rename "day_8/\320\227\320\260\320\264\320\260\321\207\320\270 (8).md" => day_8/day_8.md (100%) rename "day_9/\320\227\320\260\320\264\320\260\321\207\320\270 (9).md" => day_9/day_9.md (100%) diff --git a/README.md b/README.md index 3d2c41c..25fc9a8 100644 --- a/README.md +++ b/README.md @@ -1 +1,18 @@ # code_interview_prep + +## Days + +- [Day 1](day_1/day_1.md) +- [Day 2](day_2/day_2.md) +- [Day 3](day_3/day_3.md) +- [Day 4](day_4/day_4.md) +- [Day 5](day_5/day_5.md) +- [Day 6](day_6/day_6.md) +- [Day 7](day_7/day_7.md) +- [Day 8](day_8/day_8.md) +- [Day 9](day_9/day_9.md) +- [Day 10](day_10/day_10.md) +- [Day 11](day_11/day_11.md) +- [Day 12](day_12/day_12.md) +- [Day 13](day_13/day_13.md) +- [Day 14](day_14/day_14.md) diff --git "a/day_1/\320\227\320\260\320\264\320\260\321\207\320\270 (1).md" b/day_1/day_1.md similarity index 100% rename from "day_1/\320\227\320\260\320\264\320\260\321\207\320\270 (1).md" rename to day_1/day_1.md diff --git "a/day_10/\320\227\320\260\320\264\320\260\321\207\320\270 (10).md" b/day_10/day_10.md similarity index 100% rename from "day_10/\320\227\320\260\320\264\320\260\321\207\320\270 (10).md" rename to day_10/day_10.md diff --git "a/day_11/\320\227\320\260\320\264\320\260\321\207\320\270 (11).md" b/day_11/day_11.md similarity index 100% rename from "day_11/\320\227\320\260\320\264\320\260\321\207\320\270 (11).md" rename to day_11/day_11.md diff --git "a/day_12/\320\227\320\260\320\264\320\260\321\207\320\270 (12).md" b/day_12/day_12.md similarity index 100% rename from "day_12/\320\227\320\260\320\264\320\260\321\207\320\270 (12).md" rename to day_12/day_12.md diff --git "a/day_13/\320\227\320\260\320\264\320\260\321\207\320\270 (13).md" b/day_13/day_13.md similarity index 100% rename from "day_13/\320\227\320\260\320\264\320\260\321\207\320\270 (13).md" rename to day_13/day_13.md diff --git "a/day_14/\320\227\320\260\320\264\320\260\321\207\320\270 (14).md" b/day_14/day_14.md similarity index 100% rename from "day_14/\320\227\320\260\320\264\320\260\321\207\320\270 (14).md" rename to day_14/day_14.md diff --git "a/day_2/\320\227\320\260\320\264\320\260\321\207\320\270 (2).md" b/day_2/day_2.md similarity index 100% rename from "day_2/\320\227\320\260\320\264\320\260\321\207\320\270 (2).md" rename to day_2/day_2.md diff --git "a/day_3/\320\227\320\260\320\264\320\260\321\207\320\270 (3).md" b/day_3/day_3.md similarity index 100% rename from "day_3/\320\227\320\260\320\264\320\260\321\207\320\270 (3).md" rename to day_3/day_3.md diff --git "a/day_4/\320\227\320\260\320\264\320\260\321\207\320\270 (4).md" b/day_4/day_4.md similarity index 100% rename from "day_4/\320\227\320\260\320\264\320\260\321\207\320\270 (4).md" rename to day_4/day_4.md diff --git "a/day_5/\320\227\320\260\320\264\320\260\321\207\320\270 (5).md" b/day_5/day_5.md similarity index 100% rename from "day_5/\320\227\320\260\320\264\320\260\321\207\320\270 (5).md" rename to day_5/day_5.md diff --git "a/day_6/\320\227\320\260\320\264\320\260\321\207\320\270 (6).md" b/day_6/day_6.md similarity index 100% rename from "day_6/\320\227\320\260\320\264\320\260\321\207\320\270 (6).md" rename to day_6/day_6.md diff --git "a/day_7/\320\227\320\260\320\264\320\260\321\207\320\270 (7).md" b/day_7/day_7.md similarity index 100% rename from "day_7/\320\227\320\260\320\264\320\260\321\207\320\270 (7).md" rename to day_7/day_7.md diff --git "a/day_8/\320\227\320\260\320\264\320\260\321\207\320\270 (8).md" b/day_8/day_8.md similarity index 100% rename from "day_8/\320\227\320\260\320\264\320\260\321\207\320\270 (8).md" rename to day_8/day_8.md diff --git "a/day_9/\320\227\320\260\320\264\320\260\321\207\320\270 (9).md" b/day_9/day_9.md similarity index 100% rename from "day_9/\320\227\320\260\320\264\320\260\321\207\320\270 (9).md" rename to day_9/day_9.md From 5523444c1dcb459a027b93241a4d50ca0062b096 Mon Sep 17 00:00:00 2001 From: Eugene Maslovich Date: Tue, 29 Jul 2025 15:09:36 +0000 Subject: [PATCH 03/52] Update task descriptions for days 1 to 14, adding numbering and clarifying requirements for each task. --- day_1/day_1.md | 8 ++++---- day_10/day_10.md | 8 ++++---- day_11/day_11.md | 8 ++++---- day_12/day_12.md | 8 ++++---- day_13/day_13.md | 8 ++++---- day_14/day_14.md | 8 ++++---- day_2/day_2.md | 8 ++++---- day_3/day_3.md | 8 ++++---- day_4/day_4.md | 8 ++++---- day_5/day_5.md | 8 ++++---- day_6/day_6.md | 8 ++++---- day_7/day_7.md | 8 ++++---- day_8/day_8.md | 8 ++++---- day_9/day_9.md | 8 ++++---- 14 files changed, 56 insertions(+), 56 deletions(-) diff --git a/day_1/day_1.md b/day_1/day_1.md index e8b809d..572ee27 100644 --- a/day_1/day_1.md +++ b/day_1/day_1.md @@ -1,6 +1,6 @@ # День 1 -## Что выведется в консоль? +## 1. Что выведется в консоль? И почему? @@ -17,7 +17,7 @@ new Foo().bla(); // ? new Foo().baz(); // ? ``` -## Реализация Partial классов +## 2. Реализация Partial классов Необходимо написать функцию, которая бы позволяла расширять заданный класс новыми методами. В добавляемых методах должен корректно работать super. @@ -50,7 +50,7 @@ console.log(example.bar); // Случайное число console.log(example.bar); // Случайное число ``` -## Шаблонизатор строки с поддержкой выражений +## 3. Шаблонизатор строки с поддержкой выражений Необходимо создать функцию, которая бы принимала шаблон и объект с данными, а возвращала бы конечную строку. @@ -58,7 +58,7 @@ console.log(example.bar); // Случайное число format('Hello ${name}! May age is ${age * day_2}.', {name: 'Bob', age: 12}); // 'Hello Bob! My age is 24.' ``` -## Реализация функции аналогичной Promise.allSettled +## 4. Реализация функции аналогичной Promise.allSettled Необходимо написать функцию, которая бы повторяло поведение Promise.allSettled. diff --git a/day_10/day_10.md b/day_10/day_10.md index 89af763..48d4b01 100644 --- a/day_10/day_10.md +++ b/day_10/day_10.md @@ -1,6 +1,6 @@ # День 10 -## Реализовать функцию свертки списка в диапазоны +## 1. Реализовать функцию свертки списка в диапазоны Необходимо написать функцию, которая бы принимала неотсортированный массив чисел и возвращала бы строку, где подряд идущие числа свернуты в диапазоны. @@ -8,7 +8,7 @@ console.log(reduce([1, 3, 6, 8, 7, 11, 45, 46, 2])); // 1-3, 6-8, 11, 45-46 ``` -## Параллельные асинхронные запросы +## 2. Параллельные асинхронные запросы Необходимо написать функцию, которая бы принимала Iterable функций и возвращала результат аналогичный Promise.allSettled. Каждая из переданных функций может вернуть Promise. Одновременно может быть запущено не более заданного количества Promise, но при этом максимально возможное. @@ -22,7 +22,7 @@ allSettledLimit([ ], 2).then(console.log); ``` -## Написать итератор для дерева +## 3. Написать итератор для дерева Необходимо написать итератор для заданной структуры дерева. @@ -39,7 +39,7 @@ console.log(i.next()); // {value: 4, done: false} console.log(i.next()); // {value: undefined, done: true} ``` -## Реализация функции аналогичной parseFloat +## 4. Реализация функции аналогичной parseFloat Необходимо написать функцию, которая бы повторяло поведение parseFloat. diff --git a/day_11/day_11.md b/day_11/day_11.md index 509829f..a786381 100644 --- a/day_11/day_11.md +++ b/day_11/day_11.md @@ -1,6 +1,6 @@ # День 11 -## Определить век по году +## 1. Определить век по году Необходимо написать функцию, которая определяет век по переданному году. @@ -8,7 +8,7 @@ console.log(getCentury(1901)); // 20 ``` -## Пересечение диапазонов +## 2. Пересечение диапазонов Необходимо написать функцию, которая бы принимала бы две строки с числовыми диапазонами и возвращала бы новую строку, где отображены пересечения этих интервалов. @@ -16,7 +16,7 @@ console.log(getCentury(1901)); // 20 console.log(intersectRanges('1-2; 4-6; 9-11', '1-5; 10-14; 15')); // 1-2; 4-5; 10-11 ``` -## Найти два элемента массива сумма которых дает заданное число +## 3. Найти два элемента массива сумма которых дает заданное число Необходимо написать функцию, которая бы принимала массив чисел и число и возвращала бы индексы двух элементов массива сумма которых даёт заданное число. @@ -25,7 +25,7 @@ console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1] console.log(twoSum([3, 2, 4], 6)); // [1, 2] ``` -## Реализовать итератор на основе EventEmitter +## 4. Реализовать итератор на основе EventEmitter Необходимо написать функцию, которая бы создавала асинхронный итератор на основе заданного EventEmitter и события. diff --git a/day_12/day_12.md b/day_12/day_12.md index e8f11d5..e3d594a 100644 --- a/day_12/day_12.md +++ b/day_12/day_12.md @@ -1,6 +1,6 @@ # День 12 -## Определение палиндрома +## 1. Определение палиндрома Необходимо определить, является ли строка палиндромом или нет. @@ -11,7 +11,7 @@ console.log(isPalindrome('a')); // false console.log(isPalindrome('azt')); // false ``` -## Извлечение подстрок в кавычках +## 2. Извлечение подстрок в кавычках Необходимо написать функцию, которая принимает строку и возвращает массив подстроку исходной. Подстроки выбираются по признаку нахождения в тех или иных кавычках. @@ -20,7 +20,7 @@ console.log(isPalindrome('azt')); // false console.log(extractQuotes('Это строка в "кавычках\'" и `"эта"` тоже, а это "хитрая строка\\""')); // ["кавычках'", '"эта"', 'хитрая строка\\"'] ``` -## Реализовать zip для асинхронно итерируемых объектов +## 3. Реализовать zip для асинхронно итерируемых объектов Необходимо реализовать функцию zip для множества асинхронно итерируемых объектов. @@ -37,7 +37,7 @@ async function* makeAsync(iter) { })(); ``` -## Вычислить разницу двух строк +## 4. Вычислить разницу двух строк Необходимо написать функцию, которая бы вычисляла разницу между двумя заданными строками. Разница вычисляется из суммы всех добавлений, удалений и замен символов так, чтобы строки стали равны. diff --git a/day_13/day_13.md b/day_13/day_13.md index 8680005..00d0b8d 100644 --- a/day_13/day_13.md +++ b/day_13/day_13.md @@ -1,6 +1,6 @@ # День 13 -## Поиск подстроки в строке +## 1. Поиск подстроки в строке Необходимо написать функцию, которая принимала бы строку и подстроку и возвращала бы true, если заданная подстрока присутствует в строке. Регулярные выражения использовать нельзя. @@ -10,7 +10,7 @@ console.log(includes('hello bob!', 'bob')); // true console.log(includes('abba', 'aba')); // false ``` -## Ломающий коммит +## 2. Ломающий коммит Имеется множество коммитов и функция проверки работы программы. На одном из коммитов программа начинает ломаться. Необходимо выяснить за минимальное время этот коммит. @@ -23,7 +23,7 @@ const test = (commit) => commit === 'good'; console.log(findFirstBadCommit(commits, test)); // 3 ``` -## Максимальная подстрока без повторений +## 3. Максимальная подстрока без повторений Необходимо написать функцию, которая принимала бы строку и возвращала бы максимальную подстроку без повторяющихся символов. @@ -34,7 +34,7 @@ console.log(maxUniqueSubstr('bbbbb')); // b console.log(maxUniqueSubstr('pwwkew')); // wke ``` -## Генерация анаграмм +## 4. Генерация анаграмм Необходимо создать функцию, которая бы принимала строку и возвращала список всех анаграмм этого слова. diff --git a/day_14/day_14.md b/day_14/day_14.md index 723f51f..bef9f94 100644 --- a/day_14/day_14.md +++ b/day_14/day_14.md @@ -1,6 +1,6 @@ # День 14 -## Сжатие глубокого объекта +## 1. Сжатие глубокого объекта Необходимо написать функцию, которая бы сжимала некоторый глубокий объект в плоский вид. @@ -16,7 +16,7 @@ const obj = { console.log(collapse(obj)); ``` -## Валидация скобочных групп +## 2. Валидация скобочных групп Необходимо написать функцию, которая бы принимала строку и возвращала true, если у каждого из символов `{`, `[` и `(` есть своя закрывающая пара и они стоят в правильной последовательности. @@ -26,7 +26,7 @@ console.log(isValid('(hello{world)} and [me])')); // false console.log(isValid(')')); // false ``` -## Самый большой палиндром в строке +## 3. Самый большой палиндром в строке Необходимо написать функцию, которая бы принимала строку и возвращала подстроку с самым большим палиндромом в строке. @@ -35,7 +35,7 @@ console.log(findPalindromicSubstring('adaabbabla')); // 'abba' console.log(findPalindromicSubstring('blablur')); // null ``` -## Реализация очереди с приоритетом +## 4. Реализация очереди с приоритетом Необходимо написать класс, который бы предоставлял API очереди, но с возможности задания функции-компаратора для сортировки на вставке. diff --git a/day_2/day_2.md b/day_2/day_2.md index 70dd12b..0926495 100644 --- a/day_2/day_2.md +++ b/day_2/day_2.md @@ -1,6 +1,6 @@ # День 2 -## Что выведется в консоль? +## 1. Что выведется в консоль? И почему? @@ -16,7 +16,7 @@ for (var i = 0; i < arr.length; ++i) { val.then(console.log); // ? ``` -## Установка свойства по сложному пути в объекте +## 2. Установка свойства по сложному пути в объекте Необходимо написать функцию, которая бы устанавливало переданное значение объекту по заданному пути. @@ -29,7 +29,7 @@ setByPath(obj, 'foo.bla', 2); console.log(obj); // {foo: {bar: day_1, bla: day_2}} ``` -## Нахождение максимальной глубины в дереве +## 3. Нахождение максимальной глубины в дереве Необходимо написать функцию, которая бы возвращала максимальную глубину заданного дерева. @@ -51,7 +51,7 @@ const obj = { console.log(maxDepth(obj)); // day_2 ``` -## Реализация функции аналогичной parseInt +## 4. Реализация функции аналогичной parseInt Необходимо написать функцию, которая бы повторяло поведение parseInt. diff --git a/day_3/day_3.md b/day_3/day_3.md index 944fad1..949cf12 100644 --- a/day_3/day_3.md +++ b/day_3/day_3.md @@ -1,6 +1,6 @@ # День 3 -## Что и в каком порядке выведется в консоль? +## 1. Что и в каком порядке выведется в консоль? И почему? @@ -24,7 +24,7 @@ new Promise((resolve) => { console.log('bak'); ``` -## Реализовать функцию setImmediate +## 2. Реализовать функцию setImmediate Необходимо функцию, которая бы предоставляла API схожее с setTimeout, но создавала бы микротаску. @@ -44,7 +44,7 @@ const timer = setImmediate(() => { clearImmediate(timer); ``` -## Реализация функции каррирования +## 3. Реализация функции каррирования Необходимо написать функцию, которая бы принимала другую функцию и возвращала её каррированную версию. @@ -56,7 +56,7 @@ console.log(sum(1)(2)(3, 4)); // 10; console.log(sum(1)(2, 3, 4)); // 10; ``` -## Реализовать zip для синхронных Iterable объектов +## 4. Реализовать zip для синхронных Iterable объектов Общее количество кортежей берется по минимальному значению. Функция должна возвращать IterableIterator. diff --git a/day_4/day_4.md b/day_4/day_4.md index d19476f..e0478f0 100644 --- a/day_4/day_4.md +++ b/day_4/day_4.md @@ -1,6 +1,6 @@ # День 4 -## Реализация функции promisify +## 1. Реализация функции promisify Необходимо написать функцию, которая бы создавал функции с Promise API на основе функций с callback API. Формат callback функции ожидается в стиле Node.js, где первый аргумент - это объект ошибки. @@ -21,7 +21,7 @@ promiseDiv(1, 2).then(console.log); // 0.day_5 promiseDiv(1, 0).catch(console.log); // TypeError('Нельзя делить на 0') ``` -## Написать класс числа с рекурсивным API для арифметических операций +## 2. Написать класс числа с рекурсивным API для арифметических операций Необходимо проанализировать пример ниже и реализовать нужный API, где @@ -36,7 +36,7 @@ const num = new MyNumber(10); console.log(num.add(2).mult(2).sub(1) - 5); // 18 ``` -## Выборочная сортировка массива +## 3. Выборочная сортировка массива Необходимо написать функцию sort, которая бы сортировала элементы со значением кратным двум. @@ -44,7 +44,7 @@ console.log(num.add(2).mult(2).sub(1) - 5); // 18 console.log(sort([7, 1, 4, 2, 9, 8])); // [7, day_1, day_2, day_4, 9, 8] ``` -## Поиск в массиве строк по заданной подстроке с пропуском символов +## 4. Поиск в массиве строк по заданной подстроке с пропуском символов Необходимо написать функцию, которая бы принимала массив строк и строку и возвращала бы новый массив, состоящий только из элементов с содержанием заданной подстроки. Алгоритм должен учитывать, что подстрока может быть найдена в строке при помощи пропуска части символов в строке (нечеткий поиск). diff --git a/day_5/day_5.md b/day_5/day_5.md index 30440b3..3533e71 100644 --- a/day_5/day_5.md +++ b/day_5/day_5.md @@ -1,6 +1,6 @@ # День 5 -## Написать функцию debounce +## 1. Написать функцию debounce Необходимо написать функцию, которая бы принимала другую функцию и возвращала её debounce версию. @@ -18,7 +18,7 @@ debouncedLaugh(); debouncedLaugh(); ``` -## Написать простейший EventEmitter +## 2. Написать простейший EventEmitter Должна быть поддержка множественных и одноразовых событий и отмены сразу всех событий по имени. @@ -34,7 +34,7 @@ ee.off('foo', console.log); // Отмена конкретного обрабо ee.off('foo'); // Отмена всех обработчиков этого события ``` -## Реализация функции аналогичной Array.prototype.flat +## 3. Реализация функции аналогичной Array.prototype.flat Необходимо написать функцию, которая бы повторяло поведение Array.prototype.flat. @@ -43,7 +43,7 @@ console.log(flat([[1, 2], [[1]], 2])); // [1, 2, [1], 2] console.log(flat([[1, 2], [[1]], 2], 2)); // [1, 2, 1, 2] ``` -## Топологическая сортировка массива +## 4. Топологическая сортировка массива Необходимо написать функцию, которая бы делала топологическую сортировку массива по заданному критерию. diff --git a/day_6/day_6.md b/day_6/day_6.md index f411b10..fb7e080 100644 --- a/day_6/day_6.md +++ b/day_6/day_6.md @@ -1,6 +1,6 @@ # День 6 -## Реализация паттерна "Строитель" для класса +## 1. Реализация паттерна "Строитель" для класса Необходимо реализовать паттерн "Строитель" для заданного класса. @@ -16,7 +16,7 @@ class User { User.name('Bob').age(47).skills(['Coding']).create(); // User({name: 'Bob', age: 47, skills: ['Coding']}) ``` -## Обход дерева в ширину +## 2. Обход дерева в ширину Необходимо вывести элементы дерева таким образом, чтобы по очереди выводились все элементы каждого яруса. @@ -37,7 +37,7 @@ const tree = { log(tree); // 1 2 3 4 ``` -## Реализовать функцию преобразования CamelCase в dash-style +## 3. Реализовать функцию преобразования CamelCase в dash-style Необходимо создать функцию, которая бы принимала строку в CamelCase и возвращала бы её вариант в dash-style. @@ -47,7 +47,7 @@ console.log(dasherize('SuperMAN')); // 'super-man' console.log(dasherize('VirtualDOMFragment')); // 'virtual-dom-fragment' ``` -## Реализовать функцию waterfall для callback функций +## 4. Реализовать функцию waterfall для callback функций Необходимо создать функцию для композиции асинхронного кода на callback функциях, которая работает как показано на примере. diff --git a/day_7/day_7.md b/day_7/day_7.md index 76cfd1f..fadb1b4 100644 --- a/day_7/day_7.md +++ b/day_7/day_7.md @@ -1,6 +1,6 @@ # День 7 -## Нахождения разницы двух множеств +## 1. Нахождения разницы двух множеств Необходимо написать функцию, которая принимает два массива (элементы в массивах не повторяются) и возвращает новый массив. В новом массиве должны быть все элементы которые есть в первом массиве, но нет во втором. @@ -9,7 +9,7 @@ console.log(diff([1, 2, 3, 4, 5], [3, 4, 1])); // [2, 5] ``` -## Написать асинхронный семафор +## 2. Написать асинхронный семафор Необходимо написать функцию, которая бы создавала "асинхронный семафор" на основе переданной функции и набора флагов, как это показано в примере ниже. @@ -26,7 +26,7 @@ semaphore('bar'); // 'Boom!' semaphore(); ``` -## Сериализация нестандартных объектов +## 3. Сериализация нестандартных объектов Необходимо написать функцию, которая бы позволяла преобразовать заданные JS объекты в строку и обратно. @@ -45,7 +45,7 @@ const str = serialize(original); parse(str); ``` -## Вычисление инфиксного выражения с приоритетами +## 4. Вычисление инфиксного выражения с приоритетами Необходимо написать функцию, которая бы принимала строку с математическим выражением и вычисляла бы её результат. В строке могут использоваться операции сложения, вычитания, умножения и деления. Function и eval использовать нельзя. diff --git a/day_8/day_8.md b/day_8/day_8.md index ec4529d..ebbb8b6 100644 --- a/day_8/day_8.md +++ b/day_8/day_8.md @@ -1,6 +1,6 @@ # День 8 -## Написать функцию throttle +## 1. Написать функцию throttle Необходимо написать функцию, которая бы принимала другую функцию и возвращала её throttle версию. @@ -18,7 +18,7 @@ throttledLaugh(); throttledLaugh(); ``` -## Написать функцию для сравнения двух объектов +## 2. Написать функцию для сравнения двух объектов Необходимо написать функцию для глубокого сравнения двух заданных объектов. @@ -27,7 +27,7 @@ console.log(compare({a: 1, b: [1, 2, 3]}, {a: 1, b: [1, 2, 3]})); // true console.log(compare({a: 1, b: [1, 2]}, {a: 1, b: [1, 2, 3]})); // false ``` -## Найти строку для которой нет анаграммы +## 3. Найти строку для которой нет анаграммы Необходимо написать функцию, которая бы принимала массив строк и возвращала бы новый массив. Элементами этого массива должны быть строки, для которых не существует анаграмм среди элементов первого массива. @@ -36,7 +36,7 @@ console.log(compare({a: 1, b: [1, 2]}, {a: 1, b: [1, 2, 3]})); // false console.log(getUniqueStrs(['atoe', 'otea', 'ben', 'enb', 'baz', 'foo'])); // ['baz', 'foo'] ``` -## Реализация структуры данных очередь +## 4. Реализация структуры данных очередь Необходимо создать класс, который бы предоставлял API очереди. Использовать стандартное API массивов JS нельзя. diff --git a/day_9/day_9.md b/day_9/day_9.md index f4da58f..ea94501 100644 --- a/day_9/day_9.md +++ b/day_9/day_9.md @@ -1,6 +1,6 @@ # День 9 -## Реализовать функция бинарного поиска с callback +## 1. Реализовать функция бинарного поиска с callback Необходимо написать функцию, которая бы принимала отсортированный массив и функцию-компаратор и возвращала бы индекс найденного элемента (если компаратор вернул 0). Для поиска должен использоваться алгоритм бинарного поиска. @@ -10,7 +10,7 @@ console.log(bisect([1, 2, 3, 4, 5, 6, 7], (val) => 4 - val)); // 3 console.log(bisect([1, 2, 3, 4, 5, 6, 7], (val) => 234 - val)); // -1 ``` -## Реализовать функцию для амортизируемого повторного запроса +## 2. Реализовать функцию для амортизируемого повторного запроса Необходимо написать функцию retry, которая бы принимала бы функцию, возвращающую Promise и объект настроек для указания повторений в случае ошибки. Параметр `retry` задает максимальное количество повторения, а `delay` - задержку в мс между повторами (задается в виде функции, которая принимает номер попытки). @@ -19,7 +19,7 @@ console.log(bisect([1, 2, 3, 4, 5, 6, 7], (val) => 234 - val)); // -1 retry(() => fetch('//some-data'), {retry: 3, delay: (n) => n * 1000}).then(console.log, console.error); ``` -## Реализовать класс LRU кеша +## 3. Реализовать класс LRU кеша Необходимо реализовать класс кеширования с алгоритмом LRU. @@ -37,7 +37,7 @@ cache.set('key4', 4); console.log(cache.has('key2')); // false ``` -## Сжатие строки +## 4. Сжатие строки Необходимо написать функцию, которая бы принимала бы строку и "схлопывала" бы все подряд идущие повторения. From 6b6e7123cc14a6bbe05710510121c68782b04fd3 Mon Sep 17 00:00:00 2001 From: Eugene Maslovich Date: Tue, 29 Jul 2025 15:14:55 +0000 Subject: [PATCH 04/52] Add initial task files for days 1 to 14 --- day_1/day_1_1.ts | 0 day_1/day_1_2.ts | 0 day_1/day_1_3.ts | 0 day_1/day_1_4.ts | 0 day_10/day_10_1.ts | 0 day_10/day_10_2.ts | 0 day_10/day_10_3.ts | 0 day_10/day_10_4.ts | 0 day_11/day_11_1.ts | 0 day_11/day_11_2.ts | 0 day_11/day_11_3.ts | 0 day_11/day_11_4.ts | 0 day_12/day_12_1.ts | 0 day_12/day_12_2.ts | 0 day_12/day_12_3.ts | 0 day_12/day_12_4.ts | 0 day_13/day_13_1.ts | 0 day_13/day_13_2.ts | 0 day_13/day_13_3.ts | 0 day_13/day_13_4.ts | 0 day_14/day_14_1.ts | 0 day_14/day_14_2.ts | 0 day_14/day_14_3.ts | 0 day_14/day_14_4.ts | 0 day_2/day_2_1.ts | 0 day_2/day_2_2.ts | 0 day_2/day_2_3.ts | 0 day_2/day_2_4.ts | 0 day_3/day_3_1.ts | 0 day_3/day_3_2.ts | 0 day_3/day_3_3.ts | 0 day_3/day_3_4.ts | 0 day_4/day_4_1.ts | 0 day_4/day_4_2.ts | 0 day_4/day_4_3.ts | 0 day_4/day_4_4.ts | 0 day_5/day_5_1.ts | 0 day_5/day_5_2.ts | 0 day_5/day_5_3.ts | 0 day_5/day_5_4.ts | 0 day_6/day_6_1.ts | 0 day_6/day_6_2.ts | 0 day_6/day_6_3.ts | 0 day_6/day_6_4.ts | 0 day_7/day_7_1.ts | 0 day_7/day_7_2.ts | 0 day_7/day_7_3.ts | 0 day_7/day_7_4.ts | 0 day_8/day_8_1.ts | 0 day_8/day_8_2.ts | 0 day_8/day_8_3.ts | 0 day_8/day_8_4.ts | 0 day_9/day_9_1.ts | 0 day_9/day_9_2.ts | 0 day_9/day_9_3.ts | 0 day_9/day_9_4.ts | 0 56 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 day_1/day_1_1.ts create mode 100644 day_1/day_1_2.ts create mode 100644 day_1/day_1_3.ts create mode 100644 day_1/day_1_4.ts create mode 100644 day_10/day_10_1.ts create mode 100644 day_10/day_10_2.ts create mode 100644 day_10/day_10_3.ts create mode 100644 day_10/day_10_4.ts create mode 100644 day_11/day_11_1.ts create mode 100644 day_11/day_11_2.ts create mode 100644 day_11/day_11_3.ts create mode 100644 day_11/day_11_4.ts create mode 100644 day_12/day_12_1.ts create mode 100644 day_12/day_12_2.ts create mode 100644 day_12/day_12_3.ts create mode 100644 day_12/day_12_4.ts create mode 100644 day_13/day_13_1.ts create mode 100644 day_13/day_13_2.ts create mode 100644 day_13/day_13_3.ts create mode 100644 day_13/day_13_4.ts create mode 100644 day_14/day_14_1.ts create mode 100644 day_14/day_14_2.ts create mode 100644 day_14/day_14_3.ts create mode 100644 day_14/day_14_4.ts create mode 100644 day_2/day_2_1.ts create mode 100644 day_2/day_2_2.ts create mode 100644 day_2/day_2_3.ts create mode 100644 day_2/day_2_4.ts create mode 100644 day_3/day_3_1.ts create mode 100644 day_3/day_3_2.ts create mode 100644 day_3/day_3_3.ts create mode 100644 day_3/day_3_4.ts create mode 100644 day_4/day_4_1.ts create mode 100644 day_4/day_4_2.ts create mode 100644 day_4/day_4_3.ts create mode 100644 day_4/day_4_4.ts create mode 100644 day_5/day_5_1.ts create mode 100644 day_5/day_5_2.ts create mode 100644 day_5/day_5_3.ts create mode 100644 day_5/day_5_4.ts create mode 100644 day_6/day_6_1.ts create mode 100644 day_6/day_6_2.ts create mode 100644 day_6/day_6_3.ts create mode 100644 day_6/day_6_4.ts create mode 100644 day_7/day_7_1.ts create mode 100644 day_7/day_7_2.ts create mode 100644 day_7/day_7_3.ts create mode 100644 day_7/day_7_4.ts create mode 100644 day_8/day_8_1.ts create mode 100644 day_8/day_8_2.ts create mode 100644 day_8/day_8_3.ts create mode 100644 day_8/day_8_4.ts create mode 100644 day_9/day_9_1.ts create mode 100644 day_9/day_9_2.ts create mode 100644 day_9/day_9_3.ts create mode 100644 day_9/day_9_4.ts diff --git a/day_1/day_1_1.ts b/day_1/day_1_1.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_1/day_1_2.ts b/day_1/day_1_2.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_1/day_1_3.ts b/day_1/day_1_3.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_1/day_1_4.ts b/day_1/day_1_4.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_10/day_10_1.ts b/day_10/day_10_1.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_10/day_10_2.ts b/day_10/day_10_2.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_10/day_10_3.ts b/day_10/day_10_3.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_10/day_10_4.ts b/day_10/day_10_4.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_11/day_11_1.ts b/day_11/day_11_1.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_11/day_11_2.ts b/day_11/day_11_2.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_11/day_11_3.ts b/day_11/day_11_3.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_11/day_11_4.ts b/day_11/day_11_4.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_12/day_12_1.ts b/day_12/day_12_1.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_12/day_12_2.ts b/day_12/day_12_2.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_12/day_12_3.ts b/day_12/day_12_3.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_12/day_12_4.ts b/day_12/day_12_4.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_13/day_13_1.ts b/day_13/day_13_1.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_13/day_13_2.ts b/day_13/day_13_2.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_13/day_13_3.ts b/day_13/day_13_3.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_13/day_13_4.ts b/day_13/day_13_4.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_14/day_14_1.ts b/day_14/day_14_1.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_14/day_14_2.ts b/day_14/day_14_2.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_14/day_14_3.ts b/day_14/day_14_3.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_14/day_14_4.ts b/day_14/day_14_4.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_2/day_2_1.ts b/day_2/day_2_1.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_2/day_2_2.ts b/day_2/day_2_2.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_2/day_2_3.ts b/day_2/day_2_3.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_2/day_2_4.ts b/day_2/day_2_4.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_3/day_3_1.ts b/day_3/day_3_1.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_3/day_3_2.ts b/day_3/day_3_2.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_3/day_3_3.ts b/day_3/day_3_3.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_3/day_3_4.ts b/day_3/day_3_4.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_4/day_4_1.ts b/day_4/day_4_1.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_4/day_4_2.ts b/day_4/day_4_2.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_4/day_4_3.ts b/day_4/day_4_3.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_4/day_4_4.ts b/day_4/day_4_4.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_5/day_5_1.ts b/day_5/day_5_1.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_5/day_5_2.ts b/day_5/day_5_2.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_5/day_5_3.ts b/day_5/day_5_3.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_5/day_5_4.ts b/day_5/day_5_4.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_6/day_6_1.ts b/day_6/day_6_1.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_6/day_6_2.ts b/day_6/day_6_2.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_6/day_6_3.ts b/day_6/day_6_3.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_6/day_6_4.ts b/day_6/day_6_4.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_7/day_7_1.ts b/day_7/day_7_1.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_7/day_7_2.ts b/day_7/day_7_2.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_7/day_7_3.ts b/day_7/day_7_3.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_7/day_7_4.ts b/day_7/day_7_4.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_8/day_8_1.ts b/day_8/day_8_1.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_8/day_8_2.ts b/day_8/day_8_2.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_8/day_8_3.ts b/day_8/day_8_3.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_8/day_8_4.ts b/day_8/day_8_4.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_9/day_9_1.ts b/day_9/day_9_1.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_9/day_9_2.ts b/day_9/day_9_2.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_9/day_9_3.ts b/day_9/day_9_3.ts new file mode 100644 index 0000000..e69de29 diff --git a/day_9/day_9_4.ts b/day_9/day_9_4.ts new file mode 100644 index 0000000..e69de29 From cc8a593f3c6e38fa51f2b932b1b972ae08581425 Mon Sep 17 00:00:00 2001 From: Eugene Maslovich Date: Tue, 29 Jul 2025 17:15:36 +0000 Subject: [PATCH 05/52] Add class Foo with methods demonstrating 'this' behavior in functions and arrow functions --- day_1/day_1_1.ts | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/day_1/day_1_1.ts b/day_1/day_1_1.ts index e69de29..2cc5899 100644 --- a/day_1/day_1_1.ts +++ b/day_1/day_1_1.ts @@ -0,0 +1,36 @@ +class Foo { + bar = 1; + + bla = () => console.log(this.bar); + + baz = function () { console.log(this.bar); }; +} + +new Foo().bla(); // Выведется 1 +new Foo().baz(); // Выведется 1 + +/* + +Согласно спецификации ECMAScript 15.2.4 Runtime Semantics: InstantiateOrdinaryFunctionObject +(https://tc39.es/ecma262/#sec-runtime-semantics-instantiateordinaryfunctionobject), +значение "this" в обычной функции определяется в момент вызова функции, а не в момент +её создания. На это указывает пункт 3: +"Let F be OrdinaryFunctionCreate(..., FunctionBody, non-lexical-this, env, privateEnv)". +Как видим, указан "non-lexical-this". Детали вычисления "this" зависят от контекста +вызова функции и описаны в ECMAScript 13.3.6.2 (https://tc39.es/ecma262/#sec-evaluatecall). +Если функция вызывается как метод объекта, то "this" будет ссылаться на этот объект. +Если функция вызывается как обычная функция, то "this" будет ссылаться на глобальный объект +или будет undefined в строгом режиме. + +Значение "this" при вызове стрелочной функции определяется согласно спецификации +ECMAScript 15.3.4 Runtime Semantics: InstantiateArrowFunctionExpression +(https://tc39.es/ecma262/#sec-runtime-semantics-instantiatearrowfunctionexpression). +В частности, в пункте 5: Let closure be OrdinaryFunctionCreate(..., ConciseBody, lexical-this, env, privateEnv). +Видим, что указано "lexical-this". +Поэтому значение "this" в стрелочной функции определяется лексически, то есть +оно берётся из окружающего контекста, в котором функция была объявлена. + +Описание понятия лексической среды можно найти в 9.1 Environment Records +(https://tc39.es/ecma262/#sec-environment-records). + + */ From 45e1fd7adcd6daa890632a30a158dea2cc261bf3 Mon Sep 17 00:00:00 2001 From: Eugene Maslovich Date: Wed, 30 Jul 2025 08:31:20 +0000 Subject: [PATCH 06/52] Initialize project with TypeScript configuration, package.json, and .gitignore files --- .gitignore | 24 +++++ day_1/day_1_2.ts | 46 ++++++++++ package-lock.json | 217 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 16 ++++ tsconfig.json | 113 ++++++++++++++++++++++++ 5 files changed, 416 insertions(+) create mode 100644 .gitignore create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..887f435 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Node modules +node_modules/ + +# Logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Build output +dist/ +build/ +out/ +*.tsbuildinfo + +# VS Code settings +.vscode/ + +# Environment variables +.env +.env.* + +# OS files +.DS_Store \ No newline at end of file diff --git a/day_1/day_1_2.ts b/day_1/day_1_2.ts index e69de29..cd3548f 100644 --- a/day_1/day_1_2.ts +++ b/day_1/day_1_2.ts @@ -0,0 +1,46 @@ +class Parent { + foo() { + console.log('It works!'); + } +} + +class Example extends Parent {} + +// Your code starts here + +function partial(target, obj) { + const parentPrototype = Object.getPrototypeOf(target.prototype); + Object.setPrototypeOf(obj, parentPrototype); + Object.defineProperties(target.prototype, Object.getOwnPropertyDescriptors(obj)) +} + +/* +Важный нюанс! Super работает через [[HomeObject]] (10.2 ECMAScript Function Objects). +При этом изменить [[HomeObject]] мы не можем, и, соответственно, мы вынуждены менять +прототип именно у obj, а не его копии, например. Потому что, если бы мы решили сделать +копию obj, то при копировании в неё методов, [[HomeObject]] бы всё равно указывал на obj. +*/ + +// Your code ends here + +partial(Example, { + foo() { + super.foo(); + console.log('Yeaaah'); + }, + + get bar() { + return Math.random(); + } +}); + +const example = new Example(); + +example.foo(); // It works! Yeaaah + +// @ts-ignore +console.log(example.bar); // Случайное число +// @ts-ignore +console.log(example.bar); // Случайное число +// @ts-ignore +console.log(example.bar); // Случайное число \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f56faa5 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,217 @@ +{ + "name": "js-questions", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "js-questions", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "ts-node": "^10.9.2", + "typescript": "^5.8.3" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", + "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", + "dev": true, + "peer": true, + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true, + "peer": true + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..6748122 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "js-questions", + "version": "1.0.0", + "description": "## Days", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "ts-node": "^10.9.2", + "typescript": "^5.8.3" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1d5705c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,113 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "libReplacement": true, /* Enable lib replacement. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ + "noImplicitThis": false, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} From aa046f7cef4ec05107c540082f9e927fe83ea9d4 Mon Sep 17 00:00:00 2001 From: Eugene Maslovich Date: Sat, 2 Aug 2025 19:46:03 +0000 Subject: [PATCH 07/52] Update day 1 tasks: correct format function example and add detailed explanation for partial function --- day_1/day_1.md | 2 +- day_1/day_1_2.ts | 8 ++++++++ day_1/day_1_3.ts | 13 +++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/day_1/day_1.md b/day_1/day_1.md index 572ee27..edbdcec 100644 --- a/day_1/day_1.md +++ b/day_1/day_1.md @@ -55,7 +55,7 @@ console.log(example.bar); // Случайное число Необходимо создать функцию, которая бы принимала шаблон и объект с данными, а возвращала бы конечную строку. ```js -format('Hello ${name}! May age is ${age * day_2}.', {name: 'Bob', age: 12}); // 'Hello Bob! My age is 24.' +format('Hello ${name}! My age is ${age * 12}.', {name: 'Bob', age: 2}); // 'Hello Bob! My age is 24.' ``` ## 4. Реализация функции аналогичной Promise.allSettled diff --git a/day_1/day_1_2.ts b/day_1/day_1_2.ts index cd3548f..0bfa9f2 100644 --- a/day_1/day_1_2.ts +++ b/day_1/day_1_2.ts @@ -19,6 +19,14 @@ function partial(target, obj) { При этом изменить [[HomeObject]] мы не можем, и, соответственно, мы вынуждены менять прототип именно у obj, а не его копии, например. Потому что, если бы мы решили сделать копию obj, то при копировании в неё методов, [[HomeObject]] бы всё равно указывал на obj. + +Детальнее: при объявлении метода, внутри которого используется ключевое слово +"super", у функции создаётся скрытый атрибут [[HomeObject]], который указывает +на объект, внутри которого объявлен метод. При этом, если мы скопируем +метод в другой объект, [[HomeObject]] продолжит указывать на тот же самый +объект. То есть поиск по цепочке прототипов будет происходить исходя из +[[HomeObject]], который был зафиксирован на этапе создания функции, а не на +этапе выполнения. */ // Your code ends here diff --git a/day_1/day_1_3.ts b/day_1/day_1_3.ts index e69de29..6d6b46c 100644 --- a/day_1/day_1_3.ts +++ b/day_1/day_1_3.ts @@ -0,0 +1,13 @@ + +function format(template: string, args: Record) { + return template.replace(/\$\{([^}]+)\}/g, (_, expression: string) => { + const func = Function( + ...Object.keys(args), + `return ${expression}`, + ); + return func(...Object.values(args)); + }) +} + +let res = format('Hello ${name}! My age is ${age * 2}.', {name: 'Bob', age: 12}); +console.assert(res === 'Hello Bob! My age is 24.', `${res} !== Hello Bob! My age is 24.`); From da672f314720563b871c4f6782eafc1d5d7634cd Mon Sep 17 00:00:00 2001 From: Eugene Maslovich Date: Sat, 2 Aug 2025 20:13:48 +0000 Subject: [PATCH 08/52] Implement allSettled function to handle multiple promises and return their results --- day_1/day_1_4.ts | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/day_1/day_1_4.ts b/day_1/day_1_4.ts index e69de29..23a0020 100644 --- a/day_1/day_1_4.ts +++ b/day_1/day_1_4.ts @@ -0,0 +1,43 @@ +type PromiseState = { + status: 'fullfilled', + value: any, +} | { + status: 'rejected', + reason: any, +} + +function allSettled(promises: any[]): Promise { + return new Promise((resolve) => { + let count = 0; + const results: PromiseState[] = []; + for (let i = 0; i < promises.length; i++) { + if (promises[i]?.then) { + promises[i].then((value) => { + count += 1; + results[i] = {status: 'fullfilled', value}; + if (count === promises.length) { + resolve(results); + } + }).catch((reason) => { + count += 1; + results[i] = {status: 'rejected', reason}; + if (count === promises.length) { + resolve(results); + } + }); + } else { + count += 1; + results[i] = {status: 'fullfilled', value: promises[i]}; + if (count === promises.length) { + resolve(results); + } + } + } + }); +} + +allSettled([1, Promise.resolve(2), Promise.reject(3)]).then(([v1, v2, v3]) => { + console.log(v1); // {status: 'fulfilled', value: 1} + console.log(v2); // {status: 'fulfilled', value: 2} + console.log(v3); // {status: 'rejected', reason: 3} +}); From 175e1df5625a749524eb29f4e45256fc11be5b10 Mon Sep 17 00:00:00 2001 From: Eugene Maslovich Date: Sat, 2 Aug 2025 21:14:19 +0000 Subject: [PATCH 09/52] Add implementations for any and race functions, and update allSettled example --- day_1/day_1.md | 19 +++++++++++++++++++ day_1/day_1_4.ts | 15 +++++---------- day_1/day_1_5.ts | 25 +++++++++++++++++++++++++ day_1/day_1_6.ts | 26 ++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 10 deletions(-) create mode 100644 day_1/day_1_5.ts create mode 100644 day_1/day_1_6.ts diff --git a/day_1/day_1.md b/day_1/day_1.md index edbdcec..2cc189e 100644 --- a/day_1/day_1.md +++ b/day_1/day_1.md @@ -69,3 +69,22 @@ allSettled([1, Promise.resolve(2), Promise.reject(3)]).then(([v1, v2, v3]) => { console.log(v3); // {status: 'rejected', reason: 3} }); ``` + +## 5. Реализация функции аналогичной Promise.any + +```js +any([Promise.reject(1), Promise.resolve(2), 3]).then((res) => { + console.log(res); // 2 +}); +``` + +## 6. Реализация функции аналогичной Promise.race + +```js +race([Promise.resolve(2), 3]).then((res) => { + console.log(res); // 2 +}); +race([Promise.reject(1), Promise.resolve(2), 3]).catch((reason) => { + console.log(reason); // 1 +}); +``` diff --git a/day_1/day_1_4.ts b/day_1/day_1_4.ts index 23a0020..ca5874e 100644 --- a/day_1/day_1_4.ts +++ b/day_1/day_1_4.ts @@ -11,27 +11,22 @@ function allSettled(promises: any[]): Promise { let count = 0; const results: PromiseState[] = []; for (let i = 0; i < promises.length; i++) { - if (promises[i]?.then) { - promises[i].then((value) => { + Promise.resolve(promises[i]).then( + (value) => { count += 1; results[i] = {status: 'fullfilled', value}; if (count === promises.length) { resolve(results); } - }).catch((reason) => { + }, + (reason) => { count += 1; results[i] = {status: 'rejected', reason}; if (count === promises.length) { resolve(results); } - }); - } else { - count += 1; - results[i] = {status: 'fullfilled', value: promises[i]}; - if (count === promises.length) { - resolve(results); } - } + ); } }); } diff --git a/day_1/day_1_5.ts b/day_1/day_1_5.ts new file mode 100644 index 0000000..bffba03 --- /dev/null +++ b/day_1/day_1_5.ts @@ -0,0 +1,25 @@ +function any(promises: any[]): Promise { + let rejectedReasons: any[] = []; + return new Promise((resolve, reject) => { + for (let i = 0; i < promises.length; i++) { + Promise.resolve(promises[i]) + .then( + resolve, + (reason) => { + rejectedReasons.push(reason); + if (rejectedReasons.length === promises.length) { + reject(rejectedReasons); + } + } + ); + } + }); +} + +any([Promise.reject(1), Promise.resolve(2), 3]).then((res) => { + console.log(res); // 2 +}); + +any([Promise.reject(1), Promise.reject(2)]).catch((errors) => { + console.log(errors); // [1, 2] +}); diff --git a/day_1/day_1_6.ts b/day_1/day_1_6.ts new file mode 100644 index 0000000..476ca46 --- /dev/null +++ b/day_1/day_1_6.ts @@ -0,0 +1,26 @@ +function race(promises: any[]): Promise { + let settled = false; + return new Promise((resolve, reject) => { + for (const promise of promises) { + Promise.resolve(promise).then( + (value) => { + if (settled) return; + settled = true; + resolve(value); + }, + (reason) =>{ + if (settled) return; + settled = true; + reject(reason); + } + ); + } + }); +} + +race([Promise.resolve(2), 3]).then((res) => { + console.log(res); // 2 +}); +race([Promise.reject(1), Promise.resolve(2), 3]).then(console.log).catch((reason) => { + console.log(reason); // 1 +}); \ No newline at end of file From 4ea10f8d9b1f30de7a44179f9df7f73e34e1586a Mon Sep 17 00:00:00 2001 From: Eugene Maslovich Date: Sat, 2 Aug 2025 21:33:52 +0000 Subject: [PATCH 10/52] Add example demonstrating promise chaining and closure issue with var --- day_2/day_2_1.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/day_2/day_2_1.ts b/day_2/day_2_1.ts index e69de29..770c65b 100644 --- a/day_2/day_2_1.ts +++ b/day_2/day_2_1.ts @@ -0,0 +1,22 @@ +var val = Promise.resolve(1); + +var arr = [1, 2, 3]; + +for (var i = 0; i < arr.length; ++i) { + val = val.then((val) => val + arr[i]); +} + +val.then(console.log); // NaN + +/* +var i создаёт переменную в области видимости внешней функции, таким образом, +значение переменной всегда одно и то же для всех вызовов then. В данном +случае сначала цикл завершается, i === 3. Потом начинают выполняться +микротаски, вызываться колбэки then. Происходит сложение 1 + arr[3], что +равнозначно 1 + undefined == NaN. Затем в следующему колбэке +NaN + undefined == NaN и т.д. В итоге, получаем NaN. + +В случае использования let, для каждой итерации цикла фиксируется новая +лексическая среда с текущим значением переменной цикла согласно +спецификации 14.7.5.7 ForIn/OfBodyEvaluation. +*/ From 45b3a495efe37ddca6f83ef011ef37e672a8c32d Mon Sep 17 00:00:00 2001 From: ehpc Date: Sun, 3 Aug 2025 21:57:16 +0300 Subject: [PATCH 11/52] Update day 2 tasks: correct console output for setByPath and maxDepth examples, and implement myParseInt function --- day_2/day_2.md | 4 ++-- day_2/day_2_2.ts | 22 ++++++++++++++++++++ day_2/day_2_3.ts | 29 +++++++++++++++++++++++++++ day_2/day_2_4.ts | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 2 deletions(-) diff --git a/day_2/day_2.md b/day_2/day_2.md index 0926495..63798b7 100644 --- a/day_2/day_2.md +++ b/day_2/day_2.md @@ -26,7 +26,7 @@ const obj = {}; setByPath(obj, 'foo.bar', 1); setByPath(obj, 'foo.bla', 2); -console.log(obj); // {foo: {bar: day_1, bla: day_2}} +console.log(obj); // {foo: {bar: 1, bla: 2}} ``` ## 3. Нахождение максимальной глубины в дереве @@ -48,7 +48,7 @@ const obj = { ] }; -console.log(maxDepth(obj)); // day_2 +console.log(maxDepth(obj)); // 2 ``` ## 4. Реализация функции аналогичной parseInt diff --git a/day_2/day_2_2.ts b/day_2/day_2_2.ts index e69de29..b7df83c 100644 --- a/day_2/day_2_2.ts +++ b/day_2/day_2_2.ts @@ -0,0 +1,22 @@ +function setByPath(obj: Record, path: string, value: any) { + const keys = path.split('.'); + let currentObj = obj; + for (let i = 0; i < keys.length; i++) { + if (i === keys.length - 1) { + currentObj[keys[i]] = value; + } else if (!currentObj.hasOwnProperty(keys[i])) { + currentObj[keys[i]] = {}; + } + currentObj = currentObj[keys[i]]; + } +} + + +const obj = {}; + +setByPath(obj, 'foo.bar', 1); +setByPath(obj, 'foo.bla', 2); + +console.log(JSON.stringify(obj)); // {foo: {bar: 1, bla: 2}} + +export {}; diff --git a/day_2/day_2_3.ts b/day_2/day_2_3.ts index e69de29..e5cd7dd 100644 --- a/day_2/day_2_3.ts +++ b/day_2/day_2_3.ts @@ -0,0 +1,29 @@ +interface Tree { + value: any; + children?: Tree[]; +} + +function maxDepth(obj: Tree, rootPoints: 0 | 1 = 0): number { + if (!obj.children || obj.children.length === 0) { + return 1; + } + return rootPoints + Math.max(...obj.children.map((child) => maxDepth(child, 1))); +} + +const obj: Tree = { + value: 'foo', + children: [ + { + value: 'bar' + }, + + { + value: 'bla', + children: [{value: 'baz'}] + } + ] +}; + +console.log(maxDepth(obj)); // 2 + +export {}; diff --git a/day_2/day_2_4.ts b/day_2/day_2_4.ts index e69de29..fab1587 100644 --- a/day_2/day_2_4.ts +++ b/day_2/day_2_4.ts @@ -0,0 +1,52 @@ +function myParseInt(num: string, base: 2 | 10 | 16 = 10): number { + num = num.trim(); + const negative = num[0] === '-'; + const startingIndex = negative ? 1 : 0; + + const allowedSymbols = { + 2: {0: 0, 1: 1}, + 10: { + 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, + 7: 7, 8: 8, 9: 9, + }, + 16: { + 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, + 7: 7, 8: 8, 9: 9, 'A': 10, 'B': 11, 'C': 12, + 'D': 13, 'E': 14, 'F': 15, + }, + }; + + let usableLength = 0; + let startsWithBadSymbol = true; + for (let i = startingIndex; i < num.length; i++) { + const symbol = num[i].toUpperCase(); + if (!allowedSymbols[base].hasOwnProperty(symbol)) { + if (startsWithBadSymbol) { + return NaN; + } else { + break; + } + } + startsWithBadSymbol = false; + usableLength += 1; + } + if (usableLength < 1) { + return NaN; + } + + let currentNumber: number = 0; + let currentPlaceValuePower = usableLength - 1; + for (let i = startingIndex; i < startingIndex + usableLength; i++) { + const symbol = num[i].toUpperCase(); + const placeValue = Math.pow(base, currentPlaceValuePower); + currentNumber += allowedSymbols[base][symbol] * placeValue; + currentPlaceValuePower -= 1; + } + return negative ? -currentNumber : currentNumber; +} + +console.log(myParseInt('10')); // 10 +console.log(myParseInt('-10', 2)); // -2 +console.log(myParseInt('FFP', 16)); // 255 +console.log(myParseInt('--20')); // NaN +console.log(myParseInt('')); // NaN \ No newline at end of file From 5661a362d83876d39ac290d8292e354844fa6169 Mon Sep 17 00:00:00 2001 From: ehpc Date: Sun, 3 Aug 2025 23:39:30 +0300 Subject: [PATCH 12/52] Add event loop behavior example and explanations in day_3_1.ts --- day_3/day_3_1.ts | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/day_3/day_3_1.ts b/day_3/day_3_1.ts index e69de29..9cd6ade 100644 --- a/day_3/day_3_1.ts +++ b/day_3/day_3_1.ts @@ -0,0 +1,42 @@ +console.log('foo'); + +setTimeout(() => { + console.log('bar'); +}, 0); + +queueMicrotask(() => { + console.log('baz'); + Promise.resolve().then().then(() => console.log('ban')); +}); + +new Promise((resolve) => { + console.log('bla'); + resolve('baf'); +}).then(console.log); + +console.log('bak'); + +// foo bla bak baz baf ban bar + +/* +Поведение Event Loop зафиксировано в стандарте HTML 8.1.7.3 Processing model +https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model + +В ECMAScript 9.5 Jobs and Host Operations to Enqueue Jobs https://tc39.es/ecma262/#sec-jobs +также зафиксированы некоторые операции. + +Из этих спецификаций можно вывести примерный алгоритм работы Event Loop: + +1. Если есть хотя бы одна очередь тасок с доступной для выполнения таской: + 1. Выбрать из одной такой очереди (из какой именно, зависит от реализации) самую старую таску; + 2. Выполнить эту таску; + 3. Выполнить все микротаски, сначала самые старые; +2. Повторить всё сначала. + +Есть одна очередь микротасков, но много очередей тасков (зависит от реализации). +К микротаскам относятся колбэки промисов, queueMicrotask и MutationObserver. + +Что касается process.nextTick в node.js, то он будет выполнен сразу после +завершения любой операции Event Loop. + +*/ \ No newline at end of file From 01927d419c18341de9566303453ea3b15d18c9de Mon Sep 17 00:00:00 2001 From: ehpc Date: Mon, 4 Aug 2025 13:15:09 +0300 Subject: [PATCH 13/52] Implement mySetImmediate and myClearImmediate functions for microtask scheduling --- day_3/day_3_2.ts | 43 +++++++++++++++++++++++++++++++++++++++++++ day_3/day_3_3.ts | 17 +++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/day_3/day_3_2.ts b/day_3/day_3_2.ts index e69de29..8e1b194 100644 --- a/day_3/day_3_2.ts +++ b/day_3/day_3_2.ts @@ -0,0 +1,43 @@ +interface MySetImmediateStorage { + lastId: number, + activeIds: Set, +} + +const mySetImmediateStorage: MySetImmediateStorage = { + lastId: 0, + activeIds: new Set(), +}; + +function mySetImmediate(cb: Function): number { + const id = mySetImmediateStorage.lastId + 1; + mySetImmediateStorage.activeIds.add(id); + mySetImmediateStorage.lastId += 1; + queueMicrotask(() => { + if (mySetImmediateStorage.activeIds.has(id)) { + cb(); + } + }); + return id; +} + +function myClearImmediate(id: number) { + mySetImmediateStorage.activeIds.delete(id); +} + + +setTimeout(() => { + console.log(3); +}, 0); + +mySetImmediate(() => { + console.log(1); +}); + +const timer = mySetImmediate(() => { + console.log(2); +}); + +myClearImmediate(timer); + +// 1 3 + diff --git a/day_3/day_3_3.ts b/day_3/day_3_3.ts index e69de29..ae1fc70 100644 --- a/day_3/day_3_3.ts +++ b/day_3/day_3_3.ts @@ -0,0 +1,17 @@ +function curry(fn: Function): Function { + return function recur(...suppliedArgs) { + if (suppliedArgs.length >= fn.length) { + return fn(...suppliedArgs); + } + return function (...moreArgs) { + return recur(...suppliedArgs, ...moreArgs); + }; + }; +} + +const sum = curry((a, b, c, z) => a + b + c + z); + +console.log(sum(1, 2, 3, 4)); // 10; +console.log(sum(1)(2)(3)(4)); // 10; +console.log(sum(1)(2)(3, 4)); // 10; +console.log(sum(1)(2, 3, 4)); // 10; From 25571bbf671cefb05f4495f37cdb5c968d4a179c Mon Sep 17 00:00:00 2001 From: ehpc Date: Mon, 4 Aug 2025 14:22:06 +0300 Subject: [PATCH 14/52] Implement zip and generatorZip functions for iterable zipping --- day_3/day_3_4.ts | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/day_3/day_3_4.ts b/day_3/day_3_4.ts index e69de29..321d4e0 100644 --- a/day_3/day_3_4.ts +++ b/day_3/day_3_4.ts @@ -0,0 +1,46 @@ +function zip(...iterables: Iterable[]): IterableIterator { + const iterators = iterables.map(iterable => iterable[Symbol.iterator]()); + return { + [Symbol.iterator]() { + return this; + }, + + next() { + const zippedValue: any[] = []; + for (const iterator of iterators) { + const result = iterator.next(); + if (result.done) { + return { + done: true, + value: undefined, + }; + } + zippedValue.push(result.value); + } + return { + done: false, + value: zippedValue, + }; + } + } +} + +function generatorZip(...iterables: Iterable[]): IterableIterator { + const generator = function* () { + const iterators = iterables.map(iterable => iterable[Symbol.iterator]()); + while (true) { + const results = iterators.map(iterator => iterator.next()); + if (results.some(result => result.done)) break; + yield results.map(result => result.value); + } + }; + return generator(); +} + +const zipped = zip(new Set([1, 2]), ['a', 'b', 'z'], '...'); +console.log('1st run: ', ...zipped); // [1, 'a', '.'] [2, 'b', '.'] +console.log('2nd run: ', ...zipped); // nothing + +const generatorZipped = generatorZip(new Set([1, 2]), ['a', 'b', 'z'], '...'); +console.log('1st run: ', ...generatorZipped); // [1, 'a', '.'] [2, 'b', '.'] +console.log('2nd run: ', ...generatorZipped); // nothing From 25edbfa80486922f5a8e79bb35bba41649bc5347 Mon Sep 17 00:00:00 2001 From: ehpc Date: Tue, 5 Aug 2025 20:17:43 +0300 Subject: [PATCH 15/52] Implement promisify function and callback division example --- day_4/day_4.md | 2 +- day_4/day_4_1.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/day_4/day_4.md b/day_4/day_4.md index e0478f0..97abecc 100644 --- a/day_4/day_4.md +++ b/day_4/day_4.md @@ -17,7 +17,7 @@ function cbDiv(a, b, cb) { const promiseDiv = promisify(cbDiv); -promiseDiv(1, 2).then(console.log); // 0.day_5 +promiseDiv(1, 2).then(console.log); // 0.5 promiseDiv(1, 0).catch(console.log); // TypeError('Нельзя делить на 0') ``` diff --git a/day_4/day_4_1.ts b/day_4/day_4_1.ts index e69de29..04f2a05 100644 --- a/day_4/day_4_1.ts +++ b/day_4/day_4_1.ts @@ -0,0 +1,27 @@ +function promisify(fn: Function) { + return function (...args) { + const usableArgs = args.slice(0, fn.length - 1); + return new Promise((resolve, reject) => { + fn(...usableArgs, function (error, result) { + if (error) { + return reject(error); + } + resolve(result); + }); + }); + } +} + +function cbDiv(a, b, cb) { + if (b === 0) { + cb(new TypeError('Нельзя делить на 0')); + + } else { + cb(null, a / b); + } +} + +const promiseDiv = promisify(cbDiv); + +promiseDiv(1, 2).then(console.log); // 0.5 +promiseDiv(1, 0).catch(console.log); // TypeError('Нельзя делить на 0') From 89bbc9f88a8de7f21daac313a8af48659fc5657e Mon Sep 17 00:00:00 2001 From: ehpc Date: Wed, 6 Aug 2025 20:34:30 +0300 Subject: [PATCH 16/52] Update sorting function and enhance string search implementation --- day_4/day_4.md | 2 +- day_4/day_4_2.ts | 34 ++++++++++++++++++++++++++++++++++ day_4/day_4_3.ts | 34 ++++++++++++++++++++++++++++++++++ day_4/day_4_4.ts | 24 ++++++++++++++++++++++++ tsconfig.json | 2 +- 5 files changed, 94 insertions(+), 2 deletions(-) diff --git a/day_4/day_4.md b/day_4/day_4.md index 97abecc..4757423 100644 --- a/day_4/day_4.md +++ b/day_4/day_4.md @@ -41,7 +41,7 @@ console.log(num.add(2).mult(2).sub(1) - 5); // 18 Необходимо написать функцию sort, которая бы сортировала элементы со значением кратным двум. ```js -console.log(sort([7, 1, 4, 2, 9, 8])); // [7, day_1, day_2, day_4, 9, 8] +console.log(sort([7, 1, 4, 2, 9, 8])); // [7, 1, 2, 4, 9, 8] ``` ## 4. Поиск в массиве строк по заданной подстроке с пропуском символов diff --git a/day_4/day_4_2.ts b/day_4/day_4_2.ts index e69de29..54676e8 100644 --- a/day_4/day_4_2.ts +++ b/day_4/day_4_2.ts @@ -0,0 +1,34 @@ +class MyNumber { + #value: number; + + constructor(initialValue: number) { + this.#value = initialValue; + } + + add(value: number) { + this.#value += value; + return this; + } + + mult(value: number) { + this.#value *= value; + return this; + } + + sub(value: number) { + this.#value -= value; + return this; + } + + valueOf(): number { + return this.#value; + } + + [Symbol.toPrimitive](): number { + return this.#value; + } +} + +const num = new MyNumber(10); + +console.log(+num.add(2).mult(2).sub(1) - 5); // 18 \ No newline at end of file diff --git a/day_4/day_4_3.ts b/day_4/day_4_3.ts index e69de29..5996115 100644 --- a/day_4/day_4_3.ts +++ b/day_4/day_4_3.ts @@ -0,0 +1,34 @@ +function mySort(arr: number[]): number[] { + if (!arr.length) { + return arr; + } + + const evenNumbers: number[] = []; + const evenIndices: number[] = []; + for (let i = 0; i < arr.length; i++) { + if (arr[i] % 2 === 0) { + evenNumbers.push(arr[i]); + evenIndices.push(i); + } + } + + evenNumbers.sort(); + + const result = arr.slice(); + for (let i = 0; i < evenIndices.length; i++) { + const index = evenIndices[i]; + result[index] = evenNumbers[i]; + } + + return result; +} + +let res = mySort([7, 1, 4, 2, 9, 8]); +let expected = '7,1,2,4,9,8'; +console.assert(res.toString() === expected, `Wrong order (1): ${res}, expected: ${expected}`); + +res = mySort([1, 8, 3, 6, 5, 4]); +expected = '1,4,3,6,5,8'; +console.assert(res.toString() === expected, `Wrong order (2): ${res}, expected: ${expected}`); + +export {}; \ No newline at end of file diff --git a/day_4/day_4_4.ts b/day_4/day_4_4.ts index e69de29..d4bafff 100644 --- a/day_4/day_4_4.ts +++ b/day_4/day_4_4.ts @@ -0,0 +1,24 @@ +function find(needle: string, haystack: string[]): string[] { + const matches: string[] = []; + for (const hay of haystack) { + let hayIndex = 0; + let needleIndex = 0; + while (needleIndex < needle.length && hayIndex < hay.length) { + if (needle[needleIndex] === hay[hayIndex]) { + needleIndex += 1; + } + hayIndex += 1; + } + if (needleIndex === needle.length) { + matches.push(hay); + } + } + return matches; +} + +console.log(find('kbza', [ + 'kobezzza', + 'bob', + 'kibiza', + 'kobea' +])); // ['kobezzza', 'kibiza'] \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 1d5705c..1d89a84 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "es2024", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "libReplacement": true, /* Enable lib replacement. */ From 6eb729bd42d03efca0508faba5ef7a5bbbd64d4c Mon Sep 17 00:00:00 2001 From: ehpc Date: Wed, 6 Aug 2025 20:54:55 +0300 Subject: [PATCH 17/52] Implement debounce and throttle functions for function rate limiting --- day_5/day_5_1.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/day_5/day_5_1.ts b/day_5/day_5_1.ts index e69de29..3254c99 100644 --- a/day_5/day_5_1.ts +++ b/day_5/day_5_1.ts @@ -0,0 +1,40 @@ +function debounce(fn: Function, delay: number): Function { + let timerId; + return function (...args) { + clearTimeout(timerId); + timerId = setTimeout(() => fn(...args), delay); + }; +} + +function laugh() { + console.log('Ha-ha!') +} + +const debouncedLaugh = debounce(laugh, 300); + +debouncedLaugh(); +debouncedLaugh(); +debouncedLaugh(); +debouncedLaugh(); +debouncedLaugh(); + +function throttle(fn: Function, delay: number): Function { + let waiting = false; + return function (...args) { + if (!waiting) { + fn(...args); + waiting = true; + setTimeout(() => { + waiting = false; + }, delay); + } + }; +} + +const throttledLaugh = throttle(laugh, 300); + +throttledLaugh(); +throttledLaugh(); +throttledLaugh(); +throttledLaugh(); +throttledLaugh(); From 02334716a25f62681c068d21131bf99558974333 Mon Sep 17 00:00:00 2001 From: ehpc Date: Wed, 6 Aug 2025 23:31:22 +0300 Subject: [PATCH 18/52] Implement EventEmitter class with on, once, off, and emit methods for event handling --- day_5/day_5_2.ts | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/day_5/day_5_2.ts b/day_5/day_5_2.ts index e69de29..45f7e26 100644 --- a/day_5/day_5_2.ts +++ b/day_5/day_5_2.ts @@ -0,0 +1,60 @@ +interface Listeners { + once: Set; + on: Set; +} + +class EventEmitter { + #listeners: Map = new Map(); + + on(event: string, listener: Function) { + if (!this.#listeners.has(event)) { + this.#listeners.set(event, { once: new Set(), on: new Set() }); + } + this.#listeners.get(event)?.on.add(listener); + return this; + } + + once(event: string, listener: Function) { + if (!this.#listeners.has(event)) { + this.#listeners.set(event, { once: new Set(), on: new Set() }); + } + this.#listeners.get(event)?.once.add(listener); + return this; + } + + off(event: string, listener?: Function) { + const listeners = this.#listeners.get(event); + if (!listeners) return; + if (listener) { + listeners.once.delete(listener); + listeners.on.delete(listener); + } else { + this.#listeners.delete(event); + } + } + + emit(event: string, ...args) { + const listeners = this.#listeners.get(event); + if (!listeners) return; + listeners.once.forEach(listener => listener(...args)); + listeners.once.clear(); + listeners.on.forEach(listener => listener(...args)); + } +} + +const ee = new EventEmitter(); + +ee.once('foo', console.log); // Сработает только один раз + +ee.emit('foo', 1); +ee.emit('foo', 2); + +ee.off('foo', console.log); // Отмена конкретного обработчика события по ссылке + +ee.on('foo', console.log); // Сработает много раз +ee.emit('foo', 3); +ee.emit('foo', 4); + +ee.off('foo'); // Отмена всех обработчиков этого события + +ee.emit('foo', 5); \ No newline at end of file From 133ef16b1b11f22e86f7aa25419fb85b6248e394 Mon Sep 17 00:00:00 2001 From: ehpc Date: Thu, 7 Aug 2025 10:41:13 +0300 Subject: [PATCH 19/52] Implement flat and flatNonRecursive functions for array flattening --- day_5/day_5_3.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/day_5/day_5_3.ts b/day_5/day_5_3.ts index e69de29..ea2fa8e 100644 --- a/day_5/day_5_3.ts +++ b/day_5/day_5_3.ts @@ -0,0 +1,40 @@ +function flat(arr: any[], depth: number = 1): any[] { + if (depth <= 0) { + return arr; + } + return arr.reduce((acc, el) => { + if (Array.isArray(el)) { + return acc.concat(flat(el, depth - 1)); + } + acc.push(el); + return acc; + }, []); +} + +console.log(JSON.stringify(flat([[1, 2], [[1]], 2]))); // [1, 2, [1], 2] +console.log(JSON.stringify(flat([[1, 2], [[1]], 2], 2))); // [1, 2, 1, 2] + +function flatNonRecursive(arr: any[], depth: number = 1): any[] { + const stack: [any, number][] = []; + for (let i = arr.length - 1; i >= 0; i--) { + stack.push([arr[i], 0]); + } + + const result: any[] = []; + + while (stack.length > 0) { + const [item, currentDepth] = stack.pop()!; + if (Array.isArray(item) && currentDepth < depth) { + for (let i = item.length - 1; i >= 0; i--) { + stack.push([item[i], currentDepth + 1]); + } + } else { + result.push(item); + } + } + + return result; +} + +console.log(JSON.stringify(flatNonRecursive([[1, 2], [[1]], 2]))); // [1, 2, [1], 2] +console.log(JSON.stringify(flatNonRecursive([[1, 2], [[1]], 2], 2))); // [1, 2, 1, 2] \ No newline at end of file From 2a0ecf1be325593843b206510629ee125abe8c2a Mon Sep 17 00:00:00 2001 From: ehpc Date: Thu, 7 Aug 2025 21:48:49 +0300 Subject: [PATCH 20/52] Implement topological sorting function for dependency resolution --- day_5/day_5_4.ts | 112 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/day_5/day_5_4.ts b/day_5/day_5_4.ts index e69de29..cb6832a 100644 --- a/day_5/day_5_4.ts +++ b/day_5/day_5_4.ts @@ -0,0 +1,112 @@ +type GraphNode = Record; +type Graph = GraphNode[]; +type MapperFn = (obj: T) => [string, string[] | undefined]; + +function topoSort(graph: T[], mapper: MapperFn) { + const depsByVertex = new Map(); + const nodeByVertex = new Map(); + const verticies: string[] = []; + for (const node of graph) { + const [vertex, deps] = mapper(node); + depsByVertex.set(vertex, deps ?? []); + nodeByVertex.set(vertex, node); + verticies.push(vertex); + } + const visiting = new Set(); + const visited = new Set(); + + const result: T[] = []; + + function visit(vertex: string) { + if (visiting.has(vertex)) throw new Error(`Cyclic dependency detected at "${vertex}"`); + + visiting.add(vertex); + const deps = depsByVertex.get(vertex)!; + if (deps.length) { + for (const dep of deps) { + if (!visited.has(dep)) { + visit(dep); + } + } + } + visiting.delete(vertex); + + if (!visited.has(vertex)) { + visited.add(vertex); + result.push(nodeByVertex.get(vertex)!); + } + } + + for (const vertex of verticies) { + visit(vertex); + } + + return result; +} + +const skills = [ + { + name: 'fireball', + need: ['firehands', 'magicspell'] + }, + + { + name: 'firehands' + }, + + { + name: 'magicspell' + }, + + { + name: 'inferno', + need: ['fireball', 'crazymind'] + }, + + { + name: 'crazymind', + need: ['magicspell'] + } +]; + +/* +[ + { + name: 'firehands' + }, + + { + name: 'magicspell' + }, + + { + name: 'crazymind', + need: ['magicspell'] + } + + { + name: 'fireball', + need: ['firehands', 'magicspell'] + }, + + { + name: 'inferno', + need: ['fireball', 'crazymind'] + } +] +*/ +console.log(JSON.stringify( + topoSort(skills, ({name, need}) => [name, need]), + undefined, + ' ' +)); + +/* +firehands magicspell + \ / \ + v v v + fireball crazymind + \ / + v v + inferno +*/ \ No newline at end of file From 08a80684e39f4c8ff76482c71eb7cc8dcae17381 Mon Sep 17 00:00:00 2001 From: ehpc Date: Fri, 8 Aug 2025 13:29:02 +0300 Subject: [PATCH 21/52] Implement User and UserBuilder classes for user creation and management --- day_6/day_6_1.ts | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/day_6/day_6_1.ts b/day_6/day_6_1.ts index e69de29..9c68a0a 100644 --- a/day_6/day_6_1.ts +++ b/day_6/day_6_1.ts @@ -0,0 +1,58 @@ +class User { + name: string; + age: string; + skills: string; + constructor(params) { + this.name = params.name; + this.age = params.age; + this.skills = params.skills; + } + + static name(name: string) { + return new UserBuilder().name(name); + } + + static age(age: number) { + return new UserBuilder().age(age); + } + + static skills(skills: string[]) { + return new UserBuilder().skills(skills); + } +} + +class UserBuilder { + #name: string = ''; + #age: number = 0; + #skills: string[] = []; + + name(name: string) { + this.#name = name; + return this; + } + + age(age: number) { + this.#age = age; + return this; + } + + skills(skills: string[]) { + this.#skills = skills; + return this; + } + + create() { + return new User({ + name: this.#name, + age: this.#age, + skills: this.#skills, + }); + } +} + + +let user1Partial = User.name('Bob').age(47).skills(['Coding']); +let user2 = User.name('Alex').age(13).skills(['None']).create(); +let user1 = user1Partial.create(); +console.log(JSON.stringify(user1)); // User({name: 'Bob', age: 47, skills: ['Coding']}) +console.log(JSON.stringify(user2)); // User({name: 'Alex', age: 13, skills: ['None']}) From 153d7ed2338f3701ba34e0fa35de7b65a5ab74b9 Mon Sep 17 00:00:00 2001 From: ehpc Date: Fri, 8 Aug 2025 14:09:11 +0300 Subject: [PATCH 22/52] Implement breadth-first search (BFS) function for tree traversal --- day_6/day_6_2.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/day_6/day_6_2.ts b/day_6/day_6_2.ts index e69de29..a3235d9 100644 --- a/day_6/day_6_2.ts +++ b/day_6/day_6_2.ts @@ -0,0 +1,33 @@ +interface Tree { + value: number; + children?: Tree[]; +} + +function bfs(tree: Tree): number[] { + const queue: Tree[] = [tree]; + const result: number[] = []; + while (queue.length > 0) { + const node = queue.shift()!; + result.push(node.value); + if (!node.children) continue; + for (const child of node.children) { + queue.push(child); + } + } + return result; +} + +const tree = { + value: 1, + children: [ + { + value: 2, + children: [{value: 4}] + }, + { + value: 3 + } + ] +}; + +console.log(bfs(tree)); // 1 2 3 4 \ No newline at end of file From 666237e20f4df492f7e0007d760712721d888ec4 Mon Sep 17 00:00:00 2001 From: ehpc Date: Fri, 8 Aug 2025 15:35:52 +0300 Subject: [PATCH 23/52] Implement dasherize and dasherizeStepByStep functions for string formatting --- day_6/day_6_3.ts | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/day_6/day_6_3.ts b/day_6/day_6_3.ts index e69de29..fd174be 100644 --- a/day_6/day_6_3.ts +++ b/day_6/day_6_3.ts @@ -0,0 +1,44 @@ +function dasherize(input: string): string { + return input + .replace(/([a-z])([A-Z])|([A-Z])([A-Z][a-z])/g, '$1$3-$2$4') + .toLowerCase(); +} + +console.log(dasherize('createDocumentFragment')); // 'create-document-fragment' +console.log(dasherize('SuperMAN')); // 'super-man' +console.log(dasherize('VirtualDOMFragment')); // 'virtual-dom-fragment' + +function dasherizeStepByStep(input: string): string { + let result = ''; + let uppercasedSeq = ''; + for (const char of input) { + if (char.toUpperCase() === char) { + uppercasedSeq += char.toLowerCase(); + } else { + if (uppercasedSeq) { + if (uppercasedSeq.length === 1) { + if (result) { + result += '-'; + } + result += uppercasedSeq; + } else { + const firstWord = uppercasedSeq.slice(0, -1); + if (result) { + result += '-'; + } + result += firstWord + '-' + uppercasedSeq[uppercasedSeq.length - 1]; + } + } + uppercasedSeq = ''; + result += char; + } + } + if (uppercasedSeq) { + result += '-' + uppercasedSeq; + } + return result; +} + +console.log(dasherizeStepByStep('createDocumentFragment')); // 'create-document-fragment' +console.log(dasherizeStepByStep('SuperMAN')); // 'super-man' +console.log(dasherizeStepByStep('VirtualDOMFragment')); // 'virtual-dom-fragment' \ No newline at end of file From 7f0239de6c13fe95a3bd119ddb221467ff4ad93e Mon Sep 17 00:00:00 2001 From: ehpc Date: Fri, 8 Aug 2025 16:30:41 +0300 Subject: [PATCH 24/52] Implement waterfall function for asynchronous control flow --- day_6/day_6_4.ts | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/day_6/day_6_4.ts b/day_6/day_6_4.ts index e69de29..96f60c9 100644 --- a/day_6/day_6_4.ts +++ b/day_6/day_6_4.ts @@ -0,0 +1,71 @@ +function waterfall(fns: Iterable, resultFn: Function) { + const iter = fns[Symbol.iterator](); + function next(forwardedArgs: any[]) { + const { value: fn, done } = iter.next(); + if (done) { + return resultFn(null, ...forwardedArgs); + } + fn(...forwardedArgs, (err, ...args) => { + if (err) { + return resultFn(err); + } + next(args); + }); + } + next([]); +} + +waterfall([ + (cb) => { + cb(null, 'one', 'two'); + }, + + (arg1, arg2, cb) => { + console.log(arg1); // one + console.log(arg2); // two + cb(null, 'three'); + }, + + (arg1, cb) => { + console.log(arg1); // three + cb(null, 'done'); + } +], (err, result) => { + console.log(result); // done +}); + +waterfall(new Set([ + (cb) => { + cb('ha-ha!'); + }, + + (arg1, cb) => { + cb(null, 'done'); + } +]), (err, result) => { + console.log(err); // ha-ha! + console.log(result); // undefined +}); + +waterfall([ + (cb) => { + setTimeout(() => cb(null, 'one', 'two'), 10); + }, + + (arg1, arg2, cb) => { + setTimeout(() => { + console.log(arg1); // one + console.log(arg2); // two + cb(null, 'three'); + }, 20); + }, + + (arg1, cb) => { + setTimeout(() => { + console.log(arg1); // three + cb(null, 'done'); + }, 30); + } +], (err, result) => { + console.log(result); // done +}); From ff3e45fa6c5e6b8a3c63fe78260113fbce39b96a Mon Sep 17 00:00:00 2001 From: ehpc Date: Sat, 9 Aug 2025 19:17:30 +0300 Subject: [PATCH 25/52] Implement functions for array difference, async semaphore, object serialization, and recursive expression evaluation --- day_7/day_7_1.ts | 12 ++++ day_7/day_7_2.ts | 23 ++++++++ day_7/day_7_3.ts | 49 ++++++++++++++++ day_7/day_7_4.ts | 146 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 230 insertions(+) diff --git a/day_7/day_7_1.ts b/day_7/day_7_1.ts index e69de29..5d95701 100644 --- a/day_7/day_7_1.ts +++ b/day_7/day_7_1.ts @@ -0,0 +1,12 @@ +function diff(arrA: number[], arrB: number[]): number[] { + const setB = new Set(arrB); + const result: number[] = []; + for (const x of arrA) { + if (!setB.has(x)) { + result.push(x); + } + } + return result; +} + +console.log(diff([1, 2, 3, 4, 5], [3, 4, 1])); // [2, 5] \ No newline at end of file diff --git a/day_7/day_7_2.ts b/day_7/day_7_2.ts index e69de29..8e3bfff 100644 --- a/day_7/day_7_2.ts +++ b/day_7/day_7_2.ts @@ -0,0 +1,23 @@ +type SemaphoreFn = (flag?: T) => void; + +function createsAsyncSemaphore(fn: Function, ...requiredFlags: T[]): SemaphoreFn { + const raisedFlags = new Set(); + return function (flag?: T) { + if (flag && requiredFlags.includes(flag)) { + raisedFlags.add(flag); + if (raisedFlags.size === requiredFlags.length) { + fn(); + } + } + }; +} + +const semaphore = createsAsyncSemaphore(() => { + console.log('Boom!'); +}, 'foo', 'bar'); + +setTimeout(() => semaphore('foo'), 0); +setTimeout(() => semaphore('bar'), 10); // 'Boom!' + +// Эта функция не будет выполняться +setTimeout(() => semaphore(), 50); \ No newline at end of file diff --git a/day_7/day_7_3.ts b/day_7/day_7_3.ts index e69de29..1e6874a 100644 --- a/day_7/day_7_3.ts +++ b/day_7/day_7_3.ts @@ -0,0 +1,49 @@ +import { inspect } from 'util'; + +function serialize(obj: any): string { + return JSON.stringify(obj, function (key, value) { + if (this[key] instanceof Date) { + return `:s:d:${value.valueOf()}`; + } else if (this[key] instanceof Set) { + return `:s:s:${serialize([...value])}`; + } else if (this[key] instanceof Map) { + return `:s:m:${serialize([...value])}`; + } + return value; + }); +} + +function parse(objStr: string): any { + return JSON.parse(objStr, function (key, value) { + if (typeof value === 'string' && value.startsWith(':s')) { + const type = value.slice(3, 4); + const data = value.slice(5); + if (type === 'd') { + return new Date(data); + } else if (type === 's') { + return new Set(parse(data)); + } else if (type === 'm') { + return new Map(parse(data)); + } + } + return value; + }); +} + +const original = { + myDate: new Date(), + mySet: new Set([1, 2, 3]), + mySet2: new Set([{insideSet: new Set([1, 2])}, 3]), + myMap: new Map([ + [new Date(), {a: 1}] + ]) +}; + +console.log(inspect(original, false, 10)); + +const str = serialize(original); +console.log(str); + +// Объект должен иметь аналогичную структуру с original +const parsed = parse(str); +console.log(inspect(parsed, false, 10)); diff --git a/day_7/day_7_4.ts b/day_7/day_7_4.ts index e69de29..d0b1b44 100644 --- a/day_7/day_7_4.ts +++ b/day_7/day_7_4.ts @@ -0,0 +1,146 @@ +type Token = number | string; + +function tokenize(expression: string): Token[] { + const tokens: Token[] = []; + let i = 0; + while (i < expression.length) { + const char = expression[i]; + if (/\s/.test(char)) { + i += 1; + continue; + } else if (/\d/.test(char)) { + let token = ''; + while (i < expression.length && /\d/.test(expression[i])) { + token += expression[i]; + i += 1; + } + tokens.push(Number(token)); + } else if ('+-*/()'.includes(char)) { + tokens.push(char); + i += 1; + } else { + throw new Error(`Unexpected character '${char}' at position ${i}.`); + } + } + return tokens; +} + +function calcRecursive(expression: string): number { + const tokens = tokenize(expression); + + let pos = 0; + + function parseExpression(): number { + let lhs = parseTerm(); + while (tokens[pos] === '+' || tokens[pos] === '-') { + const op = tokens[pos]; + pos += 1; + const rhs = parseTerm(); + if (op === '+') lhs += rhs; + else lhs -= rhs; + } + return lhs; + } + + function parseTerm(): number { + let lhs = parseFactor(); + while (tokens[pos] === '*' || tokens[pos] === '/') { + const op = tokens[pos]; + pos += 1; + const rhs = parseFactor(); + if (op === '*') lhs *= rhs; + else lhs /= rhs; + } + return lhs; + } + + function parseFactor(): number { + const token = tokens[pos]; + if (typeof token === 'number') { + pos += 1; + return token; + } else if (token === '(') { + pos += 1; + const value = parseExpression(); + if (tokens[pos] !== ')') throw new Error(`Expected ")", got: ${tokens[pos]}.`); + pos += 1; + return value; + } + throw new Error(`Unexpected token: ${token}.`); + } + + const result = parseExpression(); + if (pos !== tokens.length) { + throw new Error(`Unexpected token at the end: ${tokens[pos]}.`); + } + return result; +} + +console.log(calcRecursive('2+4*3')); // 14 +console.log(calcRPN('(2+3)')); // 5 +console.log(calcRecursive('4-2')); // 2 +console.log(calcRecursive('5 * (2 + 3)')); // 25 +console.log(calcRecursive('2*3*4')); // 24 +console.log(calcRecursive('12+5*2/(60-58)-5')); // 12 + +function infixToRPN(tokens: Token[]): Token[] { + const precedenceByToken = { '+': 1, '-': 1, '*': 2, '/': 2 }; + const result: Token[] = []; + const stack: Token[] = []; + for (const token of tokens) { + if (typeof token === 'number') { + result.push(token); + } else if (token === '+' || token === '-' || token === '*' || token === '/') { + while (stack.length) { + const peeked = stack[stack.length - 1]; + if (peeked !== '(' && precedenceByToken[peeked] >= precedenceByToken[token]) { + result.push(peeked); + stack.pop(); + } else break; + } + stack.push(token); + } else if (token === '(') { + stack.push(token); + } else if (token === ')') { + while (stack.length) { + const top = stack.pop()!; + if (top === '(') break; + else result.push(top); + } + } else throw new Error(`Unexpected token: ${token}.`); + } + result.push(...stack.reverse()); + return result; +} + +function calcRPN(expression: string): number { + const tokens = tokenize(expression); + const rpn = infixToRPN(tokens); + const stack: Token[] = []; + for (const token of rpn) { + if (token === '*' || token === '/' || token === '+' || token === '-') { + if (stack.length < 2) throw new Error('Wrong RPN!'); + const rhs = stack.pop()!; + const lhs = stack.pop()!; + if (typeof rhs !== 'number' || typeof lhs !== 'number') throw new Error('Wrong token type, expected number!'); + if (token === '*') stack.push(lhs * rhs); + else if (token === '/') stack.push(lhs / rhs); + else if (token === '+') stack.push(lhs + rhs); + else if (token === '-') stack.push(lhs - rhs); + else throw new Error(`Unexpected token: ${token}.`); + } else { + stack.push(token); + } + } + if (stack.length !== 1) throw new Error(`Unexpected remaining stack: ${stack}.`); + const result = stack.pop(); + if (typeof result !== 'number') throw new Error('Wrong result type, expected number!'); + return result; +} + +console.log(calcRPN('2+4*3')); // 14 +console.log(calcRPN('(2+3)')); // 5 +console.log(calcRPN('4-2')); // 2 +console.log(calcRPN('5 * (2 + 3)')); // 25 +console.log(calcRPN('2*3*4')); // 24 +console.log(calcRPN('12+5*2/(60-58)-5')); // 12 \ No newline at end of file From 38c895e191c1c932981b6646c603c1fab0ff7d04 Mon Sep 17 00:00:00 2001 From: ehpc Date: Sun, 10 Aug 2025 10:44:21 +0300 Subject: [PATCH 26/52] Implement throttle function to limit the execution rate of a given function --- day_8/day_8_1.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/day_8/day_8_1.ts b/day_8/day_8_1.ts index e69de29..a1b8f58 100644 --- a/day_8/day_8_1.ts +++ b/day_8/day_8_1.ts @@ -0,0 +1,30 @@ +function throttle any>( + fn: T, + delay: number +): (...args: Parameters) => ReturnType | undefined { + let blocked = false; + return function (this: any, ...args: Parameters): ReturnType | undefined { + if (!blocked) { + blocked = true; + const result = fn.apply(this, args); + setTimeout(() => { + blocked = false; + }, delay); + return result; + } + }; +} + +function laugh() { + console.log('Ha-ha!') +} + +const throttledLaugh = throttle(laugh, 300); + +throttledLaugh(); +throttledLaugh(); +throttledLaugh(); +throttledLaugh(); +throttledLaugh(); + +export {}; \ No newline at end of file From 4063e435f1b845d8a64e4f61784d58544294cf94 Mon Sep 17 00:00:00 2001 From: ehpc Date: Sun, 10 Aug 2025 13:23:44 +0300 Subject: [PATCH 27/52] Implement deep comparison function for objects and arrays --- day_8/day_8_2.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/day_8/day_8_2.ts b/day_8/day_8_2.ts index e69de29..7e69f44 100644 --- a/day_8/day_8_2.ts +++ b/day_8/day_8_2.ts @@ -0,0 +1,27 @@ +function compare(objA: any, objB: any): boolean { + if (objA === objB) return true; + if (objA === null || objB === null) return false; + if (typeof objA !== typeof objB) return false; + if (typeof objA !== 'object') return objA === objB; + + if (Array.isArray(objA) !== Array.isArray(objB)) return false; + if (Array.isArray(objA)) { + if (objA.length !== objB.length) { + return false; + } + return objA.every((item, i) => compare(item, objB[i])); + } + + const keysA = Object.keys(objA); + const keysB = new Set(Object.keys(objB)); + if (keysA.length !== keysB.size) return false; + + return keysA.every(key => keysB.has(key) && compare(objA[key], objB[key])); +} + +console.log(compare({}, null)); // false +console.log(compare({a: null}, {b: null})); // false +console.log(compare({a: 1, b: [1, 2]}, {a: 1, b: [1, 2, 3]})); // false +console.log(compare(null, null)); // true +console.log(compare({a: undefined}, {a: undefined})); // true +console.log(compare({a: 1, b: [1, 2, 3]}, {a: 1, b: [1, 2, 3]})); // true \ No newline at end of file From f715e848cc099bcd114939c5bdb057786c33d0a0 Mon Sep 17 00:00:00 2001 From: ehpc Date: Sun, 10 Aug 2025 14:49:52 +0300 Subject: [PATCH 28/52] Implement function to filter unique strings from an array --- day_8/day_8_3.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/day_8/day_8_3.ts b/day_8/day_8_3.ts index e69de29..b3a5081 100644 --- a/day_8/day_8_3.ts +++ b/day_8/day_8_3.ts @@ -0,0 +1,12 @@ +function getUniqueStrs(strs: string[]): string[] { + const anagrams = new Map(); + const keys: string[] = []; + for (const str of strs) { + const key = str.split('').toSorted().join(''); + keys.push(key); + anagrams.set(key, (anagrams.get(key) || 0) + 1); + } + return strs.filter((_, i) => anagrams.get(keys[i]) === 1); +} + +console.log(getUniqueStrs(['atoe', 'otea', 'ben', 'enb', 'baz', 'foo'])); // ['baz', 'foo'] \ No newline at end of file From 91a391a7fefa172a35a8d0ebb0f8e1856f832799 Mon Sep 17 00:00:00 2001 From: ehpc Date: Sun, 10 Aug 2025 19:58:20 +0300 Subject: [PATCH 29/52] Implement queue data structure with push and pop methods --- day_8/day_8_4.ts | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/day_8/day_8_4.ts b/day_8/day_8_4.ts index e69de29..00aebd4 100644 --- a/day_8/day_8_4.ts +++ b/day_8/day_8_4.ts @@ -0,0 +1,44 @@ +class QueueItem { + value: number; + next: QueueItem | null = null; + + constructor(value: number) { + this.value = value; + } +} + +class Queue { + #head: QueueItem | null = null; + #tail: QueueItem | null = null; + + push(value: number) { + const newItem = new QueueItem(value); + if (this.#tail) { + this.#tail.next = newItem; + this.#tail = newItem; + } else { + this.#head = newItem; + this.#tail = newItem; + } + } + + pop(): number | undefined { + if (this.#head) { + const item = this.#head; + this.#head = item.next; + if (!this.#head) this.#tail = null; + return item.value; + } + } +} + +const queue = new Queue(); + +queue.push(1); +queue.push(2); +queue.push(3); + +console.log(queue.pop()); // 1 +console.log(queue.pop()); // 2 +console.log(queue.pop()); // 3 +console.log(queue.pop()); // undefined \ No newline at end of file From a56ca5be5fd697aed9953f900662739c27858e2d Mon Sep 17 00:00:00 2001 From: ehpc Date: Sun, 10 Aug 2025 20:17:00 +0300 Subject: [PATCH 30/52] Implement binary search function with comparator --- day_9/day_9_1.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/day_9/day_9_1.ts b/day_9/day_9_1.ts index e69de29..8e6e5e0 100644 --- a/day_9/day_9_1.ts +++ b/day_9/day_9_1.ts @@ -0,0 +1,16 @@ +function bisect(arr: number[], comparator: (val: number) => number): number { + let left = 0; + let right = arr.length - 1; + while (left <= right) { + let middle = left + ((right - left) >> 1) + let middleResult = comparator(arr[middle]); + if (middleResult === 0) return middle; + if (middleResult < 0) right = middle - 1; + else left = middle + 1; + } + return - 1; +} + +console.log(bisect([1, 2, 3, 4, 5, 6, 7], (val) => 4 - val)); // 3 +console.log(bisect([1, 2, 3, 4, 5, 6, 7], (val) => 234 - val)); // -1 +console.log(bisect([], (val) => 234 - val)); // -1 \ No newline at end of file From d349c0df25b51e5ce8fe9baf6b9eb2f048ae0640 Mon Sep 17 00:00:00 2001 From: ehpc Date: Mon, 11 Aug 2025 10:26:54 +0300 Subject: [PATCH 31/52] Implement retry function with customizable options for retries and delays --- day_9/day_9_2.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/day_9/day_9_2.ts b/day_9/day_9_2.ts index e69de29..888f5ff 100644 --- a/day_9/day_9_2.ts +++ b/day_9/day_9_2.ts @@ -0,0 +1,24 @@ +interface RetryOptions { + retry: number; + delay: (retryNumber: number) => number; +} + +type RetryFn = (...args: any[]) => Promise; + +function retry, R>(fn: T, options: RetryOptions): Promise { + return new Promise((resolve, reject) => { + let retries = 0; + function tryExecute() { + fn().then(resolve).catch((err) => { + console.log(retries); + if (retries >= options.retry) return reject(err); + retries += 1; + const delay = options.delay(retries); + setTimeout(tryExecute, delay); + }); + } + tryExecute(); + }); +} + +retry(() => fetch('//some-data'), {retry: 3, delay: (n) => n * 100}).then(console.log, console.error); \ No newline at end of file From c8281583b2a8fd1693fa9fefffb599acefa30698 Mon Sep 17 00:00:00 2001 From: ehpc Date: Mon, 11 Aug 2025 13:03:52 +0300 Subject: [PATCH 32/52] Implement LRU Cache with set, get, and has methods --- day_9/day_9_3.ts | 96 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/day_9/day_9_3.ts b/day_9/day_9_3.ts index e69de29..2cccfbf 100644 --- a/day_9/day_9_3.ts +++ b/day_9/day_9_3.ts @@ -0,0 +1,96 @@ +class LRUNode { + next: LRUNode | null = null; + prev: LRUNode | null = null; + key: string; + value: number; + + constructor (key: string, value: number) { + this.key = key; + this.value = value; + } +} + +class LRUCache { + #size: number; + #used: number = 0; + #head: LRUNode | null = null; + #tail: LRUNode | null = null; + #nodeByKey: Map = new Map(); + + constructor(size: number) { + this.#size = size; + } + + #moveToHead(node: LRUNode) { + if (this.#head === node) return; + + if (!this.#head) { + this.#head = node; + this.#tail = node; + return; + } + + if (node === this.#tail) this.#tail = node.prev; + + if (node.prev) node.prev.next = node.next; + if (node.next) node.next.prev = node.prev; + + node.prev = null; + node.next = this.#head; + this.#head.prev = node; + this.#head = node; + } + + set(key: string, value: number) { + if (this.#nodeByKey.has(key)) { + const node = this.#nodeByKey.get(key)!; + node.value = value; + this.#moveToHead(node); + } else { + const node = new LRUNode(key, value); + this.#nodeByKey.set(key, node); + this.#used += 1; + this.#moveToHead(node); + } + + if (this.#used > this.#size) { + const tail = this.#tail; + if (tail) { + this.#tail = tail.prev; + if (tail.prev) { + tail.prev.next = null; + } else { + this.#head = null; + } + this.#nodeByKey.delete(tail.key); + this.#used -= 1; + } + } + } + + get(key: string): number | undefined { + const node = this.#nodeByKey.get(key); + if (!node) return; + + this.#moveToHead(node); + + return node.value; + } + + has(key: string): boolean { + return this.#nodeByKey.has(key); + } +} + + +const cache = new LRUCache(3); // Размер кеша + +cache.set('key1', 1); +cache.set('key2', 2); +cache.set('key3', 3); + +console.log(cache.get('key1')); // 1 + +cache.set('key4', 4); + +console.log(cache.has('key2')); // false From e54acc2df673259f91a5be372c64d2a467972593 Mon Sep 17 00:00:00 2001 From: ehpc Date: Mon, 11 Aug 2025 13:16:22 +0300 Subject: [PATCH 33/52] Implement function to collapse consecutive duplicate characters in a string --- day_9/day_9.md | 2 +- day_9/day_9_4.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/day_9/day_9.md b/day_9/day_9.md index ea94501..193cd86 100644 --- a/day_9/day_9.md +++ b/day_9/day_9.md @@ -42,5 +42,5 @@ console.log(cache.has('key2')); // false Необходимо написать функцию, которая бы принимала бы строку и "схлопывала" бы все подряд идущие повторения. ```js -console.log(zipStr('abbaabbafffbezza')); // abafbeza +console.log(zipStr('abbaabbafffbezza')); // ababafbeza ``` diff --git a/day_9/day_9_4.ts b/day_9/day_9_4.ts index e69de29..5efee83 100644 --- a/day_9/day_9_4.ts +++ b/day_9/day_9_4.ts @@ -0,0 +1,13 @@ +function zipStr(str: string): string { + const result: string[] = []; + let prev = ''; + for (const char of str) { + if (prev !== char) { + result.push(char); + prev = char; + } + } + return result.join(''); +} + +console.log(zipStr('abbaabbafffbezza')); // ababafbeza \ No newline at end of file From f8b657cd6f9bfb48cc915f2cb57bd487826eee6c Mon Sep 17 00:00:00 2001 From: ehpc Date: Mon, 11 Aug 2025 14:15:18 +0300 Subject: [PATCH 34/52] Implement function to reduce an array of numbers into ranges --- day_10/day_10_1.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/day_10/day_10_1.ts b/day_10/day_10_1.ts index e69de29..c6f1641 100644 --- a/day_10/day_10_1.ts +++ b/day_10/day_10_1.ts @@ -0,0 +1,20 @@ +function reduce(arr: number[]): string { + if (!arr.length) return ''; + + const sorted = arr.toSorted((a, b) => a - b); + let result: string[] = []; + let start = sorted[0]; + for (let i = 1; i <= sorted.length; i++) { + if (i === sorted.length || sorted[i] - sorted[i - 1] !== 1) { + const end = sorted[i - 1]; + if (start === end) result.push(`${start}`); + else result.push(`${start}-${end}`); + start = sorted[i]; + } + + } + return result.join(', '); +} + +console.log(reduce([1, 3, 6, 8, 7, 11, 45, 46, 2])); // 1-3, 6-8, 11, 45-46 +console.log(reduce([1, 3, 6, 8, 7, 11, 45, 80, 46, 2])); // 1-3, 6-8, 11, 45-46, 80 \ No newline at end of file From 2ff4aff623971e78f4b70968472c5aa77145d345 Mon Sep 17 00:00:00 2001 From: ehpc Date: Mon, 11 Aug 2025 17:36:49 +0300 Subject: [PATCH 35/52] Implement allSettledLimit function to execute promises with a concurrency limit --- day_10/day_10_2.ts | 46 ++++++++++++++++++++++++++++++++++++++++++++++ day_10/day_10_3.ts | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/day_10/day_10_2.ts b/day_10/day_10_2.ts index e69de29..35f305c 100644 --- a/day_10/day_10_2.ts +++ b/day_10/day_10_2.ts @@ -0,0 +1,46 @@ +type PromiseFn = () => Promise; + +function allSettledLimit(fns: PromiseFn[], limit: number): Promise[]> { + const results: PromiseSettledResult[] = new Array(fns.length); + + let currentIndex = 0; + let completedCount = 0; + + return new Promise((resolve) => { + function runNext() { + if (currentIndex >= fns.length) return; + + const index = currentIndex; + currentIndex += 1; + fns[index]() + .then((value) => { + results[index] = { status: 'fulfilled', value }; + }) + .catch((reason) => { + results[index] = { status: 'rejected', reason }; + }) + .finally(() => { + completedCount += 1; + if (completedCount === fns.length) { + resolve(results); + } else { + runNext(); + } + }); + } + + const batchCount = Math.min(fns.length, limit); + for (let i = 0; i < batchCount; i++) { + runNext(); + } + }); +} + +allSettledLimit([ + () => fetch('//some-data-1'), + () => fetch('http://example.com'), + () => fetch('//some-data-3'), + () => fetch('//some-data-4') +], 2).then((results) => { + console.log(results); +}); \ No newline at end of file diff --git a/day_10/day_10_3.ts b/day_10/day_10_3.ts index e69de29..444ee6d 100644 --- a/day_10/day_10_3.ts +++ b/day_10/day_10_3.ts @@ -0,0 +1,33 @@ +interface Tree { + value: T; + children?: Tree[]; +} + +function iterate(tree: Tree): Iterator { + const queue: Tree[] = []; + queue.push(tree); + return { + next() { + const currentNode = queue.shift(); + if (!currentNode) return { value: undefined, done: true }; + + if (currentNode.children?.length) { + queue.push(...currentNode?.children); + } + return { value: currentNode.value, done: false } + } + }; +} + +const i = iterate({ + value: 1, + children: [{value: 2}, {value: 3, children: [{value: 4}]}] +}); + +console.log(i.next()); // {value: 1, done: false} +console.log(i.next()); // {value: 2, done: false} +console.log(i.next()); // {value: 3, done: false} +console.log(i.next()); // {value: 4, done: false} +console.log(i.next()); // {value: undefined, done: true} + +export {}; \ No newline at end of file From 4fca4d47f1fb317174006b7c48a3dcc3e26521fc Mon Sep 17 00:00:00 2001 From: ehpc Date: Mon, 11 Aug 2025 18:45:04 +0300 Subject: [PATCH 36/52] Implement myParseFloat function to parse a string into a floating-point number --- day_10/day_10_4.ts | 56 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/day_10/day_10_4.ts b/day_10/day_10_4.ts index e69de29..a694514 100644 --- a/day_10/day_10_4.ts +++ b/day_10/day_10_4.ts @@ -0,0 +1,56 @@ +function myParseFloat(str: string): number { + str = str.trim(); + if (!str) return NaN; + + let negative = false; + let negativeExponent = false; + let isLeftPart = true; + let exponent = false; + let left = 0; + let right = 0; + let rightCount = 0; + for (let i = 0; i < str.length; i++) { + const char = str[i]; + if (char === '-' && i === 0) { + if (str[i + 1] === '-') return NaN; + negative = true; + } else if (char === 'e') { + if (i === 0) return NaN; + if (exponent) break; + exponent = true; + isLeftPart = false; + if (str[i + 1] === '-') { + negativeExponent = true; + i += 1; + } + } else if (char === '.') { + if (!isLeftPart) break; + isLeftPart = false; + } else if (/\d/.test(char)) { + if (isLeftPart) { + left = 10 * left + Number(char); + } else { + right = right * 10 + Number(char); + rightCount += 1; + } + } else break; + } + if (exponent) { + if (negativeExponent) return left / Math.pow(10, right) * (negative ? -1 : 1); + else return left * Math.pow(10, right) * (negative ? -1 : 1); + } + return (left + right / Math.pow(10, rightCount)) * (negative ? -1 : 1); +} + +console.log(myParseFloat('10')); // 10 +console.log(myParseFloat('-10.2')); // -10.2 +console.log(myParseFloat('-562.234')); // -562.234 +console.log(myParseFloat('6e-2')); // 0.06 +console.log(myParseFloat('6e-e-2')); // 6 +console.log(myParseFloat('6e-f--2')); // 6 +console.log(myParseFloat('--20')); // NaN +console.log(myParseFloat('')); // NaN +console.log(myParseFloat(' 123')); // 123 (ignores leading whitespace) +console.log(myParseFloat('123abc')); // 123 (stops at first invalid char) +console.log(myParseFloat('1.2.3')); // 1.2 (stops at second decimal) +console.log(myParseFloat('e5')); // NaN From acfa4e160c1abb60f6d4c7837ef980751697ca21 Mon Sep 17 00:00:00 2001 From: ehpc Date: Mon, 11 Aug 2025 21:51:40 +0300 Subject: [PATCH 37/52] Implement getCentury function to calculate the century from a given year --- day_11/day_11_1.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/day_11/day_11_1.ts b/day_11/day_11_1.ts index e69de29..68c7f0b 100644 --- a/day_11/day_11_1.ts +++ b/day_11/day_11_1.ts @@ -0,0 +1,6 @@ +function getCentury(year: number): number { + return Math.ceil(year / 100); +} + +console.log(getCentury(1901)); // 20 +console.log(getCentury(1900)); // 19 \ No newline at end of file From 97857307907de47b5ec81210c65f69c25ce96fd1 Mon Sep 17 00:00:00 2001 From: ehpc Date: Tue, 12 Aug 2025 10:00:05 +0300 Subject: [PATCH 38/52] Implement functions to convert string ranges to arrays and find intersections --- day_11/day_11_2.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/day_11/day_11_2.ts b/day_11/day_11_2.ts index e69de29..46757f5 100644 --- a/day_11/day_11_2.ts +++ b/day_11/day_11_2.ts @@ -0,0 +1,25 @@ +function stringRangesToArray(ranges: string): number[][] { + return ranges.split('; ').map(range => { + const parts = range.split('-').map(Number); + if (parts.length === 1) parts.push(parts[0]); + return parts; + }); +} + +function intersectRanges(rangesAStr: string, rangesBStr: string): string { + const rangesA = stringRangesToArray(rangesAStr); + const rangesB = stringRangesToArray(rangesBStr); + const results: string[] = []; + for (const [aStart, aEnd] of rangesA) { + for (const [bStart, bEnd] of rangesB) { + const start = Math.max(aStart, bStart); + const end = Math.min(aEnd, bEnd); + if (start === end) results.push(`${start}`); + else if (start < end) results.push(`${start}-${end}`); + } + } + return results.join('; '); +} + +console.log('>', intersectRanges('1-2; 4-6; 9-11', '1-5; 10-14; 15')); // 1-2; 4-5; 10-11 +console.log('>', intersectRanges('1-2', '5-6')); // "" (no intersection) From 5be0f075ee0ece9e71085c86ae495a65637f035c Mon Sep 17 00:00:00 2001 From: ehpc Date: Tue, 12 Aug 2025 18:27:39 +0300 Subject: [PATCH 39/52] Implement twoSum function to find indices of two numbers that add up to a target --- day_11/day_11_3.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/day_11/day_11_3.ts b/day_11/day_11_3.ts index e69de29..3feff9e 100644 --- a/day_11/day_11_3.ts +++ b/day_11/day_11_3.ts @@ -0,0 +1,17 @@ +function twoSum(arr: number[], target: number): [number, number] | undefined { + const complements = new Map(); + + for (let i = 0; i < arr.length; i++) { + const complement = target - arr[i]; + if (complements.has(complement)) { + return [complements.get(complement)!, i]; + } + complements.set(arr[i], i); + } + + return undefined; +} + +console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1] +console.log(twoSum([3, 2, 4], 6)); // [1, 2] +console.log(twoSum([3, 3], 6)); // [0, 1] \ No newline at end of file From 52bd11b5a8e43688996fe13bd832550371e21b76 Mon Sep 17 00:00:00 2001 From: ehpc Date: Tue, 12 Aug 2025 22:34:20 +0300 Subject: [PATCH 40/52] Implement stream and streamGen functions using EventEmitter for async iteration --- day_11/day_11_4.ts | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/day_11/day_11_4.ts b/day_11/day_11_4.ts index e69de29..2a9e567 100644 --- a/day_11/day_11_4.ts +++ b/day_11/day_11_4.ts @@ -0,0 +1,77 @@ +import { EventEmitter } from 'events'; + +const ee = new EventEmitter(); + +function stream(eventEmitter: EventEmitter, event: string): AsyncIterableIterator { + let resolver: Function | null; + let queue: any[] = []; + + eventEmitter.on(event, (data) => { + if (resolver) { + resolver({ value: data, done: false }); + resolver = null; + } else { + queue.push(data); + } + }); + + return { + [Symbol.asyncIterator]() { + return this; + }, + + next() { + if (queue.length) return Promise.resolve({ value: queue.shift(), done: false }); + return new Promise((resolve) => { + resolver = resolve; + }); + } + }; +} + +(async () => { + for await (const e of stream(ee, 'foo')) { + console.log(e); // 1 2 3 + } +})(); + +ee.emit('foo', 1); +ee.emit('foo', 2); +ee.emit('foo', 3); + +// Generator version + +const ee2 = new EventEmitter(); + +async function *streamGen(eventEmitter: EventEmitter, event: string): AsyncIterableIterator { + const queue: any[] = []; + let resolver: Function | null; + + eventEmitter.on(event, (data) => { + if (resolver) { + resolver(data); + resolver = null; + } else { + queue.push(data); + } + }); + + while (true) { + while (queue.length) { + yield queue.shift(); + } + yield new Promise(resolve => resolver = resolve); + } +} + +(async () => { + for await (const e of streamGen(ee2, 'foo')) { + console.log(e); // 11 21 31 + } +})(); + +ee2.emit('foo', 11); +ee2.emit('foo', 21); +ee2.emit('foo', 31); + +export {}; From 71561f11cffc7ba4fe00314ed8c95faa12244c78 Mon Sep 17 00:00:00 2001 From: ehpc Date: Tue, 12 Aug 2025 22:37:50 +0300 Subject: [PATCH 41/52] Implement isPalindrome function to check if a string is a palindrome --- day_12/day_12_1.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/day_12/day_12_1.ts b/day_12/day_12_1.ts index e69de29..9b8811c 100644 --- a/day_12/day_12_1.ts +++ b/day_12/day_12_1.ts @@ -0,0 +1,16 @@ +function isPalindrome(str: string): boolean { + if (str.length <= 1) return false; + let left = 0; + let right = str.length - 1; + while (left <= right) { + if (str[left] !== str[right]) return false; + left += 1; + right -= 1; + } + return true; +} + +console.log(isPalindrome('bob')); // true +console.log(isPalindrome('abba')); // true +console.log(isPalindrome('a')); // false +console.log(isPalindrome('azt')); // false \ No newline at end of file From 3c154f96fe3b428c9056ff29991848a78038f2b0 Mon Sep 17 00:00:00 2001 From: ehpc Date: Wed, 13 Aug 2025 14:42:11 +0300 Subject: [PATCH 42/52] Implement extractQuotes function to extract quoted substrings from a given string --- day_12/day_12_2.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/day_12/day_12_2.ts b/day_12/day_12_2.ts index e69de29..c9186ed 100644 --- a/day_12/day_12_2.ts +++ b/day_12/day_12_2.ts @@ -0,0 +1,37 @@ +function extractQuotes(str: string): string[] { + const allowedQuotes = new Set(['"', "'", '`']); + const results: string[] = []; + + let i = 0; + while (i < str.length) { + const char = str[i]; + + if (allowedQuotes.has(char)) { + const currentQuote = char; + let fragment = ''; + i += 1; + + while (i < str.length) { + const inQuotesChar = str[i]; + if (inQuotesChar === '\\' && i + 1 < str.length) { + fragment += inQuotesChar + str[i + 1]; + i += 2; + } else if (inQuotesChar === currentQuote) { + results.push(fragment); + i += 1; + break; + } else { + fragment += inQuotesChar; + i += 1; + } + } + } else { + i += 1; + } + } + + return results; +} + +console.log(extractQuotes('Это строка в "кавычках\'" и `"эта"` тоже, а это "хитрая строка\\""')); // ["кавычках'", '"эта"', 'хитрая строка\\"'] +console.log(extractQuotes('"test\\\\"')); // ["test\\"] \ No newline at end of file From f1d87d6b458cbd4907bc9625d211f5627a4f3b60 Mon Sep 17 00:00:00 2001 From: ehpc Date: Wed, 13 Aug 2025 16:22:37 +0300 Subject: [PATCH 43/52] Implement zip function for async iteration over multiple AsyncIterables --- day_12/day_12_3.ts | 31 +++++++++++++++++++++++++++++++ day_3/day_3_4.ts | 2 ++ 2 files changed, 33 insertions(+) diff --git a/day_12/day_12_3.ts b/day_12/day_12_3.ts index e69de29..d581d24 100644 --- a/day_12/day_12_3.ts +++ b/day_12/day_12_3.ts @@ -0,0 +1,31 @@ +function zip(...iters: AsyncIterable[]): AsyncIterableIterator { + const iterators: AsyncIterator[] = iters.map(iter => iter[Symbol.asyncIterator]()); + return { + [Symbol.asyncIterator]() { + return this; + }, + async next() { + const nextPromises = iterators.map(iterator => iterator.next()); + const results = await Promise.all(nextPromises); + const done = results.some(({ done }) => done); + if (done) return { value: undefined, done: true }; + const combined = results.map(result => result.value); + return { value: combined, done: false }; + } + }; +} + + + +async function* makeAsync(iter) { + yield* iter; +} + +// [1, 'a', '.'] [2, 'b', '.'] +(async () => { + for await (const el of zip(makeAsync(new Set([1, 2])), makeAsync(['a', 'b', 'z']), makeAsync('...'))) { + console.log(el); + } +})(); + +export {}; diff --git a/day_3/day_3_4.ts b/day_3/day_3_4.ts index 321d4e0..c620826 100644 --- a/day_3/day_3_4.ts +++ b/day_3/day_3_4.ts @@ -44,3 +44,5 @@ console.log('2nd run: ', ...zipped); // nothing const generatorZipped = generatorZip(new Set([1, 2]), ['a', 'b', 'z'], '...'); console.log('1st run: ', ...generatorZipped); // [1, 'a', '.'] [2, 'b', '.'] console.log('2nd run: ', ...generatorZipped); // nothing + +export {}; \ No newline at end of file From 86073d1c3d49f00726c5be3ec1712912b5c7cede Mon Sep 17 00:00:00 2001 From: ehpc Date: Fri, 15 Aug 2025 10:25:25 +0300 Subject: [PATCH 44/52] Implement Levenshtein distance functions with recursive, memoized, and tabulated approaches --- day_12/day_12_4.ts | 172 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/day_12/day_12_4.ts b/day_12/day_12_4.ts index e69de29..524de11 100644 --- a/day_12/day_12_4.ts +++ b/day_12/day_12_4.ts @@ -0,0 +1,172 @@ +function levenshteinRecursive(strA: string, strB: string): number { + // Need to insert every symbol from B to A + if (!strA) return strB.length; + // Need to delete every symbol in A + if (!strB) return strA.length; + + // Do not need to do anything + if (strA.slice(-1) === strB.slice(-1)) { + return levenshteinRecursive(strA.slice(0, -1), strB.slice(0, -1)); + } + + return 1 + Math.min( + // Insert from B to A + levenshteinRecursive(strA, strB.slice(0, -1)), + // Remove from A + levenshteinRecursive(strA.slice(0, -1), strB), + // Replace in A + levenshteinRecursive(strA.slice(0, -1), strB.slice(0, -1)), + ); +} + +function levenshteinRecursiveMemoized(strA: string, strB: string): number { + const cache: Map = new Map(); + + function strsToKey(a: string, b: string): string { + return `${a}::${b}`; + } + + function recur(a: string, b: string): number { + const key = strsToKey(a, b); + if (cache.has(key)) return cache.get(key)!; + + let result: number; + + if (!a) result = b.length; + else if (!b) result = a.length; + else if (a.slice(-1) === b.slice(-1)) { + result = recur(a.slice(0, -1), b.slice(0, -1)); + } else { + result = 1 + Math.min( + recur(a, b.slice(0, -1)), + recur(a.slice(0, -1), b), + recur(a.slice(0, -1), b.slice(0, -1)), + ); + } + + cache.set(key, result); + return result; + } + + return recur(strA, strB); +} + +function levenshteinTabled(strA: string, strB: string): number { + /* + DP table format + s t r B + - b a b + s - 0 1 2 3 + t b 1 ? ? ? + r o 2 ? ? ? + A b 3 ? ? ? <- answer + */ + const table = new Array(strA.length + 1).fill(undefined).map(_ => new Array(strB.length + 1).fill(undefined)); + for (let i = 0; i <= strA.length; i++) table[i][0] = i; + for (let j = 0; j <= strB.length; j++) table[0][j] = j; + + for (let i = 1; i <= strA.length; i++) { + for (let j = 1; j <= strB.length; j++) { + const a = strA[i - 1]; + const b = strB[j - 1]; + if (a === b) table[i][j] = table[i - 1][j - 1]; + else table[i][j] = 1 + Math.min(table[i][j - 1], table[i - 1][j], table[i - 1][j - 1]); + } + } + + return table[strA.length][strB.length]; +} + + +// Test cases +const testCases = [ + // Basic examples + ['bob', 'rob', 1, 'одна замена'], + ['австрия', 'австралия', 2, 'два удаления'], + + // Edge cases + ['', '', 0, 'both empty'], + ['', 'abc', 3, 'insert all'], + ['abc', '', 3, 'delete all'], + ['a', 'a', 0, 'identical'], + + // Single character operations + ['a', 'b', 1, 'replace'], + ['a', 'ab', 1, 'insert'], + ['ab', 'a', 1, 'delete'], + + // Classic examples + ['kitten', 'sitting', 3, 'k→s, e→i, insert g'], + ['saturday', 'sunday', 3, 'sat→sun, ur→, ay→ay'], + + // Insertions only + ['cat', 'cats', 1, 'insert s'], + ['', 'hello', 5, 'insert all'], + + // Deletions only + ['hello', 'hell', 1, 'delete o'], + ['hello', '', 5, 'delete all'], + + // Replacements only + ['abc', 'xyz', 3, 'replace all'], + ['test', 'best', 1, 't→b'], + + // Mixed operations + ['intention', 'execution', 5, 'complex mixed'], + ['distance', 'editing', 5, 'complex mixed'], + + // Same length, different content + ['abcd', 'efgh', 4, 'replace all'], + ['abcd', 'abef', 2, 'replace c,d'], + + // Reversed strings + ['abc', 'cba', 2, 'a↔c'], + ['hello', 'olleh', 4, 'reverse'], + + // Repeated characters + ['aaa', 'aa', 1, 'delete one a'], + ['aa', 'aaa', 1, 'insert one a'], + ['aaa', 'bbb', 3, 'replace all'], + + // Unicode/Cyrillic + ['кот', 'код', 1, 'т→д'], + ['мама', 'папа', 2, 'м→п, м→п'], +] as const; + +// Functions to test +const functions = [ + { name: 'levenshteinRecursive', fn: levenshteinRecursive }, + { name: 'levenshteinRecursiveMemoized', fn: levenshteinRecursiveMemoized }, + { name: 'levenshteinTabled', fn: levenshteinTabled } +]; + +// Run tests +console.log('Running Levenshtein distance tests...\n'); + +for (const { name, fn } of functions) { + console.log(`Testing ${name}:`); + let passed = 0; + let failed = 0; + + for (const [strA, strB, expected, description] of testCases) { + const actual = fn(strA, strB); + const isCorrect = actual === expected; + + console.assert( + isCorrect, + `FAIL: ${name} "${strA}" → "${strB}" expected ${expected}, got ${actual} (${description})` + ); + + if (isCorrect) { + console.log(` ✓ "${strA}" → "${strB}" = ${actual} (${description})`); + passed++; + } else { + console.log(` ✗ "${strA}" → "${strB}" = ${actual}, expected ${expected} (${description})`); + failed++; + } + } + + console.log(` Results: ${passed} passed, ${failed} failed\n`); +} + +console.log('All tests completed!'); From d68b469b1b01260ac2637e11a7d9729c89cf2ffa Mon Sep 17 00:00:00 2001 From: ehpc Date: Fri, 15 Aug 2025 17:21:56 +0300 Subject: [PATCH 45/52] Implement brute force and Knuth-Morris-Pratt substring search algorithms --- day_13/day_13_1.ts | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/day_13/day_13_1.ts b/day_13/day_13_1.ts index e69de29..52d7486 100644 --- a/day_13/day_13_1.ts +++ b/day_13/day_13_1.ts @@ -0,0 +1,77 @@ +function includesBruteforce(haystack: string, needle: string): boolean { + for (let i = 0; i <= haystack.length - needle.length; i++) { + let isSubstring = true; + for (let j = 0; j < needle.length; j++) { + if (haystack[i + j] !== needle[j]) { + isSubstring = false; + break; + } + } + if (isSubstring) return true; + } + return false; +} + +console.log('# Brute force'); +console.log(includesBruteforce('adsgwadsxdsgwadsgz', 'dsgwadsgz')); // true +console.log(includesBruteforce('aaaxaaax', 'aaxa')); // true +console.log(includesBruteforce('aaaxaaax', 'aaaa')); // false +console.log(includesBruteforce('hello bob!', 'bob')); // true +console.log(includesBruteforce('abba', 'aba')); // false +console.log(includesBruteforce('abc', 'c')); // true +console.log(includesBruteforce('aaaaaaaaab', 'aaaab')); // true + +// Knuth-Morris-Pratt algorithm +function includesKMP(haystack: string, needle: string): boolean { + /* + Calculate longest prefix that is also a suffix for needle + aaaxaaaa => [a=0, aa=1, aaa=2, aaax=0, aaaxa=1, aaaxaa=2, aaaxaaa=3, aaaxaaaa=3] = [0,1,2,0,1,2,3,3] + */ + const lps: number[] = [0]; + let lpsIndex = 0; + let needleIndex = 1; + while (needleIndex < needle.length) { + if (needle[lpsIndex] === needle[needleIndex]) { + lps[needleIndex] = lpsIndex + 1; + needleIndex += 1; + lpsIndex += 1; + } else { + if (lpsIndex === 0) { + lps[needleIndex] = 0; + needleIndex += 1; + } else { + lpsIndex = lps[lpsIndex - 1]; + } + } + } + + // Now find substring + let haystackIndex = 0; + needleIndex = 0; + while (haystackIndex < haystack.length) { + if (haystack[haystackIndex] === needle[needleIndex]) { + haystackIndex += 1; + needleIndex += 1; + if (needleIndex === needle.length) { + return true; + } + } else { + if (needleIndex === 0) { + haystackIndex += 1; + } else { + needleIndex = lps[needleIndex - 1]; + } + } + } + + return false; +} + +console.log('# KMP'); +console.log(includesKMP('adsgwadsxdsgwadsgz', 'dsgwadsgz')); // true +console.log(includesKMP('aaaxaaax', 'aaxa')); // true +console.log(includesKMP('aaaxaaax', 'aaaa')); // false +console.log(includesKMP('hello bob!', 'bob')); // true +console.log(includesKMP('abba', 'aba')); // false +console.log(includesKMP('abc', 'c')); // true +console.log(includesKMP('aaaaaaaaab', 'aaaab')); // true From 309c6a899adc31c85b94b5e4b5bc629c2c818b2c Mon Sep 17 00:00:00 2001 From: ehpc Date: Fri, 15 Aug 2025 19:52:26 +0300 Subject: [PATCH 46/52] Implement binary search algorithm to find the first bad commit in an array --- day_13/day_13_2.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/day_13/day_13_2.ts b/day_13/day_13_2.ts index e69de29..1b3ab0e 100644 --- a/day_13/day_13_2.ts +++ b/day_13/day_13_2.ts @@ -0,0 +1,24 @@ +function findFirstBadCommit(commits: T[], comparator: (T) => boolean): number { + let left = 0; + let right = commits.length - 1; + let firstBadIndex = -1; + while (left <= right) { + const middle = Math.floor(left + (right - left) / 2); + const isGood = comparator(commits[middle]); + if (isGood) { + left = middle + 1; + } else { + right = middle - 1; + firstBadIndex = middle; + } + } + return firstBadIndex; +} + +const commits = ['good', 'good', 'good', 'bad', 'bad', 'bad', 'bad', 'bad', 'bad']; + +const test = (commit) => commit === 'good'; + +console.log(findFirstBadCommit(commits, test)); // 3 +console.log(findFirstBadCommit(['good', 'good'], test)); // -1 +console.log(findFirstBadCommit(['bad'], test)); // 0 From beaa61ad6115a323b2abc5fb100d4f4f17efb2e0 Mon Sep 17 00:00:00 2001 From: ehpc Date: Fri, 15 Aug 2025 22:00:12 +0300 Subject: [PATCH 47/52] Implement maxUniqueSubstr function to find the longest substring with unique characters --- day_13/day_13_3.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/day_13/day_13_3.ts b/day_13/day_13_3.ts index e69de29..c133ade 100644 --- a/day_13/day_13_3.ts +++ b/day_13/day_13_3.ts @@ -0,0 +1,33 @@ +function maxUniqueSubstr(str: string): string { + if (str.length <= 1) return str; + + let left = 0; + let right = 0; + const lastIndexByChar: Map = new Map(); + let bestLeft = 0; + let bestLength = 0; + while (right < str.length) { + const currentChar = str[right]; + if (lastIndexByChar.has(currentChar) && lastIndexByChar.get(currentChar)! >= left) { + left = lastIndexByChar.get(currentChar)! + 1; + } + + lastIndexByChar.set(currentChar, right); + + if (right - left + 1 > bestLength) { + bestLeft = left; + bestLength = right - left + 1; + } + + right += 1; + } + + return str.slice(bestLeft, bestLeft + bestLength); +} + +console.log(maxUniqueSubstr('acbabcbb')); // acb +console.log(maxUniqueSubstr('aab')); // ab +console.log(maxUniqueSubstr('abcabcbb')); // abc +console.log(maxUniqueSubstr('bbbbb')); // b +console.log(maxUniqueSubstr('pwwkew')); // wke +console.log(maxUniqueSubstr('abba')); // ab \ No newline at end of file From e4ce445de35f0d34eeaca84914fd473aae5b76b5 Mon Sep 17 00:00:00 2001 From: ehpc Date: Sat, 16 Aug 2025 15:52:28 +0300 Subject: [PATCH 48/52] Implement functions to generate anagrams and anagrams with frequency count --- day_13/day_13_4.ts | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/day_13/day_13_4.ts b/day_13/day_13_4.ts index e69de29..61aa960 100644 --- a/day_13/day_13_4.ts +++ b/day_13/day_13_4.ts @@ -0,0 +1,58 @@ +function getAnagram(str: string): string[] { + const chars = str.split('').sort(); + const used = new Array(chars.length).fill(false); + const results: string[] = []; + + function recur(permutation: string[]) { + if (permutation.length === chars.length) { + results.push(permutation.join('')); + return; + } + for (let i = 0; i < used.length; i++) { + if (used[i]) continue; + if (i > 0 && chars[i] === chars[i - 1] && !used[i - 1]) continue; + used[i] = true; + permutation.push(chars[i]); + recur(permutation); + used[i] = false; + permutation.pop(); + } + } + + recur([]); + return results; +} + +console.log(getAnagram('cat')); // ['act', 'atc', 'cat', 'cta', 'tac', 'tca'] +console.log(getAnagram('aab')); // ['aab', 'aba', 'baa'] +console.log(getAnagram('aba')); // ['aab', 'aba', 'baa'] + +function getAnagramFreq(str: string): string[] { + const chars = str.split('').sort(); + const freqByChar: Map = new Map(); + chars.forEach(char => freqByChar.set(char, freqByChar.has(char) ? freqByChar.get(char)! + 1 : 1)); + const results: string[] = []; + + function recur(permutation: string[]) { + if (permutation.length === chars.length) { + results.push(permutation.join('')); + return; + } + + for (const [char, freq] of freqByChar) { + if (freq <= 0) continue; + freqByChar.set(char, freq - 1); + permutation.push(char); + recur(permutation); + freqByChar.set(char, freq); + permutation.pop(); + } + } + + recur([]); + return results; +} + +console.log(getAnagramFreq('cat')); // ['act', 'atc', 'cat', 'cta', 'tac', 'tca'] +console.log(getAnagramFreq('aab')); // ['aab', 'aba', 'baa'] +console.log(getAnagramFreq('aba')); // ['aab', 'aba', 'baa'] From a83ba155b24fbd0b7ed9bd90d15fc13f099154c2 Mon Sep 17 00:00:00 2001 From: ehpc Date: Sat, 16 Aug 2025 17:06:29 +0300 Subject: [PATCH 49/52] Implement collapse function to flatten nested objects and arrays --- day_14/day_14_1.ts | 41 +++++++++++++++++++++++++++++++++++++++++ day_14/day_14_2.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/day_14/day_14_1.ts b/day_14/day_14_1.ts index e69de29..186e30d 100644 --- a/day_14/day_14_1.ts +++ b/day_14/day_14_1.ts @@ -0,0 +1,41 @@ +function collapse(obj: Record): Record { + const result: Record = {}; + + function recur(node: any, path: string = '') { + if (node !== null && typeof node === 'object' && node.constructor === Object) { + for (const prop of Object.getOwnPropertyNames(node)) { + recur(node[prop], path ? `${path}.${prop}` : prop); + } + } else if (Array.isArray(node)) { + for (let i = 0; i < node.length; i++) { + recur(node[i], path ? `${path}.${i}` : i.toString()) + } + } else { + result[path] = node; + } + } + + recur(obj); + + return result; +} + +const obj = { + a: { + b: [1, 2], + '': {c: 2} + } +}; + +/* {'a.b.0': 1, 'a.b.1': 2, 'a..c': 2} */ +console.log(collapse(obj)); + +const complexObj = { + a: { + b: [1, {c: 3}, 2], + '': {d: 4} + } +}; + +// {'a.b.0': 1, 'a.b.1.c': 3, 'a.b.2': 2, 'a..d': 4} +console.log(collapse(complexObj)); diff --git a/day_14/day_14_2.ts b/day_14/day_14_2.ts index e69de29..23274f5 100644 --- a/day_14/day_14_2.ts +++ b/day_14/day_14_2.ts @@ -0,0 +1,26 @@ +function isValid(str: string): boolean { + const stack: string[] = []; + const matchingParentheses = { + '}': '{', + ')': '(', + ']': '[', + }; + + for (const char of str) { + if (char === '{' || char === '(' || char === '[') { + stack.push(char); + } else if (matchingParentheses[char]) { + const lastParenthesis = stack.pop(); + if (lastParenthesis !== matchingParentheses[char]) return false; + } + } + + return stack.length === 0; +} + +console.log(isValid('(hello{world} and [me])')); // true +console.log(isValid('(hello{world)} and [me])')); // false +console.log(isValid(')')); // false +console.log(isValid('(hello')); // false + +export {}; \ No newline at end of file From 9a00292f8956a0a58ee65a837abe27b6ec6a8f66 Mon Sep 17 00:00:00 2001 From: ehpc Date: Sun, 17 Aug 2025 14:23:31 +0300 Subject: [PATCH 50/52] Implement functions to find palindromic substrings using two approaches --- day_14/day_14_3.ts | 124 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/day_14/day_14_3.ts b/day_14/day_14_3.ts index e69de29..fc0a56c 100644 --- a/day_14/day_14_3.ts +++ b/day_14/day_14_3.ts @@ -0,0 +1,124 @@ +function findPalindromicSubstring(str: string): string | null { + if (str.length < 2) return null; + + function expand(left: number, right: number): [number, number] { + while (left >= 0 && right < str.length) { + if (str[left] === str[right]) { + left -= 1; + right += 1; + } else break; + } + return [left + 1, right - 1]; + } + + let bestLen = 0; + let bestStart = 0; + let left = 0; + while (left < str.length) { + let [pLeft, pRight] = expand(left, left); + let len = pRight - pLeft + 1; + if (len > bestLen) { + bestLen = len; + bestStart = pLeft; + } + + if (left + 1 < str.length) { + [pLeft, pRight] = expand(left, left + 1); + len = pRight - pLeft + 1; + if (len > bestLen) { + bestLen = len; + bestStart = pLeft; + } + } + + left += 1; + } + return bestLen >= 2 ? str.slice(bestStart, bestStart + bestLen) : null; +} + +/** + * str = i a i a d a i a d a k + * diams = 1 3 3 1 7 1 7 1 3 1 1 + */ +function findPalindromicSubstringManachers(str: string): string | null { + const str_prime = `#${str.split('').join('#')}#`; // Make str always odd len + const diams = new Array(str_prime.length).fill(0); // Palindrome lengths at center i + let center = -1; // Center index of a palindrome with the most extended right border + let right_border = -1; // Inclusive right border + let longestPalindromeCenter = -1; + let longestPalindromeLen = 0; + for (let i = 0; i < str_prime.length; i++) { + let left = i - 1; + let right = i + 1; + if (i <= right_border) { + // Current center is inside another palindrome + const mirrorI = center - (i - center); + const mirrorRadius = diams[mirrorI]; + const distanceToRightBorder = right_border - i; + const containedILen = distanceToRightBorder*2 + 1; + if (mirrorRadius < containedILen) { + // If a mirror palindrome is not touching the border of the containing palindrome + diams[i] = mirrorRadius; + continue; + } else { + // If a mirror palindrome is touching the border of the containing palindrome + left = i - distanceToRightBorder - 1; + right = i + distanceToRightBorder + 1; + } + } + { + // Expand + while (left >= 0 && right < str_prime.length && str_prime[left] === str_prime[right]) { + left -= 1; + right += 1; + } + left += 1; + right -= 1; + const len = right - left + 1; + diams[i] = len; + + // Update center + if (right_border < right) { + center = i; + right_border = right; + } + + if (len > longestPalindromeLen) { + longestPalindromeCenter = i; + longestPalindromeLen = len; + } + } + } + + const realCenter = Math.floor(longestPalindromeCenter / 2); + const realLen = Math.floor(longestPalindromeLen / 2); + const left = realCenter - Math.floor(realLen / 2); + return realLen > 1 + ? str.slice(left, left + realLen) + : null; +} + + +for (const fn of [findPalindromicSubstring, findPalindromicSubstringManachers]) { + console.log('Testing', fn.name); + [ + ["iaiadaiadak", "aiadaia"], + ["", null], + ["a", null], + ["aa", "aa"], + ["ab", null], + ["aba", "aba"], + ["abba", "abba"], + ["cbbd", "bb"], + ["babad", "bab"], // или "aba" — обе ок + ["forgeeksskeegfor", "geeksskeeg"], + ["abcda", null], // длины 1 не считаем + ["aaaabaaa", "aaabaaa"], + ].forEach(([s, expect]) => { + const got = fn(s as string); + console.log(s, "=>", got, "✓", expect === null ? got === null : got === expect || (s==="babad" && (got==="bab"||got==="aba"))); + }); + console.log('\n\n'); +} + +export {}; \ No newline at end of file From 6de647011b57ca6ea55303c6fadcfdc318f15efe Mon Sep 17 00:00:00 2001 From: ehpc Date: Sun, 17 Aug 2025 18:57:27 +0300 Subject: [PATCH 51/52] Implement OrderedQueue class with push and pop methods for max heap functionality --- day_14/day_14_4.ts | 98 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/day_14/day_14_4.ts b/day_14/day_14_4.ts index e69de29..0abb11e 100644 --- a/day_14/day_14_4.ts +++ b/day_14/day_14_4.ts @@ -0,0 +1,98 @@ +type Comparator = (a: T, b: T) => number; + +class OrderedQueue { + #comparator: Comparator + /* + Examples: + Max heap as complete binary tree: + 9 level 0 + / \ + 5 7 level 1 + / \ / + 3 1 4 level 2 + As an array: + Root Left1 Right1 Left1.L Left1.R Right1.L + [ 9 5 7 3 1 4 ] + 0 1 2 3 4 5 + [9 5 7 3 1 4] + */ + #heap: T[] + + constructor(comparator: (a: T, b: T) => number) { + this.#comparator = comparator; + this.#heap = []; + } + + push(element: T) { + let index = this.#heap.length; + this.#heap.push(element); + while (index !== 0) { + const parentIndex = this.#heapGetParentIndex(index); + if (this.#comparator(this.#heap[parentIndex], element) < 0) { + [this.#heap[index], this.#heap[parentIndex]] = [this.#heap[parentIndex], this.#heap[index]]; + index = parentIndex; + } else break; + } + } + + pop(): T | undefined { + if (!this.#heap.length) return undefined; + + const result = this.#heap[0]; + const last = this.#heap.pop()!; + const size = this.#heap.length; + if (size > 0) { + this.#heap[0] = last + let index = 0; + while (index < size) { + const leftIndex = this.#heapGetLeftIndex(index); + const rightIndex = this.#heapGetRightIndex(index); + if (leftIndex >= size) break; + let swapIndex = leftIndex; + if (rightIndex < size && this.#comparator(this.#heap[leftIndex], this.#heap[rightIndex]) < 0) { + swapIndex = rightIndex; + } + if (this.#comparator(this.#heap[index], this.#heap[swapIndex]) < 0) { + [this.#heap[index], this.#heap[swapIndex]] = [this.#heap[swapIndex], this.#heap[index]]; + index = swapIndex; + } else break; + } + } + + return result; + } + + #heapGetLeftIndex(index: number) { + return 2 * index + 1; + } + + #heapGetRightIndex(index: number) { + return 2 * index + 2; + } + + #heapGetParentIndex(index: number) { + return (index - 1) >> 1; + } +} + +const queue = new OrderedQueue((a, b) => a - b); + +queue.push(1); +console.log(queue.pop()); // 1 +queue.push(1); +queue.push(5); +queue.push(2); +queue.push(-1); +queue.push(5); +queue.push(2); +queue.push(-1); +queue.push(5); + +console.log(queue.pop()); // 5 +console.log(queue.pop()); // 5 + +console.log(queue.pop()); // 5 +console.log(queue.pop()); // 2 +console.log(queue.pop()); // 2 + +export {}; From 00e24e73cce92fceae83dfe6fc2fca4edb8f53ce Mon Sep 17 00:00:00 2001 From: ehpc Date: Sun, 17 Aug 2025 21:50:40 +0300 Subject: [PATCH 52/52] Add VS Code configuration files for debugging and settings --- .gitignore | 3 --- .vscode/launch.json | 13 +++++++++++++ .vscode/settings.json | 6 ++++++ 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index 887f435..5522a36 100644 --- a/.gitignore +++ b/.gitignore @@ -13,9 +13,6 @@ build/ out/ *.tsbuildinfo -# VS Code settings -.vscode/ - # Environment variables .env .env.* diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2181663 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-node", + "request": "launch", + "name": "Debug Active TypeScript File", + "program": "${file}", + "runtimeArgs": ["-r", "ts-node/register"], + "skipFiles": ["/**"] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bcc71c0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "github.copilot.enable": { + "javascript": false, + "typescript": false + } +} \ No newline at end of file