Підготовка до співбесіди з JavaScript за допомогою платформи LeetCode. Приклади рішень

Усі статті, обговорення, новини про Front-end — в одному місці. Підписуйтеся на телеграм-канал!

Всім привіт! Мене звати Іван, я фулстек-розробник, останні кілька місяців активно вирішую задачі на LeetCode в перерві між робочими проєктами.

Сьогодні хочу розповісти про новий пул задач на платформі LeetCode, які підійдуть для практикування навичок з використанням мови програмування JavaScript. Тобто це саме задачі з практики, які доводиться вирішувати на фронтенді та бекенді за допомогою коду.

Також поділюся досвідом вирішення нового пула задач на платформі, і хочу закликати спільноту доєднатися до 30-денного челенджу — JavaScript 30 Days Challenge. Але про це поговоримо наприкінці статті.

Новий розділ на LeetCode — задачі з JavaScript

Нещодавно LeetCode проводив опитування, а також запит на приклади задач, які можна було б додати у розділ з JavaScript. Тож пройшов час і ці задачі після тестування стали доступними для вирішення та практики.

Щоб перейти в цей розділ, на головній сторінці необхідно вибрати Problems у меню, а потім натиснути на фільтр JavaScript.

Зображення 1. Список JavaScript задач на платформі LeetCode

На сьогодні там вже опубліковано 36 задачок. Задачі, які пропонуються, базуються на навичках та знаннях мови програмування JavaScript. Наприклад, треба написати свою реалізацію методу JSON.stringify або імплементувати Debounce, Throttle, Proxy тощо.

Багато задач на самостійну імплементацію вбудованих методів, або вже існуючих в бібліотеках, таких як в прикладі вище, а також достатньо тривіальні — Array may, reduce, filter. Прив’язка контексту тощо.

Загалом мені сподобались ці задачки саме тим, що можна повторити основи основ JS — так само, як перед підготовкою до співбесіди. Типи даних, замикання, проміси, таймаути, event emitter — все там є. Тож однозначно можу рекомендувати таку практику як для новачків, які тільки починають розбиратися з JS, так і для досвідчених гуру.

Скажу відверто, що деякі задачі змушують попотіти, треба гарно подумати над їх вирішенням: спочатку — брутфорс, або наївне рішення, а потім ще й знайти відповідь оптимальним шляхом. Таким чином додається ефект змагання, хоча б із самим собою, щоб покращити час виконання — це також плюс.

Від себе ще додам, що час, який заміряє платформа під час виконання не завжди збігається з дійсністю, тому не треба засмучуватися, коли оптимальне за алгоритмом рішення виконується довше ніж із використанням вбудованих методів.

Розбираємо деякі задачі та приклади рішення

Розгляньмо конкретний приклад. Задача 2620. Counter. За умовою, ми маємо ціле число n, потрібно повернути функцію-лічильник. Ця функція спочатку має повернути n, потім додати 1 до попереднього значення і далі повертати інкрементоване значення (n, n + 1, n + 2 тощо).

Ізі задача? Навіщо її розглядати? Гарне питання. Але при вирішенні подібних задачок ми маємо багато простору для творчості та експериментів. Як думаєте, скільки способів можна реалзівати для вирішення цієї задачі? Я спробував 4, але впевнений, що їх є більше. Розгляньмо кожен з них.

Варіант 1. Замикання

var createCounter = function(n) {
  let count = n;
  return function() {
    return count++;
  };
};

Тут, думаю, коментарі не потрібні — замкнули count з початковим n, і далі повертаємо та інкрементуємо одночасно, за допомогою постфіксного оператора.

Варіант 2. Варіація замикання — використовуємо параметр-змінну для збереження результатів

var createCounter = function(n) {
    return function() {
        return n++
    };
};

Виходить, що n, передане як параметр, буде збережено для наступного виклику. Тому що завдяки функції, це вийде те саме замикання, та для зберігання використаємо параметр-змінну, що початково передали для виклику.

Варіант 3. Generator

Зі свого досвіду я не часто бачив їх застосування, але позаяк ми експериментуємо — йдемо читати теорію на сайт Mozilla Developer Network.

Generator — об’єкт, що повертається генераторною функцією та задовольняє два протоколи — ітерабельний та ітераторний. Тобто цей об’єкт може повертати якесь значення за допомогою оператора yield. Стає ще цікавіше. Розглянемо наш приклад:

var createCounter = function(n) {
  const counter = function* () {
    let count = n;
    while (true) {
      yield count;
      count += 1;
    }
  }
  const iterator = counter();
  return () => iterator.next().value;
};

Тобто в нас буде нескінченний цикл (але загалом, його можна обмежити, та для нашої задачі це теж підійде), і в ньому, в циклі, ми будемо повертати поточне значення за допомогою оператора yield!

Забігаючи наперед, скажу, що вже встиг попрактикуватися з генераторами у деяких задачах, наприклад, на обхід матриць, коли ми робимо BFS (Breadth First Search) або DFS (Depth First Search). Нам потрібно завжди дивитись на сусідню клітинку — зліва, справа, зверху та знизу. За допомогою генератора можна це дуже гарно оформити.

Ну, і також я зрозумів, що були часи, коли я успішно міг використовувати генераторні функції для поліпшення коду — ніколи не пізно дізнаватися щось нове!

Варіант 4. Поточний контекст this

var createCounter = function(n) {
    this.count = n;
    return function() {
        return this.count++;
    };
};

Ідея майже та сама, як і для замикання, але ми явно використовуємо контекст this.

Тож моя думка полягає в тому, що, розібравшись зі складними речами на простому прикладі, їх вже буде легше імплементувати і для комплекснішого випадку.

2627. Debounce

Це класична задача, позначена рівнем medium, яка має широку практику у фронтенд-розробці. До речі, завдяки цій задачі я дізнався, що вперше цей підхід був запрограмований у 2009 році, за авторства John Hann, та названий так автором.

За умовою, дана функція fn та час t в мілісекундах, потрібну повернути «відбивану»/ відкладену версію цієї функції. Це функція, виконання якої відкладено у часі на інтервал t, а також чиє виконання буде перервано у разі повторного виклику тієї ж самої функції у проміжок часу від першого виклику до t. Також ця функція повинна отримувати передані параметри.

Практичний приклад: пошук будь-чого — поки користувач вводить дані, ми будемо «відбивати» кожний попередній виклик функції, якщо введення було виконано у проміжок часу t. Таким чином ми зменшимо навантаження на бекенд і відправлятимемо запит, коли користувач вже завершить введення даних.

var debounce = function(fn, t) {
 let timer;
 return function(...args) {
   if (timer != null) clearTimeout(timer);


   timer = setTimeout(() => {
     fn(...args)
   }, t);


   return timer;
 }
};

Замикання, зберігаємо timer, спочатку перевіряємо, чи є вже попереднє заплановане виконання. Потім — нове планування за допомогою setTimeout та повернення цієї функції, загорнутої у timer.

Важливий крок — використання clearTimeout. Бо через те, що setTimeout додає виклик у чергу ((макро) таска?), якщо ми навіть перепишемо timer, то наш попередньо запланований виклик все одно буде виконано при добіганні інтервалу.

2630. Memoize II

Це задача з рівня hard. Таких поки що всього три, але які ж вони! За умовою, нам потрібно для заданої функції fn повернути мемоізовану версію цієї функції. Мемоізована версія — або функція з кешуванням попередніх результатів — це така, яка не буде викликана двічі для тих самих параметрів, а буде повернута з кеша.

Сказано, що fn може бути будь-якою функцією та не обмежена за типами параметрів, які вона приймає. Також вхідні параметри будуть рахуватися як ідентичні при виконанні для них суворої рівності — і за значенням, і за типом (===).

function memoize(fn) {
  const memo = new Map();
  function explore(memo, args, index){
      if (index === args.length) {
          if (memo.has('result')) {
              return memo.get('result');
          }
          const result = fn(...args);
          memo.set('result', result)
          return result;
      }
      if (!memo.has(args[index])) {
          memo.set(args[index], new Map());
      }
      return explore(memo.get(args[index]), args, index + 1);
  }
  return function (...args) {
      return explore(memo, args, 0);  
  }
}

Використовуємо Map для збереження попередніх результатів. Пишемо метод для пошуку попередньої відповіді, куди будемо передавати Map, args та індекс.

Індекс — це параметр для відстежування інших параметрів, переданих у функцію (вибачте за каламбур). Тобто починаємо з нуля, а далі дивимося рекурсивно на наступний індекс, поки не добіжимо кінця масиву аргументів, і якщо в нас буде збережений результат, повернемо його.

Інакше виконаємо fn та запишемо результат у кеш. Для кожного аргументу зберігаємо свій особистий Map, продовжуємо йти масивом аргументів за наступним індексом.

Підсумки

Ми розглянули новий пул задач на платформі LeetCode, спрямованих на практику навичок у розробці з мовою програмування JavaScript. Я вважаю, що чим більше і доступніше будуть такі задачі, тим краще, тому що з деякими особливостями цієї мови програмування ми можемо зіткнутися на таких платформах, або вже на проді, коли буде запізно:)

За час практикування з вирішенням задач, я звик до інтерфейсу та неідеальності платформи, тому мені важко об’єктивно оцінити її зручність. Платформа-конкурент для практики подібних задач — Bigfrontend.

Додам, що попри поточну посаду та кількість років досвіду, розбиратися з основами та фундаментом тої мови програмування, з якою працюєте постійно — це гарна практика. Також, більшість із запитань, які зараз опубліковані, я вже бачив на співбесідах, тож зайва практика тут точно не завадить, а навпаки, допоможе у підготовці до майбутніх перевірок ваших знань та вмінь!

Про 30-денний челендж

Наостанок: починаючи з 5 травня, платформа запустила денний виклик на 30 днів. Для участі вам потрібно вирішувати завдання, які публікуються на спеціальній сторінці, впродовж 30 днів. Якщо якусь із задач пропустили — її потрібно встигнути вирішити до кінця челенджу.

Для зручності можна зареєструватися, щоб отримувати лист-нагадування, а також у випадку вирішення усіх задач за 10-20-30 днів — отримати бонус у вигляді поінтів платформи. Усі деталі є за посиланням.

Для тих хто тільки починає розбиратися, або кому цікаво подивитися за напрямком думок при вирішенні подібних задач на платформі LeetCode за допомогою мови програмування JavaScript — запрошую підписатися на мій YouTube-канал українською мовою. Коли шукав подібні канали, то знайшов не так багато саме за цією темою, а із задачами JavaScript їх ще менше.

У відео розглядаю умови, приклади задач, проговорюю теорію і потім пишу код, іноді з помилками, але їх виправлення також присутнє. Сподіваюсь, таким чином допоможу українському ком’юніті формувати базу корисних ресурсів з теорією про JavaScript та вирішенню задач на платформі LeetCode. Посилання на мій канал та плейліст саме із дейлі челендж. Доєднуйтеся для вирішення проблем разом зі мною!

Корисні посилання

  • You Don’t Know JS — крутезна книга фундаментальних знань з мови програмування JavaScript. Англійською, це вже друга версія.
  • @CoderbyteDevelopers — YouTube-канал з теорією та прикладами на структури даних та алгоритми, використовуючи JavaScript. Також англійською, дуже зрозуміле пояснення, гарні приклади.
  • Code with Us! LeetCode Study Room — сторінка з описом програми LeetCode Study Room: заповнюєте форму, отримуєте посилання на Дискорд-сервер — і вирішуєте задачки кожного дня та відмічаєте це. Роздають поінти, але головне — можливість поставити питання або обговорити варіанти рішень. Якщо не вистачало саме групи для спілкування з вирішення проблем — непоганий варіант. Мова спілкування — англійська.
👍ПодобаєтьсяСподобалось21
До обраногоВ обраному21
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

a хто має досвід створення leetcode study room with daily chek-ins поділіться як таке можна зробити ?
чи як можна приєднатись до уже існуючої?

Посилання вище — то офіційні кімнати від платформи LeetCode — але хто забороняє створити свою кімнату із монополією?) Давайте спробуємо зробити разом. Посилання на діскорд сервер discord.gg/ynPaDaK6dD

варіант 2 першої задачки це теж замикання. нічим не брудніше, ніж варіант 1. глобального скоупу там теж нема.

«Брудний», бо мені особисто не хотілось би бачити такий код у продакшені, хоча б через те, що це погано читається. Так воно буде працювати, але навіщо? «Глобальний скоуп» — бо рішення буде викликатися із одним і тім самим середовищем для різних тест-кейсів, саме це мав на увазі

Іване, ви б могли провірити мою гіпотезу, що ці 2 варіанти будуть поводитися абсолютно ідентично ?
з того, що я знаю, вони по функціоналу не мають різниці взагалі.

тому й кажу, що там ніякого глобального чи глобальнішого скоупу нема.

Так вона працює, я це підтвердив. Можу зрозуміти що я своїм глобальним скопом трохи ввів у оману. Але загалом коли про це казав мав на увазі отаке — коли ми робимо змінну глобальною і далі в рамках саме середовища запуску LeetCode використовуємо її — ось гарний приклад від користувача Brute Lee на платформі leetcode.com/...​ix/submissions/961885374

пропоную вам виправити текст статті (шляхом видалення 2 способу), бо слово «глобальний» може спровокувати інших людей написати вам образливий комент.
чому я одразу помітив помилку, бо колись вчив замикання по книжці. і там наводилося 3 варіанти (які по суті технічно всі однакові).
і ваші 2 варіанти якраз класично лягають в перші 2 варіанти.

ну й загалом локальні змінні функції і параметри функції з того, що я пригадую, поводяться ідентично. тому я й одразу звернув увагу.

Дякую за фідбек і за рекомендацію! Подумаю як це змінити

Ви справді вважаєте що хтось може ще й образливий коментар написати? (риторично, без сарказму)

так. я сам хотів це зробити. він не був би аж таким сильно образливим. але з підйобом.

Змінив текст, ще раз дякую за пильність!

Полезная штука.
Учит осмысленно походить к коду.

Аби б ще десь в реальному житті ці задачі зустрічались, а не тільки на літкоді

Воно і так, і трошечки не так) Задачі рівня Easy — частіше за все саме те с чим стикаємось повсякденно, прохід матриць, обробка строкових літералів, тощо. Стосовно нових задач — в тому чи іншому вигляді це також набір задач із реального життя. Загалом користь задач саме в усвідомленому практикуванні знань по Структурам даних та алгоритмам

В реальному житті зустрічаються на співбесідах.

Так це теж правда!

Чекаю коментарів, що всі ці літкоди фігня, а питання про Spring, як створити прототип всередині сінглтона, то кул

гуглиться 5хв, а chatgpt видає за 1 хв) Цікаво навіщо задавати такі питання на співбесіді?

якщо хтось себе заставив робити літкод щодня поділіться як у вас це получилось

Тримаюсь 102 дні, 238 задач 112/102/24 — Easy/Medium/Hard, 873 сабмішена. Вирішив прокачатися по ДСА, поки є перерва з проектами, і якось воно потім затягнуло. Важко починати, багато чого незрозуміло, але потроху, починаючи з ізі, деякі медіум і хард, що будуть схожі/або скомпоновані з легших. Також обов’язково теорія, і ставити цілі. Мені допомогло додатись в чат Study Room, на нього є посилання. Знаю ще варіанти — гуртуватись із колегами, друзями і потім проводити мок інтерв’ю. Я замість цього почав записувати відео
Доречі в мене вже є створений Діскорд-сервер. Думав використати його для пошуку однодумців, підтримувати, допомагати один одному. Але поки що не знаю с чого починати

102 дні, 238 задач. Задачі вирішувались в час між проектами. Нескладна арифметика каже що у вас за 102 днів було по 2-3 проекта на день. Напевно дуже цікаві проекти були

а інвайт на діскор-сервер можна?

Якщо ви себе заставляєте — просто киньте.
Який сенс над собою знущатися?
Якщо у вас нема внутрішньої цікавості, або мети яка рухає вами — ви будете робити задачі на відвали, аби зелені квадратики не були сірими)

Если не интересен сам процесс, то каждый день себя не заставишь 100%.
Но в общем это и не надо, если есть база по алгоритмом и структурам данных. И на работе уже успел заточить мозг под решение алгоритмических задач (а это мы каждый день делаем).

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

Цікава стаття! Раніше сидів тільки на Codewars, прийшов час спробувати Leetcode.

Можу також порадити Codewars, де є задачі на мемоізацію. До того ж, там вони починаються з 3 рівня складності, що може буди більш зручним в освоєнні

Дякую, за пораду! Треба буде подивитись на Codewars 👀

Підписатись на коментарі