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

Подготовка

  • Вопросы6104
  • Задачи30

Аналитика

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

Практика

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

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

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

Подготовка

  • Вопросы6104
  • Задачи30

Аналитика

  • 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-бот

Задачи по Frontend Developer

Развернуть слова в строке
1.0 Junior🔥 25💬 1

Решение

Задача на разворот слов — простая, но требует внимательности к деталям. Покажем несколько способов реализации.

Решение 1: map + split/join (самый читаемый)

Способ с использованием функциональных методов:

function reverseWords(str: string): string {
  return str
    .split(" ")           // Разбиваем на слова
    .map(word =>          // Разворачиваем каждое слово
      word.split("").reverse().join("")
    )
    .join(" ");           // Собираем обратно
}

console.log(reverseWords("Hello World"));
// "olleH dlroW"

console.log(reverseWords("The quick brown fox"));
// "ehT kciuq nworb xof"

console.log(reverseWords("a b c"));
// "a b c"

console.log(reverseWords("JavaScript"));
// "tpircSavaJ"

Анализ:

  • Временная сложность: O(n) — проходим по каждому символу
  • Пространственная сложность: O(n) — новые строки
  • Читаемость: Отличная — сразу видна логика
  • Масштабируемость: Хорошая — легко изменить
Читать полностью ->
Группировка массива объектов по ключу
1.0 Junior🔥 25💬 1

Решение: Группировка массива объектов по ключу

Практическое применение

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

Базовое решение

function groupBy(arr, key) {
  return arr.reduce((result, item) => {
    const groupKey = item[key];
    
    if (!result[groupKey]) {
      result[groupKey] = [];
    }
    
    result[groupKey].push(item);
    return result;
  }, {});
}

Версия с поддержкой функции

Читать полностью ->
Реализовать функцию memoize
1.7 Middle🔥 24💬 1

Решение

Задача на бесконечный скролл с Intersection Observer API — показывает понимание производительности и современных браузерных API.

Решение 1: Базовый бесконечный скролл

import React, { useState, useEffect, useRef, useCallback } from "react";

interface InfiniteScrollProps<T> {
  loadMore: (page: number) => Promise<T[]>;
  renderItem: (item: T, index: number) => React.ReactNode;
  hasMore?: boolean;
  offset?: number;
}

function InfiniteScroll<T>({
  loadMore,
  renderItem,
  hasMore = true,
  offset = 500,
}: InfiniteScrollProps<T>) {
  const [items, setItems] = useState<T[]>([]);
  const [page, setPage] = useState(0);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);
  const observerTarget = useRef<HTMLDivElement>(null);
  const loadingRef = useRef(false);

// Загрузка данных
  const handleLoadMore = useCallback(async () => {
    if (loadingRef.current || !hasMore) return;
Читать полностью ->
Реализовать функцию curry
2.0 Middle🔥 24💬 1

Решение

Задача на функцию curry демонстрирует функциональное программирование.

Решение 1: Базовый curry

function curry(fn) {
  const arity = fn.length;
  return function curried(...args) {
    if (args.length >= arity) {
      return fn(...args);
    }
    return (...nextArgs) => curried(...args, ...nextArgs);
  };
}

function sum(a, b, c) {
  return a + b + c;
}

const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3));      // 6
console.log(curriedSum(1, 2)(3));      // 6

Как работает

  1. fn.length получает количество параметров
  2. Накапливаем аргументы через замыкание
  3. Когда достаточно - вызываем функцию
  4. Поддерживает смешанный стиль

Практический пример

function fetchAPI(baseURL, headers, endpoint, query) {
  const url = new URL(endpoint, baseURL);
  return fetch(url.toString(), { headers });
}
Читать полностью ->
Реализовать функцию throttle
1.7 Middle🔥 24💬 1

Решение: Реализация функции throttle

Различие между throttle и debounce

В отличие от debounce, который откладывает вызов до конца периода спокойствия, throttle выполняет функцию максимум один раз за заданный интервал времени. Это критично при обработке часто возникающих событий типа scroll или mousemove, где нужна регулярная обработка без перегрузки.

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

function throttle(fn, interval) {
  let lastCallTime = 0;
  
  return function throttled(...args) {
    const now = Date.now();
    
    if (now - lastCallTime >= interval) {
      lastCallTime = now;
      fn.apply(this, args);
    }
  };
}

Версия с трейлингим вызовом

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

Решение: Реализация Promise.all

Понимание задачи

Promise.all — это фундаментальный метод для работы с несколькими асинхронными операциями одновременно. Он ждёт выполнения всех промисов и возвращает единственный промис с массивом результатов в исходном порядке. Это критически важно для сценариев, где нужны результаты нескольких асинхронных вызовов перед дальнейшей обработкой.

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

Читать полностью ->
Реализовать пользовательский хук useDebounce
2.2 Middle🔥 20💬 1

Решение

Задача на создание хука useDebounce — классическая для показа понимания асинхронности, эффектов и очистки ресурсов в React. Создадим несколько версий.

Решение 1: Базовый useDebounce

Простая и понятная реализация:

import { useState, useEffect } from "react";

function useDebounce<T>(value: T, delay: number): T {
  const [debouncedValue, setDebouncedValue] = useState<T>(value);

useEffect(() => {
    // Устанавливаем таймер
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

// Очищаем таймер при изменении value или размонтировании
    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

return debouncedValue;
}

export default useDebounce;
Читать полностью ->
Подсчёт гласных в строке
2.2 Middle🔥 20💬 1

Решение

Задача на подсчёт гласных букв — простая на первый взгляд, но имеет несколько интересных вариантов реализации. Покажем разные подходы.

Решение 1: Регулярные выражения

Самый компактный и понятный способ:

function countVowels(str: string): number {
  // Регулярное выражение: ищем гласные (любой регистр)
  const vowels = str.match(/[aeiou]/gi);
  return vowels ? vowels.length : 0;
}

console.log(countVowels("hello"));              // 2
console.log(countVowels("AEIOU"));              // 5
console.log(countVowels("rhythm"));             // 0
console.log(countVowels("The quick brown fox")); // 5
console.log(countVowels(""));                   // 0

Анализ:

  • [aeiou] — класс символов, гласные английского алфавита
  • g флаг — глобальный поиск (все совпадения)
  • i флаг — игнорировать регистр
  • Временная сложность: O(n)
  • Очень компактно и читаемо

Решение 2: Цикл с массивом

Отсутствие зависимостей от regex:

Читать полностью ->
Проверка строки на палиндром
2.0 Middle🔥 20💬 1

Решение

Задача на проверку палиндрома — классическая в собеседованиях. Требует понимания строк, регулярных выражений и оптимизации по памяти.

Базовое решение

Сначала решим задачу стандартным способом:

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

Анализ:

  • toLowerCase() — игнорируем регистр
  • replace(/[^a-z0-9]/g, "") — убираем всё кроме букв и цифр
  • Сравниваем исходную с перевёрнутой версией
  • Временная сложность: O(n), пространственная: O(n) (создаём новую строку)

Оптимизированное решение (два указателя)

Для бонуса решим без создания дополнительной строки:

Читать полностью ->
Реализовать функцию debounce
2.0 Middle🔥 20💬 1

Решение функции debounce

Что такое debounce?

Debounce — это техника оптимизации, которая откладывает выполнение функции до тех пор, пока не пройдёт определённое время с момента последнего вызова. Это критически важно при обработке часто вызываемых событий (изменение input, скролл, resize), чтобы избежать множественных ненужных вычислений.

Реализация базовой версии

function debounce(fn, delay) {
  let timeoutId;
  
  return function debounced(...args) {
    // Очищаем предыдущий таймер
    clearTimeout(timeoutId);
    
    // Устанавливаем новый таймер
    timeoutId = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

С методом cancel() и улучшениями

Читать полностью ->
Определить вывод кода с замыканиями и циклом
1.7 Middle🔥 19💬 1

Решение

Эта классическая задача на понимание замыканий, области видимости (var vs let) и асинхронного выполнения JavaScript.

Что выведет код?

for (var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}

// Результат: 3 3 3

Почему 3, 3, 3?

  1. Цикл выполняется синхронно, переменная i увеличивается до 3
  2. setTimeout() выполняется асинхронно (через 1000мс)
  3. К моменту выполнения callback'ов цикл уже завершён, i = 3
  4. Все три callback'а ссылаются на одну переменную i в глобальной области видимости
  5. Поэтому все выводят 3

Способ 1: Использовать let вместо var

for (let i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}

// Результат: 0 1 2

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

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

Способ 2: Замыкание через IIFE

Читать полностью ->
Создать форму регистрации с валидацией на React
1.7 Middle🔥 19💬 1

Решение

Задача на валидирующую форму регистрации — классическая, показывает понимание управления состоянием и валидации в React. Создадим полное решение.

Решение 1: Базовая форма с валидацией

import React, { useState, useMemo } from "react";

interface FormErrors {
  email?: string;
  password?: string;
  confirmPassword?: string;
  username?: string;
}

interface FormData {
  email: string;
  password: string;
  confirmPassword: string;
  username: string;
}

function RegistrationForm() {
  const [formData, setFormData] = useState<FormData>({
    email: "",
    password: "",
    confirmPassword: "",
    username: "",
  });

// Валидация
  const errors = useMemo<FormErrors>(() => {
    const newErrors: FormErrors = {};
Читать полностью ->
FizzBuzz
2.2 Middle🔥 19💬 1

Решение

FizzBuzz — классическая задача на собеседованиях, проверяющая логику условий и способность обобщать код. Решим её в несколько уровней сложности.

Базовое решение

Прямолинейный подход с условными операторами:

function fizzBuzz(n: number): string[] {
  const result: string[] = [];
  
  for (let i = 1; i <= n; i++) {
    // Проверяем делимость на 15 первой (это и 3, и 5)
    if (i % 15 === 0) {
      result.push("FizzBuzz");
    } else if (i % 3 === 0) {
      result.push("Fizz");
    } else if (i % 5 === 0) {
      result.push("Buzz");
    } else {
      result.push(i.toString());
    }
  }
  
  return result;
}

console.log(fizzBuzz(15).join(", "));
// 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz

Анализ:

  • Временная сложность: O(n)
  • Пространственная сложность: O(n)
  • Главное: проверить 15 перед 3 и 5, иначе будет неправильно

Улучшенное решение: Конкатенация

Более элегантный способ без многоуровневых условий:

Читать полностью ->
Проверка двух строк на анаграмму
2.0 Middle🔥 19💬 1

Решение

Задача на проверку анаграмм — проверяет понимание структур данных и алгоритмов сортировки. Это часто встречается на собеседованиях, так как показывает знание оптимизации.

Решение 1: Сортировка

Самый интуитивный подход — отсортировать буквы и сравнить:

function isAnagram(str1: string, str2: string): boolean {
  // Очищаем строки: удаляем пробелы, приводим к нижнему регистру
  const clean1 = str1.toLowerCase().replace(/\s/g, "");
  const clean2 = str2.toLowerCase().replace(/\s/g, "");
  
  // Проверяем длину
  if (clean1.length !== clean2.length) {
    return false;
  }
  
  // Сортируем буквы и сравниваем
  const sorted1 = clean1.split("").sort().join("");
  const sorted2 = clean2.split("").sort().join("");
  
  return sorted1 === sorted2;
}

Анализ:

  • Временная сложность: O(n log n) из-за сортировки
  • Пространственная сложность: O(n) для новых строк
  • Простота: высокая, код читаемый

Решение 2: Подсчёт частоты (оптимальное)

Читать полностью ->
Развертывание вложенного объекта в плоскую структуру
2.0 Middle🔥 19💬 1

Решение: Развертывание вложенного объекта в плоскую структуру

Задача и её применение

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

Базовое решение

function flattenObject(obj, prefix = '') {
  const result = {};
  
  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;
}

Версия с поддержкой массивов

Читать полностью ->
Создать компонент бесконечного скролла на React
2.0 Middle🔥 18💬 1

Решение: Компонент бесконечного скролла на React

Базовое решение с Intersection Observer

Компонент InfiniteScroll использует IntersectionObserver для определения когда пользователь прокрутил к концу списка. При этом вызывается callback onLoadMore для загрузки новых данных.

Ключевые компоненты

  1. Intersection Observer: observerTarget ref отслеживает видимость элемента в конце списка. Когда элемент становится видимым, вызывается handleLoadMore.

  2. Защита от дублей: isLoadingRef предотвращает множественные одновременные запросы. Проверяем флаг перед загрузкой.

  3. rootMargin: параметр offset задаёт расстояние в пиксель до конца списка, при котором нужно загружать (предзагрузка).

  4. Индикаторы: отображаем спиннер при загрузке, сообщение об ошибке, кнопку Retry и сообщение о конце списка.

Использование

Читать полностью ->
Создать React-компонент таблицы с сортировкой
2.0 Middle🔥 18💬 1

Решение

Задача на React-компонент таблицы с сортировкой — показывает понимание хуков, состояния и работы с данными. Создадим несколько версий.

Решение 1: Базовая таблица с сортировкой

import React, { useState, useMemo } from "react";

interface Column<T> {
  key: keyof T;
  label: string;
}

interface SortableTableProps<T extends Record<string, any>> {
  data: T[];
  columns: Column<T>[];
}

type SortOrder = "asc" | "desc" | null;

function SortableTable<T extends Record<string, any>>({
  data,
  columns,
}: SortableTableProps<T>) {
  const [sortKey, setSortKey] = useState<keyof T | null>(null);
  const [sortOrder, setSortOrder] = useState<SortOrder>(null);
Читать полностью ->
Числа Фибоначчи с мемоизацией
2.0 Middle🔥 18💬 1

Решение

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

Проблема: Наивная рекурсия

Без оптимизации это работает очень медленно:

// ❌ НЕЭФФЕКТИВНО: O(2^n)
function fibonacciNaive(n: number): number {
  if (n <= 1) return n;
  return fibonacciNaive(n - 1) + fibonacciNaive(n - 2);
}

// fibonacci(50) займет вечность! Будет 2^50 вызовов
// fibonacci(20) уже ~ 1 миллион вызовов

Почему медленно: Одно и то же значение вычисляется много раз. Например, fibonacci(5) вычисляет fibonacci(3) дважды, fibonacci(2) трижды и т.д.

Решение 1: Мемоизация (Caching)

Запоминаем результаты вычислений:

Читать полностью ->
Реализовать собственный метод Array.prototype.map
2.3 Middle🔥 18💬 1

Решение: Реализация Array.prototype.map

Понимание map

Метод map — один из столпов функционального программирования в JavaScript. Он создаёт новый массив, применяя функцию преобразования к каждому элементу исходного массива. Понимание внутреннего устройства помогает глубже освоить работу с данными.

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

function myMap(arr, callback) {
  const result = [];
  
  for (let i = 0; i < arr.length; i++) {
    result.push(callback(arr[i], i, arr));
  }
  
  return result;
}

С поддержкой thisArg

function myMap(arr, callback, thisArg) {
  const result = [];
  
  for (let i = 0; i < arr.length; i++) {
    // Вызываем callback с заданным контекстом
    result.push(callback.call(thisArg, arr[i], i, arr));
  }
  
  return result;
}

TypeScript версия

Читать полностью ->
Создать функцию для валидации email
1.7 Middle🔥 17💬 1

Решение

Задача на валидацию email — показывает понимание регулярных выражений и строк.

Решение 1: С регулярным выражением

function isValidEmail(email) {
  const regex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
  return regex.test(email);
}

// Использование
console.log(isValidEmail("test@example.com"));      // true
console.log(isValidEmail("user.name@domain.org"));  // true
console.log(isValidEmail("invalid"));               // false
console.log(isValidEmail("@nodomain.com"));         // false
console.log(isValidEmail("test@.com"));             // false
console.log(isValidEmail("test@domain"));           // false

Объяснение regex:

  • ^ — начало строки
  • [a-zA-Z0-9._+-]+ — одна или больше букв, цифр, точка, подчёркивание, плюс, дефис
  • @ — обязательный символ @
  • [a-zA-Z0-9.-]+ — домен с буквами, цифрами, точками, дефисом
  • \. — обязательная точка
  • [a-zA-Z]{2,} — минимум 2 буквы после точки (TLD)
  • $ — конец строки
Читать полностью ->
Создать адаптивное навигационное меню
2.0 Middle🔥 17💬 1

Решение

Задача на адаптивное навигационное меню — показывает понимание responsive design, accessibility и работы с состоянием. Создадим несколько версий.

Решение 1: React с Tailwind CSS

import React, { useState } from "react";

interface NavLink {
  label: string;
  href: string;
  icon?: string;
}

interface NavbarProps {
  brand: string;
  links: NavLink[];
  currentPath?: string;
}

function Navbar({ brand, links, currentPath = "/" }: NavbarProps) {
  const [isOpen, setIsOpen] = useState(false);

const toggleMenu = () => {
    setIsOpen(!isOpen);
  };

const closeMenu = () => {
    setIsOpen(false);
  };
Читать полностью ->
Объединение перекрывающихся интервалов
2.3 Middle🔥 17💬 1

Решение: Объединение перекрывающихся интервалов

Концепция и подход

Эта классическая задача часто встречается в интервьюшах и имеет элегантное решение. Ключевая идея — отсортировать интервалы по началу, затем пройти по ним один раз, объединяя те, которые перекрываются. Сложность O(n log n) достигается за счёт сортировки.

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

Читать полностью ->
Реализовать простой роутер на JavaScript
1.7 Middle🔥 16💬 1

Решение

Задача на простой роутер — показывает понимание History API, регулярных выражений и управления состоянием приложения.

Решение 1: Базовый роутер

class Router {
  constructor() {
    this.routes = [];
    this.current = null;
    this.init();
  }

add(path, callback) {
    this.routes.push({ path, callback });
  }

navigate(path) {
    window.history.pushState({}, "", path);
    this.handleRoute(path);
  }

match(path) {
    for (let route of this.routes) {
      const regex = this.pathToRegex(route.path);
      const match = regex.exec(path);

if (match) {
        const params = this.extractParams(route.path, match);
        return { callback: route.callback, params };
      }
    }
    return null;
  }

pathToRegex(path) {
    const pattern = path
      .replace(/\//g, "\\/')
      .replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, "([^/]+)");
    return new RegExp("^" + pattern + "$");
  }
Читать полностью ->
Найти уникальный элемент в массиве
2.0 Middle🔥 15💬 1

Решение: Найти уникальный элемент в массиве

Проблема и подход

Эта задача демонстрирует мощь побитовых операций для оптимального решения. Наивный подход с хеш-таблицей требует O(n) пространства, но побитовая операция XOR позволяет решить за O(1) памяти благодаря уникальному свойству этой операции.

Почему XOR работает?

Операция XOR (исключающее ИЛИ) имеет уникальное свойство:

  • a ^ a = 0 (число XOR с самим собой равно нулю)
  • a ^ 0 = a (число XOR с нулём равно самому числу)
  • a ^ b = b ^ a (XOR коммутативна)
  • (a ^ b) ^ c = a ^ (b ^ c) (XOR ассоциативна)

Поэтому если XOR-ить все числа массива, все пары исчезнут (станут 0), и останется только уникальное число.

Оптимальное решение с XOR

function findUnique(arr) {
  return arr.reduce((result, num) => result ^ num, 0);
}

Развёрнутая версия с пояснениями

Читать полностью ->
Глубокое клонирование объекта
2.0 Middle🔥 13💬 1

Решение: Глубокое клонирование объекта

Почему глубокое клонирование важно?

При работе со сложными структурами данных в JavaScript обычное присваивание создаёт ссылку на объект, а не его копию. Изменение значений во вложенных объектах или массивах повлияет на оригинал. Глубокое клонирование создаёт полностью независимую копию, включая все вложенные структуры.

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

function deepClone(obj) {
  // Обработка примитивов
  if (obj === null || typeof obj !== "object") {
    return obj;
  }
  
  // Обработка Date
  if (obj instanceof Date) {
    return new Date(obj.getTime());
  }
  
  // Обработка Array
  if (Array.isArray(obj)) {
    return obj.map(item => deepClone(item));
  }
  
  // Обработка Object
  const cloned = {};
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloned[key] = deepClone(obj[key]);
    }
  }
  
  return cloned;
}

Версия с обработкой циклических ссылок

Читать полностью ->
Реализовать бинарный поиск
2.0 Middle🔥 11💬 1

Решение

Задача на бинарный поиск — классическая для показа понимания алгоритмов и оптимизации.

Решение 1: Итеративный бинарный поиск

function binarySearch(arr, target) {
  let left = 0;
  let right = arr.length - 1;

while (left <= right) {
    const mid = Math.floor((left + right) / 2);
    const midValue = arr[mid];

if (midValue === target) {
      return mid;
    } else if (midValue < target) {
      left = mid + 1;  // Ищем справа
    } else {
      right = mid - 1; // Ищем слева
    }
  }

return -1; // Не найдено
}

// Тесты
console.log(binarySearch([1, 2, 3, 4, 5, 6, 7, 8, 9], 5)); // 4
console.log(binarySearch([1, 2, 3, 4, 5], 1));              // 0
console.log(binarySearch([1, 2, 3, 4, 5], 5));              // 4
console.log(binarySearch([1, 2, 3, 4, 5], 6));              // -1
console.log(binarySearch([], 1));                           // -1
Читать полностью ->
Реализовать пользовательский хук useFetch
2.2 Middle🔥 11💬 1

Решение

Задача на создание хука useFetch — показывает понимание работы с API, управления состоянием и жизненным циклом компонента. Создадим несколько версий.

Решение 1: Базовый useFetch

Простая реализация с основной функциональностью:

import { useState, useEffect, useRef } from "react";

interface UseFetchResult<T> {
  data: T | null;
  loading: boolean;
  error: Error | null;
}

function useFetch<T>(url: string): UseFetchResult<T> {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  const abortControllerRef = useRef<AbortController | null>(null);

useEffect(() => {
    // Создаём AbortController для отмены запроса
    abortControllerRef.current = new AbortController();

const fetchData = async () => {
      try {
        setLoading(true);
        setError(null);
Читать полностью ->
Самая длинная подстрока без повторений
2.0 Middle🔥 10💬 1

Решение: Самая длинная подстрока без повторений

Концепция скользящего окна

Эта задача идеально решается техникой скользящего окна (sliding window). Идея в том, что мы используем два указателя (left и right) для создания окна, которое расширяется и сжимается, всегда сохраняя условие уникальности символов. Это позволяет решить задачу за один проход.

Базовое решение

Читать полностью ->
Реализовать функцию pipe и compose
1.8 Middle🔥 8💬 1

Решение

Задача на pipe и compose — демонстрирует функциональное программирование и преобразование функций.

Что такое pipe и compose?

pipe — слева направо

  • pipe(f, g, h)(x) = h(g(f(x)))

compose — справа налево

  • compose(f, g, h)(x) = f(g(h(x)))

Решение 1: Базовые версии

const pipe = (...fns) => (x) =>
  fns.reduce((acc, fn) => fn(acc), x);

const compose = (...fns) => (x) =>
  fns.reduceRight((acc, fn) => fn(acc), x);

// Использование
const add5 = x => x + 5;
const multiply2 = x => x * 2;
const subtract3 = x => x - 3;

const piped = pipe(add5, multiply2, subtract3);
console.log(piped(10)); // ((10 + 5) * 2) - 3 = 27

const composed = compose(subtract3, multiply2, add5);
console.log(composed(10)); // ((10 + 5) * 2) - 3 = 27

Решение 2: С типизацией TypeScript

type Fn<T, R> = (arg: T) => R;

function pipe<T, R>(...fns: Fn<any, any>[]): (arg: T) => R {
  return (x: T) => fns.reduce((acc, fn) => fn(acc), x);
}
Читать полностью ->
Максимальная сумма подмассива (алгоритм Кадана)
2.0 Middle🔥 6💬 1

Решение: Максимальная сумма подмассива (Алгоритм Кадана)

История и значение

Алгоритм Кадана, разработанный Джеем Кадейном в 1977 году, — один из классических примеров динамического программирования. Он решает задачу о максимальной сумме подмассива за линейное время, что считается оптимальным решением для этой проблемы.

Основная идея

Алгоритм поддерживает две переменные:

  • currentSum — максимальная сумма подмассива, заканчивающегося в текущей позиции
  • maxSum — глобально максимальная сумма найденная до текущего момента

На каждой итерации принимаем решение: либо расширить текущий подмассив, включив текущий элемент, либо начать новый подмассив с текущего элемента.

Базовое решение

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