PrepBro
Профессии
PrepBro
Профессия:

Подготовка

  • Вопросы785
  • Задачи32

Аналитика

  • hh статистика
  • Анализ резюме

Практика

  • Тестовое собеседование
  • Mock-собеседование
  • Менторы

Поддержка / отзывы

Telegram админа
Профессия:

Подготовка

  • Вопросы785
  • Задачи32

Аналитика

  • hh статистика
  • Анализ резюме

Практика

  • Тестовое собеседование
  • Mock-собеседование
  • Менторы

Поддержка / отзывы

Telegram админа
Все 24 профессии
Android DeveloperData AnalystSystem Analyst1С DeveloperiOS DeveloperBusiness AnalystJava DeveloperData ScientistQA EngineerQA AutomationPHP BackendC/C++ BackendDevOps EngineerIT Project ManagerFrontend DeveloperNode.js BackendUnity DeveloperC# BackendProduct AnalystFlutter DeveloperPython DeveloperIT Product ManagerGo DeveloperData Engineer

© 2026 PrepBro. Все права защищены.

Telegram-бот

Задачи по Node.js Backend

Event Loop: определите порядок вывода
1.7 Middle🔥 29💬 1

Решение

Порядок вывода

1
7
3
5
2
4
6

Объяснение

Event Loop в Node.js работает в несколько фаз, и критически важно различать:

  1. Синхронный код — выполняется сразу
  2. Микрозадачи (microtask queue) — Promise.then(), Promise.catch(), queueMicrotask()
  3. Макрозадачи (macrotask queue) — setTimeout(), setInterval(), setImmediate()

Порядок выполнения в каждом цикле:

  1. Выполнить весь синхронный код
  2. Выполнить все микрозадачи из микроочереди
  3. Выполнить одну макрозадачу
  4. Вернуться к шагу 2

Пошаговый разбор кода

Этап 1: Синхронный код (Call Stack)

Читать полностью ->
Реализуйте reduce для массива
2.2 Middle🔥 25💬 1

Решение: Array.prototype.myReduce

Полная реализация

Читать полностью ->
Реализуйте собственный Promise.all
2.0 Middle🔥 25💬 1

Решение

Базовая реализация

function promiseAll<T>(promises: Promise<T>[]): Promise<T[]> {
  return new Promise((resolve, reject) => {
    // Если массив пуст, резолвим пустой массив
    if (promises.length === 0) {
      resolve([]);
      return;
    }

const results: T[] = [];
    let completedCount = 0;

promises.forEach((promise, index) => {
      Promise.resolve(promise)
        .then((value) => {
          results[index] = value;
          completedCount++;

// Если все промисы выполнены, резолвим результат
          if (completedCount === promises.length) {
            resolve(results);
          }
        })
        .catch((error) => {
          // При первой ошибке сразу реджектим весь результат
          reject(error);
        });
    });
  });
}

Как это работает

Ключевые моменты:

Читать полностью ->
Вычислите число Фибоначчи
1.0 Junior🔥 24💬 1

Решение

Числа Фибоначчи: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55... Каждое число = сумма двух предыдущих. F(n) = F(n-1) + F(n-2)

Подход 1: Простая рекурсия (неоптимальная)

function fibonacci(n: number): number {
  if (n < 0) throw new Error("n must be non-negative");
  if (n === 0) return 0;
  if (n === 1) return 1;
  
  return fibonacci(n - 1) + fibonacci(n - 2);
}

// Примеры:
console.log(fibonacci(0));  // 0
console.log(fibonacci(1));  // 1
console.log(fibonacci(10)); // 55
console.log(fibonacci(20)); // 6765

Проблема: Экспоненциальная сложность O(2^n)

fibonacci(5) вызывает:
              fib(5)
            /       \
         fib(4)     fib(3)
        /    \       /    \
     fib(3) fib(2) fib(2) fib(1)
     /  \    / \    / \
  fib(2) fib(1) fib(1) fib(0) ...
  / \
...

Много дублирующихся вычислений! fib(3) вычисляется 2 раза, fib(2) - 3 раза

Подход 2: Мемоизация (O(n) время)

Читать полностью ->
Сервис записи к врачу с напоминаниями
3.0 Senior🔥 22💬 1

Решение: Архитектура сервиса записи к врачу с напоминаниями

1. Архитектура системы

Компоненты:

┌─────────────┐
│   API       │ (Express/Fastify)
│ Endpoints   │
└──────┬──────┘
       │
       ├──► AppointmentService (бизнес-логика)
       │
       ├──► ReminderQueue (Bull/Redis)
       │
       ├──► Database (PostgreSQL)
       │
       └──► NotificationService (SMS/Push/Email)

Основные микросервисы:

  • API Gateway: обработка запросов пациентов и врачей
  • AppointmentService: управление записями
  • ReminderWorker: обработка задач из очереди
  • NotificationService: отправка напоминаний

2. Структура базы данных

-- Таблица пациентов
CREATE TABLE patients (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  phone VARCHAR(20) NOT NULL UNIQUE,
  email VARCHAR(255),
  name VARCHAR(255) NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW()
);
Читать полностью ->
Реализуйте filter для массива
1.0 Junior🔥 22💬 1

Решение

Полная реализация

Array.prototype.myFilter = function<T>(callback: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[] {
  const result: T[] = [];
  
  for (let i = 0; i < this.length; i++) {
    // Проверяем, что элемент существует в массиве (важно для разреженных массивов)
    if (i in this) {
      // Вызываем callback с контекстом thisArg, если передан
      const shouldInclude = callback.call(thisArg, this[i], i, this);
      
      // Если callback вернул truthy значение, добавляем элемент в результат
      if (shouldInclude) {
        result.push(this[i]);
      }
    }
  }
  
  return result;
};

Ключевые моменты реализации

1. Итерация через элементы

  • Используем обычный for цикл для перебора всех индексов
  • Проверка i in this необходима для корректной работы с разреженными массивами (sparse arrays), где есть пропуски
Читать полностью ->
Бинарный поиск в отсортированном массиве
1.6 Junior🔥 20💬 1

Решение: Бинарный поиск

Итеративная реализация

function binarySearch(arr: number[], target: number): number {
  let left = 0;
  let right = arr.length - 1;
  
  while (left <= right) {
    const mid = left + Math.floor((right - left) / 2);
    const midValue = arr[mid];
    
    if (midValue === target) {
      return mid;
    } else if (midValue < target) {
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }
  
  return -1;
}

const arr = [1, 3, 5, 7, 9, 11, 13, 15];
console.log(binarySearch(arr, 7));  // 3
console.log(binarySearch(arr, 1));  // 0
console.log(binarySearch(arr, 15)); // 7
console.log(binarySearch(arr, 6));  // -1

Рекурсивная реализация

function binarySearchRecursive(arr: number[], target: number): number {
  return helper(arr, target, 0, arr.length - 1);
}
Читать полностью ->
Напишите middleware для логирования запросов Express
1.2 Junior🔥 20💬 1

Решение

Базовая реализация

import { Request, Response, NextFunction } from 'express';

function requestLogger() {
  return (req: Request, res: Response, next: NextFunction) => {
    const startTime = Date.now();

// Отслеживаем момент отправки ответа
    res.on('finish', () => {
      const duration = Date.now() - startTime;
      console.log(`${req.method} ${req.url} - ${duration}ms`);
    });

next();
  };
}

Как это работает

Принцип работы middleware:

  1. Фиксируем время начала — startTime = Date.now()
  2. Слушаем событие 'finish' — это событие срабатывает когда ответ полностью отправлен
  3. Вычисляем время — duration = Date.now() - startTime
  4. Логируем информацию — выводим метод, URL и время
  5. Передаём управление — next() вызывает следующий middleware

Порядок выполнения:

Читать полностью ->
Сумма чисел в диапазоне
2.0 Middle🔥 18💬 1

Решение: Сумма чисел в диапазоне

1. Наивное решение O(n)

function sumRangeNaive(start: number, end: number): number {
  let sum = 0;
  const min = Math.min(start, end);
  const max = Math.max(start, end);
  
  for (let i = min; i <= max; i++) {
    sum += i;
  }
  
  return sum;
}

console.log(sumRangeNaive(1, 5));   // 15
console.log(sumRangeNaive(5, 1));   // 15
console.log(sumRangeNaive(-3, 3));  // 0

Минусы: На диапазоне 1-1,000,000 требует 1M итераций

2. Оптимальное решение O(1) - Формула арифметической прогрессии

function sumRange(start: number, end: number): number {
  // Гарантируем, что start <= end
  const min = Math.min(start, end);
  const max = Math.max(start, end);
  
  // Формула: sum = n * (first + last) / 2
  // где n = количество элементов
  const count = max - min + 1;
  const sum = (count * (min + max)) / 2;
  
  return sum;
}
Читать полностью ->
Реализуйте собственный map для массива
1.2 Junior🔥 18💬 1

Решение

Базовая реализация

Array.prototype.myMap = function<T, U>(
  callback: (value: T, index: number, array: T[]) => U
): U[] {
  const result: U[] = [];

for (let i = 0; i < this.length; i++) {
    // Проверяем, что индекс существует (для sparse arrays)
    if (i in this) {
      result[i] = callback(this[i], i, this);
    }
  }

return result;
};

Версия с поддержкой thisArg

Array.prototype.myMap = function<T, U>(
  callback: (this: any, value: T, index: number, array: T[]) => U,
  thisArg?: any
): U[] {
  const result: U[] = [];

for (let i = 0; i < this.length; i++) {
    if (i in this) {
      // Вызываем callback с заданным контекстом this
      result[i] = callback.call(thisArg, this[i], i, this);
    }
  }

return result;
};

Как это работает

Ключевые моменты:

Читать полностью ->
Deep clone объекта
1.7 Middle🔥 17💬 1

Решение: Deep Clone объекта

Базовая реализация

function deepClone<T>(obj: T): T {
  // Примитивные типы и null
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  
  // Date
  if (obj instanceof Date) {
    return new Date(obj.getTime()) as unknown as T;
  }
  
  // RegExp
  if (obj instanceof RegExp) {
    return new RegExp(obj.source, obj.flags) as unknown as T;
  }
  
  // Массивы
  if (Array.isArray(obj)) {
    const clonedArray: any[] = [];
    for (let i = 0; i < obj.length; i++) {
      clonedArray[i] = deepClone(obj[i]);
    }
    return clonedArray as T;
  }
  
  // Объекты
  if (obj instanceof Object) {
    const clonedObject: any = {};
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        clonedObject[key] = deepClone((obj as any)[key]);
      }
    }
    return clonedObject as T;
  }
  
  return obj;
}

С обработкой циклических ссылок (WeakMap)

Читать полностью ->
Опишите код эндпоинта для загрузки большого файла на S3
3.0 Senior🔥 17💬 1

Решение

Загрузка 4 ГБ файла требует архитектурного подхода. Нельзя загружать всё в память.

Почему не в память?

Node.js может использовать максимум ~1.4 ГБ памяти (V8 heap). Загрузка 4 ГБ файла приведёт к OutOfMemory error и падению процесса. Весь сервер зависнет.

Правильный подход: Streams + S3 Multipart Upload

1. Используем AWS SDK с lib-storage

import { Upload } from '@aws-sdk/lib-storage';
import { S3Client } from '@aws-sdk/client-s3';

const s3Client = new S3Client({ region: 'us-east-1' });

app.post('/upload', async (req, res) => {
  const bucketName = process.env.AWS_BUCKET;
  const key = `uploads/${Date.now()}`;
  const totalSize = parseInt(req.headers['content-length'] || '0', 10);

try {
    const upload = new Upload({
      client: s3Client,
      params: {
        Bucket: bucketName,
        Key: key,
        Body: req, // Поток напрямую
        ContentType: req.headers['content-type'],
      },
      partSize: 5 * 1024 * 1024,
    });
Читать полностью ->
Реализуйте функцию debounce
2.0 Middle🔥 17💬 1

Решение

Базовая реализация

function debounce<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: Parameters<T>) => void {
  let timerId: NodeJS.Timeout | null = null;

return function (...args: Parameters<T>) {
    // Отменяем предыдущий таймер
    if (timerId !== null) {
      clearTimeout(timerId);
    }

// Устанавливаем новый таймер
    timerId = setTimeout(() => {
      fn(...args);
      timerId = null;
    }, delay);
  };
}

Как это работает

Ключевой механизм — замыкание (Closure):

  • Переменная timerId сохраняется в замыкании возвращаемой функции
  • Каждый вызов debounced функции имеет доступ к одному и тому же timerId
  • Это позволяет отменять предыдущие таймеры

Порядок выполнения:

  1. debouncedLog(1) → timerId = setTimeout(..., 300)
  2. debouncedLog(2) → clearTimeout(timerId) → новый таймер на 300мс
  3. debouncedLog(3) → clearTimeout(timerId) → новый таймер на 300мс
  4. Через 300мс: fn(3) → вывод 3
Читать полностью ->
Реализуйте функцию sleep на промисах
1.0 Junior🔥 15💬 1

Решение

Базовая реализация

function sleep(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Как это работает

Принцип работы:

  1. Создаём промис — оборачиваем setTimeout в Promise
  2. Устанавливаем таймер — setTimeout вызывает resolve через ms миллисекунд
  3. Резолвим промис — resolve() выполнит await в async функции

Пошаговый процесс:

Старт: sleep(2000)
├─ Создан новый Promise
├─ setTimeout установлен на 2000мс
├─ Возвращаем промис
└─ При await управление передаётся дальше

Через 2 сек:
├─ setTimeout срабатывает
├─ resolve() выполняется
├─ Промис резолвится
└─ await разблокируется, код продолжается

Пример использования

async function demo() {
  console.log("Start");
  await sleep(2000);
  console.log("After 2 seconds");
  await sleep(1000);
  console.log("After 1 more second");
}

demo();
Читать полностью ->
Асинхронная итерация по массиву с задержкой
2.3 Middle🔥 14💬 1

Решение

Вариант 1: Использование let (Рекомендуется)

const arr = [10, 20, 30, 40, 50];

for (let i = 0; i < arr.length; i++) {
  setTimeout(() => {
    console.log(i);
  }, i * 1000);
}

// Вывод:
// 0 (через 1 сек)
// 1 (через 2 сек)
// 2 (через 3 сек)
// 3 (через 4 сек)
// 4 (через 5 сек)

Почему это работает:

  • let создаёт новое замыкание для каждой итерации
  • Каждый setTimeout получает свою копию переменной i
  • Поэтому каждый вывод показывает правильное значение

Вариант 2: Использование IIFE (Старый подход с var)

const arr = [10, 20, 30, 40, 50];

for (var i = 0; i < arr.length; i++) {
  (function(index) {
    setTimeout(() => {
      console.log(index);
    }, index * 1000);
  })(i);
}
Читать полностью ->
Удалите дубликаты из массива
1.8 Middle🔥 13💬 1

Решение: Удаление дубликатов из массива

1. С помощью Set (оптимально)

function removeDuplicatesSet<T>(arr: T[]): T[] {
  return Array.from(new Set(arr));
  // или
  // return [...new Set(arr)];
}

const arr = [1, 2, 2, 3, 4, 4, 5, 1];
console.log(removeDuplicatesSet(arr)); // [1, 2, 3, 4, 5]

Плюсы: O(n) время, O(n) память, сохраняет порядок, очень быстро

Минусы: Сравнивает по ===, проблема с объектами и NaN

2. С помощью filter и indexOf

function removeDuplicatesFilter<T>(arr: T[]): T[] {
  return arr.filter((item, index) => arr.indexOf(item) === index);
}

const arr = [1, 2, 2, 3, 4, 4, 5, 1];
console.log(removeDuplicatesFilter(arr)); // [1, 2, 3, 4, 5]

Плюсы: Простой, читаемый код, работает с любыми типами

Минусы: O(n²) время, медленнее на больших массивах

3. С помощью reduce

Читать полностью ->
Вычислите факториал числа
2.0 Middle🔥 13💬 1

Решение

Факториал n! = 1 × 2 × 3 × ... × n, где 0! = 1 по определению. Разберу несколько подходов.

Подход 1: Рекурсивный (классический)

function factorialRecursive(n: number): number {
  // Граничные случаи
  if (n < 0) throw new Error("Factorial undefined for negative numbers");
  if (n === 0 || n === 1) return 1;
  
  // Рекурсивный случай: n! = n × (n-1)!
  return n * factorialRecursive(n - 1);
}

// Примеры:
console.log(factorialRecursive(5));  // 120
console.log(factorialRecursive(0));  // 1
console.log(factorialRecursive(10)); // 3628800

Как работает:

factorialRecursive(5)
= 5 * factorialRecursive(4)
= 5 * (4 * factorialRecursive(3))
= 5 * (4 * (3 * factorialRecursive(2)))
= 5 * (4 * (3 * (2 * factorialRecursive(1))))
= 5 * (4 * (3 * (2 * 1)))
= 5 * (4 * (3 * 2))
= 5 * (4 * 6)
= 5 * 24
= 120
Читать полностью ->
Рекурсивный flatten массива
2.3 Middle🔥 13💬 1

Решение

Flattening — преобразование вложенного массива в одномерный. Это классическая задача на рекурсию, которая хорошо показывает понимание рекурсивных алгоритмов.

Подход 1: Классическая рекурсия

function flatten(arr: any[]): any[] {
  const result: any[] = [];
  
  for (const item of arr) {
    if (Array.isArray(item)) {
      // Рекурсивно вызываем flatten и добавляем элементы
      result.push(...flatten(item));
    } else {
      // Если не массив, добавляем элемент напрямую
      result.push(item);
    }
  }
  
  return result;
}

// Примеры:
console.log(flatten([1, [2, [3, [4]], 5]]));
// [1, 2, 3, 4, 5]

console.log(flatten([[1, 2], [3, [4, [5, [6]]]]]]));
// [1, 2, 3, 4, 5, 6]

Как работает:

  1. Проходим по каждому элементу массива
  2. Если элемент — массив, рекурсивно вызываем flatten и разворачиваем результат
  3. Если нет, добавляем элемент в результат
Читать полностью ->
Проверка на палиндром
1.2 Junior🔥 13💬 1

Решение

Палиндром — это строка, которая читается одинаково в обе стороны. Решу эту задачу пошагово, рассмотрев несколько подходов.

Основной подход с регулярными выражениями

function isPalindrome(str: string): boolean {
  // Очищаем строку: удаляем всё кроме букв и цифр, переводим в нижний регистр
  const cleaned = str.replace(/[^a-z0-9]/gi, "").toLowerCase();
  
  // Проверяем, равна ли строка её зеркальному отражению
  return cleaned === cleaned.split("").reverse().join("");
}

Объяснение:

  • replace(/[^a-z0-9]/gi, "") — регулярное выражение удаляет всё кроме букв (a-z) и цифр (0-9), флаг i для игнорирования регистра, g для глобального поиска
  • .toLowerCase() — приводим к нижнему регистру
  • .split("") — разбиваем на массив символов
  • .reverse() — разворачиваем массив
  • .join("") — объединяем обратно в строку
  • Сравниваем исходную очищенную строку с её зеркальным отражением

Оптимизированный подход с двумя указателями

Читать полностью ->
Напишите функцию каррирования
3.0 Senior🔥 13💬 1

Решение

Базовая реализация

function curry<T extends (...args: any[]) => any>(
  fn: T
): (...args: any[]) => any {
  const arity = fn.length; // Количество параметров функции

return function curried(...args: any[]): any {
    // Если собрали достаточно аргументов, вызываем функцию
    if (args.length >= arity) {
      return fn(...args);
    }

// Иначе возвращаем функцию, которая соберёт оставшиеся аргументы
    return (...nextArgs: any[]) => curried(...args, ...nextArgs);
  };
}

Как это работает

Принцип каррирования:

  • Преобразует функцию с несколькими параметрами в цепочку функций с одним параметром
  • Каждый вызов собирает аргумент и возвращает новую функцию
  • Когда собрано достаточно аргументов, выполняется исходная функция

Пошаговый процесс для curriedSum(1)(2)(3):

Шаг 1: curriedSum(1)
  - args = [1]
  - arity = 3 (функция sum ожидает 3 параметра)
  - 1 < 3, возвращаем новую функцию
Читать полностью ->
Event Loop с задержками
3.0 Senior🔥 12💬 1

Решение

Порядок вывода

setTimeout2          // 100 мс
setTimeout1          // 1000 мс
setTimeout4          // 1000 мс
Promise1             // 1000 мс (срабатывает после setTimeout1/4)
Promise3             // 1000 мс (срабатывает после setTimeout1/4)
setTimeout3          // 2000 мс
Promise2             // 2000 мс (срабатывает после setTimeout3)

Объяснение по временной шкале

t = 0 мс: Регистрация всех таймеров

const myPromise = (delay) => new Promise((res) => setTimeout(res, delay));
Читать полностью ->
Flatten вложенного объекта
2.2 Middle🔥 12💬 1

Решение

Базовая реализация

function flattenObject(
  obj: Record<string, any>,
  prefix: string = ""
): Record<string, any> {
  const result: Record<string, any> = {};

for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      const value = obj[key];
      const newKey = prefix ? `${prefix}.${key}` : key;

if (value !== null && typeof value === "object" && !Array.isArray(value)) {
        // Рекурсивно обрабатываем вложенные объекты
        Object.assign(result, flattenObject(value, newKey));
      } else {
        // Добавляем примитивные значения
        result[newKey] = value;
      }
    }
  }

return result;
}

Как это работает

Принцип работы:

  1. Итерируем по ключам — проходим по каждому свойству объекта
  2. Формируем полный ключ — собираем путь через точку (a.b.c)
  3. Проверяем значение — если объект, рекурсивно разворачиваем
  4. Добавляем в результат — примитивные значения добавляем сразу
Читать полностью ->
Реализуйте функцию throttle
2.0 Middle🔥 12💬 1

Решение

Базовая реализация

function throttle<T extends (...args: any[]) => any>(
  fn: T,
  limit: number
): (...args: Parameters<T>) => void {
  let inThrottle: boolean = false;

return function (...args: Parameters<T>) {
    if (!inThrottle) {
      fn(...args);
      inThrottle = true;

setTimeout(() => {
        inThrottle = false;
      }, limit);
    }
  };
}

Как это работает

Принцип работы:

  • Флаг inThrottle блокирует повторные вызовы на время limit
  • После истечения limit миллисекунд флаг сбрасывается
  • Новые вызовы могут быть выполнены

Вызовы функции при частых обращениях:

Время:  0ms    100ms   200ms   300ms   400ms   500ms   600ms   700ms   800ms   900ms  1000ms
Вызовы: X      X       X       X       X       -       -       -       -       -      X
Выпол:  ✓      ✗       ✗       ✗       ✗       -       -       -       -       -      ✓
Читать полностью ->
Группировка людей по городу
1.6 Junior🔥 11💬 1

Решение

Группировка элементов — одна из самых частых операций в обработке данных. Нужно создать объект, где ключ — город, значение — массив людей из этого города. Временная сложность O(n) означает один проход по массиву.

Подход 1: Простой цикл (самый понятный)

interface Person {
  name: string;
  city: string;
}

type GroupedByCity = Record<string, Person[]>;

function groupByCity(people: Person[]): GroupedByCity {
  const result: GroupedByCity = {};
  
  for (const person of people) {
    // Если города нет в результате, создаём пустой массив
    if (!result[person.city]) {
      result[person.city] = [];
    }
    // Добавляем человека к его городу
    result[person.city].push(person);
  }
  
  return result;
}

Анализ:

  • Временная сложность: O(n) — один проход по массиву
  • Пространственная сложность: O(n) — хранение всех людей в новом объекте
  • Простая и понятная логика
  • Не требует понимания продвинутых методов
Читать полностью ->
Преобразование строки в nested объект
2.0 Middle🔥 10💬 1

Решение

Эта задача требует создания вложенной структуры объектов, где каждая точка в строке представляет уровень вложенности. Разберу несколько подходов.

Подход 1: С использованием reduce (элегантный)

function stringToNestedObject(str: string, value: any): Record<string, any> {
  // Разбиваем строку по точкам
  const keys = str.split(".");
  
  // Идём с конца и создаём объекты
  return keys.reduceRight((acc, key) => {
    return { [key]: acc };
  }, value);
}

// Примеры:
console.log(stringToNestedObject("a.b.c", 42));
// { a: { b: { c: 42 } } }

console.log(stringToNestedObject("user.name.first", "John"));
// { user: { name: { first: "John" } } }

Как работает:

  1. Разбиваем строку "a.b.c" на массив ["a", "b", "c"]
  2. reduceRight идёт справа налево (от конца)
  3. Стартуем с value (42)
  4. На каждом шаге оборачиваем текущий аккумулятор в новый объект с ключом
  5. Итераций: { c: 42 } → { b: { c: 42 } } → { a: { b: { c: 42 } } }
Читать полностью ->
FizzBuzz
1.2 Junior🔥 10💬 1

Решение

FizzBuzz — классическая задача для проверки базовой логики программирования. Разберу несколько подходов от простого к более элегантному.

Базовый подход с if-else

function fizzBuzz(n: number): void {
  for (let i = 1; i <= n; i++) {
    if (i % 15 === 0) {
      console.log("FizzBuzz");
    } else if (i % 3 === 0) {
      console.log("Fizz");
    } else if (i % 5 === 0) {
      console.log("Buzz");
    } else {
      console.log(i);
    }
  }
}

Ключевой момент: проверяем сначала на 15 (делимое и на 3, и на 5), потом на 3, затем на 5. Порядок важен!

Более элегантный подход с конструированием строки

function fizzBuzz(n: number): void {
  for (let i = 1; i <= n; i++) {
    let output = "";
    
    if (i % 3 === 0) output += "Fizz";
    if (i % 5 === 0) output += "Buzz";
    
    console.log(output || i);
  }
}
Читать полностью ->
Проверьте простое число
1.3 Junior🔥 9💬 1

Решение

Простое число — это натуральное число больше 1, которое делится только на 1 и на себя. Разберу несколько подходов от базового к оптимизированному.

Подход 1: Базовый (O(n))

function isPrime(n: number): boolean {
  // Граничные случаи
  if (n <= 1) return false;
  if (n === 2) return true;
  if (n % 2 === 0) return false;
  
  // Проверяем делители от 3 до n
  for (let i = 3; i < n; i += 2) {
    if (n % i === 0) return false;
  }
  
  return true;
}

Анализ:

  • Временная сложность: O(n)
  • На большие числа очень медленно
  • Пропускаем чётные числа (кроме 2)

Подход 2: Оптимизированный (O(√n)) — РЕКОМЕНДУЕМЫЙ

Читать полностью ->
Проверка на анаграмму
1.3 Junior🔥 9💬 1

Решение

Анаграмма — это слово или фраза, составленная из букв другого слова/фразы в другом порядке. Разберу несколько подходов.

Подход 1: Сортировка символов

function isAnagram(str1: string, str2: string): boolean {
  // Очищаем строки, переводим в нижний регистр
  const normalize = (str: string) => 
    str.toLowerCase().replace(/[^a-z0-9]/g, "");
  
  const clean1 = normalize(str1);
  const clean2 = normalize(str2);
  
  // Сортируем символы и сравниваем
  return clean1.split("").sort().join("") === clean2.split("").sort().join("");
}

Как работает:

  1. Нормализуем обе строки (нижний регистр, удаляем пробелы/знаки)
  2. Разбиваем на символы .split("")
  3. Сортируем .sort()
  4. Объединяем .join("")
  5. Сравниваем результаты

Временная сложность: O(n log n) из-за сортировки Пространственная сложность: O(n)

Подход 2: Подсчёт символов (оптимально)

Читать полностью ->
Найти максимум и минимум без Math.max/min
1.0 Junior🔥 7💬 1

Решение

Поиск минимального и максимального элемента — базовая задача, которая показывает понимание алгоритмов и обработки граничных случаев. Хотя Math.max/min существуют, запрет на них заставляет написать собственную реализацию.

Подход 1: Простой цикл (классический)

interface MinMax {
  min: number;
  max: number;
}

function findMinMax(arr: number[]): MinMax {
  // Граничный случай: пустой массив
  if (arr.length === 0) {
    throw new Error("Array must not be empty");
  }
  
  let min = arr[0];
  let max = arr[0];
  
  // Начинаем с индекса 1, так как 0 уже обработана
  for (let i = 1; i < arr.length; i++) {
    if (arr[i] < min) {
      min = arr[i];
    }
    if (arr[i] > max) {
      max = arr[i];
    }
  }
  
  return { min, max };
}

// Примеры:
console.log(findMinMax([3, 1, 4, 1, 5, 9, 2, 6]));
// { min: 1, max: 9 }

console.log(findMinMax([-5, 0, 5, 10, -10]));
// { min: -10, max: 10 }
Читать полностью ->
Посчитайте количество гласных в строке
1.3 Junior🔥 6💬 1

Решение

Подход 1: С использованием регулярного выражения

function countVowels(str: string): number {
  const matches = str.match(/[aeiou]/gi);
  return matches ? matches.length : 0;
}

Как работает:

  • /[aeiou]/gi — ищет любую гласную
  • g флаг — все совпадения
  • i флаг — регистронезависимый
  • match() возвращает массив или null

Подход 2: С использованием простого цикла

function countVowels(str: string): number {
  const vowels = 'aeiouAEIOU';
  let count = 0;
  
  for (const char of str) {
    if (vowels.includes(char)) {
      count++;
    }
  }
  
  return count;
}

Подход 3: С использованием filter

function countVowels(str: string): number {
  return [...str].filter(char => /[aeiou]/i.test(char)).length;
}

Подход 4: С использованием Set

Читать полностью ->
Event Loop с Promise.reject
3.0 Senior🔥 6💬 1

Решение

Порядок вывода

1
6
3
5
2
4
7

Объяснение

Это типичная задача на понимание Event Loop в JavaScript. Разберу пошагово.

Основные концепции

Call Stack — стек синхронного кода

Microtask Queue — очередь микрозадач (Promises, queueMicrotask, MutationObserver)

Macrotask Queue — очередь макрозадач (setTimeout, setInterval, setImmediate, I/O)

Порядок исполнения

  1. Synchronous code (Call Stack):
    console.log(1);      // Выводит: 1
    setTimeout(...);      // Добавляет в Macrotask Queue
    Promise.reject(3)..  // Добавляет в Microtask Queue
    new Promise(...);    // setTimeout создаёт новый Promise
    Promise.resolve(5).. // Добавляет в Microtask Queue
    console.log(6);      // Выводит: 6
    setTimeout(...);      // Добавляет в Macrotask Queue
    
    Вывод: 1, 6
Читать полностью ->
Объедините пересекающиеся интервалы
2.0 Middle🔥 6💬 1

Решение

Базовая реализация

function mergeIntervals(intervals: number[][]): number[][] {
  // Если интервалов меньше 2, возвращаем как есть
  if (intervals.length <= 1) {
    return intervals;
  }

// Сортируем интервалы по началу
  intervals.sort((a, b) => a[0] - b[0]);

const result: number[][] = [intervals[0]];

for (let i = 1; i < intervals.length; i++) {
    const current = intervals[i];
    const last = result[result.length - 1];

// Если текущий интервал пересекается с последним, объединяем
    if (current[0] <= last[1]) {
      last[1] = Math.max(last[1], current[1]);
    } else {
      // Иначе добавляем как новый интервал
      result.push(current);
    }
  }

return result;
}

Как это работает

Принцип работы:

Читать полностью ->