Skip to content

Commit a5d935d

Browse files
committed
[CON-19/compl] interview-with-ai, 2-nd
Solving +11 task. Working with "arr/obj meth's, IIFE, closures, DOM". Worth noting: - all this interview (note the solutions). FS-dev: B-3 / JS basic
1 parent 01cedb3 commit a5d935d

File tree

4 files changed

+350
-5
lines changed

4 files changed

+350
-5
lines changed

full-stack-dev/3-js-basic/19-conclusion/interview-with-ai/1-first/interview.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
// ** Задание 1:
3+
// Задание 1:
44
// Представь, что мы делаем простой "To-Do" лист.
55
// Напиши одну функцию createTodoItem(text). Эта функция должна:
66
// 1. Принимать один аргумент — text (строка, текст задачи).
@@ -39,7 +39,7 @@ function createTodoItem(text) {
3939

4040
todoList.append(createTodoItem('Погулять с собакой')); // создание элемента и сразу добавление
4141

42-
// ** Задание 2:
42+
// Задание 2:
4343
// Я — твой тимлид. Я вижу, что другой 'джун' написал код для интерактивного списка продуктов. Код работает, но у меня к нему есть претензии.
4444
// Вот его Html:
4545
/*
@@ -110,7 +110,7 @@ productList.addEventListener('click', selectProduct);
110110
//
111111
// productList.addEventListener('click', selectProducts);
112112

113-
// ** Задание 3:
113+
// Задание 3:
114114
// Задача: Напиши функцию getPositiveReviews(reviewsArray), которая в одну цепочку (chaining) делает следующее:
115115
// 1. Отфильтровывает "плохие" отзывы: оставляет только те, у которых rating >= 4.
116116
// 2. Отфильтровывает "пустые" отзывы: оставляет только те, у которых text — это не null и не пустая строка (после trim()'а).

full-stack-dev/3-js-basic/19-conclusion/interview-with-ai/2-second/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<input id="num2" type="number" value="10">
1515
<button id="addBtn">Сложить</button>
1616
<h2>Результат: <span id="result">?</span></h2>
17+
<ul id="todo-list"></ul>
1718
</body>
1819

1920
</html>

full-stack-dev/3-js-basic/19-conclusion/interview-with-ai/2-second/interview.js

Lines changed: 328 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
// ** Задание 1:
3+
// Задание 1:
44
// Как "починить" эти два проблемных вызова?
55
// Как нужно изменить строку setTimeout(counter.increment, 100) с помощью bind(), чтобы this не терялся?
66
// Как можно было бы сразу вызвать standaloneIncrement с правильным this с помощью call()?
@@ -18,7 +18,7 @@ setTimeout(counter.increment.bind(counter), 100); // 1
1818
const standaloneIncrement = counter.increment;
1919
standaloneIncrement.call(counter); // 2
2020

21-
// ** Задание 2:
21+
// Задание 2:
2222
// Согласно разметки напиши JavaScript-код, который:
2323
// 1. Найдёт оба инпута (#num1, #num2), кнопку (#addBtn) и span для результата (#result).
2424
// 2. Добавит обработчик события (addEventListener) на кнопку (#addBtn) по клику ('click').
@@ -39,3 +39,329 @@ addBtnEl.addEventListener('click', () => {
3939
const sum = num1 + num2;
4040
resultEl.textContent = sum;
4141
});
42+
43+
// !! Задание 3:
44+
// Напиши функцию mySort(arr), которая сортирует массив чисел по возрастанию, не используя метод .sort(). Реализуй алгоритм «Пузырьковой сортировки» (Bubble Sort).
45+
// Условия:
46+
// 1. Используй вложенные циклы for.
47+
// 2. Помни про оптимизацию: если массив уже частично отсортирован, лишние итерации не нужны (но для начала сделай хотя бы базовый рабочий вариант).
48+
49+
const bubbleNum = [45, 12, 3, 67, 1, 9];
50+
51+
for (let i = 0; i < bubbleNum.length; i++) {
52+
for (let j = 0; j < bubbleNum.length - 1 - i; j++) {
53+
if (bubbleNum[j] > bubbleNum[j + 1]) {
54+
let temp = bubbleNum[j + 1];
55+
bubbleNum[j + 1] = bubbleNum[j];
56+
bubbleNum[j] = temp;
57+
}
58+
}
59+
}
60+
61+
console.log(bubbleNum); // [1, 3, 9, 12, 45, 67]
62+
63+
// !! Задание 4:
64+
// Пришли "грязные" данные с сервера. В массиве есть дубликаты, пустые строки, null, undefined и прочий мусор.
65+
// Напиши код, который вернет чистый массив уникальных строковых и числовых значений (без 0, null, false и т.д.).
66+
// Требование: Реализуй два разных варианта удаления дубликатов (как раз вспомним способы из конспекта):
67+
// 1. Вариант Modern: Используя структуру Set (самый лаконичный способ).
68+
// 2. Вариант Functional: Используя методы массивов (filter + indexOf).
69+
// Ожидаемый результат (для обоих вариантов): [1, 'apple', 'banana', 'orange']
70+
71+
const dirtyData = [
72+
1,
73+
0,
74+
'apple',
75+
'',
76+
'banana',
77+
null,
78+
'apple',
79+
undefined,
80+
'orange',
81+
0,
82+
false,
83+
'banana',
84+
];
85+
86+
const setDirtyData = [...new Set(dirtyData)].filter(Boolean);
87+
console.log(setDirtyData); // [1, 'apple', 'banana', 'orange']
88+
89+
const filterDirtyData = dirtyData
90+
.filter(Boolean)
91+
.filter((value, index, currentArr) => currentArr.indexOf(value) === index);
92+
console.log(filterDirtyData); // [1, 'apple', 'banana', 'orange']
93+
94+
// !! Задание 5:
95+
// Напиши функцию maskCreditCard(card), которая скрывает все цифры номера карты, кроме последних четырёх.
96+
// Требования:
97+
// 1. Функция должна принимать как строку, так и число (нужна конвертация типов, Source 14 ).
98+
// 2. Используй метод .slice() с отрицательным индексом для получения последних 4 цифр.
99+
// 3. Используй метод .padStart() для заполнения начала строки звёздочками *. Длина итоговой строки должна соответствовать длине исходного номера.
100+
101+
function maskCreditCard(card) {
102+
if (!card) {
103+
return '';
104+
}
105+
106+
if (typeof card === 'string') {
107+
card = card.trim();
108+
}
109+
110+
if (typeof card === 'number') {
111+
card = String(card);
112+
}
113+
114+
if (card.length < 4) {
115+
return '';
116+
}
117+
118+
const cardLength = card.length;
119+
const lastNum = card.slice(-4);
120+
121+
return lastNum.padStart(cardLength, '*');
122+
}
123+
124+
console.log(maskCreditCard('1234567812345678')); // "************5678"
125+
console.log(maskCreditCard(4455667788991234)); // "************1234"
126+
console.log(maskCreditCard('1234')); // "1234"
127+
128+
// !! Задание 6:
129+
// Напиши модуль "Счётчик", который защищает свои данные от прямого изменения извне.
130+
// Требования:
131+
// 1. Используй IIFE (Immediately Invoked Function Expression), чтобы создать закрытую область видимости сразу при объявлении.
132+
// 2. Внутри должна быть приватная переменная count (начальное значение 0).
133+
// 3. Функция должна возвращать объект с тремя методами:
134+
// 4. increase(): увеличивает счётчик на 1.
135+
// 5. decrease(): уменьшает счётчик на 1.
136+
// 6. getValue(): возвращает текущее значение.
137+
// 7. Переменная count не должна быть доступна напрямую (например, counter.count должно быть undefined).
138+
139+
const counterIIFE = (function () {
140+
let count = 0;
141+
142+
return {
143+
increase() {
144+
count++;
145+
},
146+
decrease() {
147+
count--;
148+
},
149+
getValue() {
150+
return count;
151+
},
152+
};
153+
})();
154+
155+
counterIIFE.increase();
156+
counterIIFE.increase();
157+
console.log(counterIIFE.getValue()); // 2
158+
console.log(counterIIFE.count); // undefined
159+
160+
// !! Задание 7:
161+
// У нас есть "Волшебник", который умеет лечить (heal). И есть "Воин", у которого мало здоровья, но нет метода лечения.
162+
// Твоя задача:
163+
// 1. Вылечи воина Артура на 20 HP заклинанием 'Holy Light', используя метод волшебника и .call().
164+
// 2. Вылечи воина Артура ещё на 50 HP заклинанием 'Great Heal', используя метод волшебника и .apply().
165+
// Требование: Обрати внимание на разницу в передаче аргументов между call и apply (запятые vs массив).
166+
167+
const wizard = {
168+
name: 'Merlin',
169+
health: 50,
170+
heal(amount, spellName) {
171+
this.health += amount;
172+
console.log(
173+
`${this.name} исцелен на ${amount} HP с помощью заклинания "${spellName}". Текущее здоровье: ${this.health}`
174+
);
175+
},
176+
};
177+
178+
const warrior = {
179+
name: 'Arthur',
180+
health: 30,
181+
};
182+
183+
wizard.heal.call(warrior, 20, 'Holy Light'); // "Arthur исцелен на 20 HP с помощью заклинания "Holy Light". Текущее здоровье: 50"
184+
wizard.heal.apply(warrior, [50, 'Great Heal']); // "Arthur исцелен на 50 HP с помощью заклинания "Great Heal". Текущее здоровье: 100"
185+
186+
// !! Задание 8:
187+
// Используя тот же объект wizard и его метод heal(amount, spellName), создай новую функцию.
188+
// Требования:
189+
// 1. Создай функцию healArthur50.
190+
// 2. Она должна быть основана на методе wizard.heal.
191+
// 3. Контекст (this) должен быть навсегда привязан к воину Arthur (warrior).
192+
// 4. Первый аргумент (amount) должен быть навсегда зафиксирован на значении 50.
193+
// 5. Вызови полученную функцию healArthur50, передав ей только название заклинания (например, "Super Potion").
194+
// Ожидаемый результат: "Arthur исцелен на 50 HP с помощью заклинания "Super Potion". Текущее здоровье: ..."
195+
196+
const healArthur50 = wizard.heal.bind(warrior, 50);
197+
console.log(healArthur50('Super Potion')); // "Arthur исцелен на 50 HP с помощью заклинания "Super Potion". Текущее здоровье: 150"
198+
199+
// !! Задание 9:
200+
// Твоя задача: Используя только метод .reduce(), создай объект, где ключами будут имена пользователей, а значениями — общая сумма их заказов.
201+
// Алгоритм:
202+
// 1. Начни с пустого объекта {} (initialValue).
203+
// 2. На каждой итерации проверяй: есть ли уже такой user в аккумуляторе?
204+
// 3. Если есть — прибавляй price.
205+
// 4. Если нет — создавай ключ и записывай туда price.
206+
// 5. Не забудь вернуть аккумулятор.
207+
// Ожидаемый результат:
208+
/*
209+
{
210+
Alice: 200,
211+
Bob: 120,
212+
Charlie: 40
213+
}
214+
*/
215+
216+
const orders = [
217+
{ user: 'Alice', price: 50 },
218+
{ user: 'Bob', price: 100 },
219+
{ user: 'Alice', price: 150 },
220+
{ user: 'Charlie', price: 40 },
221+
{ user: 'Bob', price: 20 },
222+
];
223+
224+
const users = orders.reduce((acc, order) => {
225+
if (!acc[order.user]) {
226+
acc[order.user] = 0;
227+
}
228+
229+
acc[order.user] += order.price;
230+
231+
return acc;
232+
}, {});
233+
234+
console.log(users); // {Alice: 200, Bob: 120, Charlie: 40}
235+
236+
// Задание 10:
237+
// Напиши функцию myFilter(array, callback), которая полностью повторяет логику стандартного метода .filter().
238+
// Требования:
239+
// 1. Функция принимает два аргумента: array (исходный массив) и callback (функция-условие).
240+
// 2. Функция должна возвращать новый массив.
241+
// 3. Внутри функции используй цикл for (или for...of) для перебора элементов.
242+
// 4. callback должен вызываться для каждого элемента. Если callback возвращает true, элемент попадает в новый массив. Если false — игнорируется.
243+
// 5. Важно: callback в стандартном filter принимает 3 аргумента: (item, index, array). Реализуй поддержку всех трёх аргументов при вызове колбэка внутри своей функции.
244+
245+
const numbers = [10, 20, 5, 30, 2, 15];
246+
const isBigEnough = (value, index, arr) => value >= 10;
247+
248+
function myFilter(array, callback) {
249+
if (!array || !Array.isArray(array)) {
250+
return [];
251+
}
252+
253+
if (!callback || typeof callback !== 'function') {
254+
return [];
255+
}
256+
257+
const filteredArr = [];
258+
259+
for (let i = 0; i < array.length; i++) {
260+
if (callback(array[i], i, array)) {
261+
filteredArr.push(array[i]);
262+
}
263+
}
264+
265+
return filteredArr;
266+
}
267+
268+
console.log(myFilter(numbers, isBigEnough)); // [10, 20, 30, 15]
269+
270+
// !! Задание 11:
271+
// Напиши функцию renderTasks(tasks), которая принимает массив строк и отрисовывает их в HTML-списке.
272+
// Требования:
273+
// 1. Внутри функции найди элемент <ul> с id="todo-list" (предположим, он есть в HTML).
274+
// 2. Очисти его текущее содержимое перед отрисовкой (чтобы не дублировать задачи при повторном вызове). Используй innerHTML = '' или textContent = ''.
275+
// 3. Пройдись циклом по массиву tasks.
276+
// 4. Для каждой задачи:
277+
// - Создай новый элемент <li> через document.createElement.
278+
// - Запиши текст задачи внутрь <li> (свойство textContent предпочтительнее, чем innerText, так как работает быстрее и предсказуемее).
279+
// - Добавь этот <li> в конец списка <ul> (метод append или appendChild).
280+
281+
const myTasks = ['Сделать рефакторинг', 'Выпить кофе', 'Запушить в main'];
282+
const toDoList = document.getElementById('todo-list');
283+
284+
function renderTasks(tasksArr) {
285+
if (!tasksArr || !Array.isArray(tasksArr)) {
286+
return [];
287+
}
288+
289+
toDoList.innerHTML = ''; // предварительная очистка
290+
const toDoFragment = document.createDocumentFragment();
291+
292+
for (const task of tasksArr) {
293+
const toDoItem = document.createElement('li');
294+
toDoItem.classList.add('todo-item');
295+
toDoItem.textContent = task;
296+
297+
toDoFragment.appendChild(toDoItem);
298+
}
299+
300+
toDoList.append(toDoFragment);
301+
}
302+
303+
renderTasks(myTasks);
304+
305+
// !! Задание 12:
306+
// Добавь функционал: при клике на любую задачу (<li>), она должна "зачеркиваться" (добавляем/убираем класс).
307+
// Входящие данные: Используй переменную toDoList из прошлой задачи (это наш <ul>). Предположим, в CSS есть класс:
308+
// .completed { text-decoration: line-through; opacity: 0.5; }
309+
// Требования:
310+
// 1. Навесь слушатель события 'click' только на родительский элемент toDoList.
311+
// 2. Внутри функции-обработчика используй event.target, чтобы понять, куда именно кликнули.
312+
// 3. Проверь, что клик был именно по LI (или внутри него). Подсказка: здесь пригодится метод closest() или проверка tagName.
313+
// 4. Если клик был по задаче — переключи класс 'completed' с помощью classList.toggle().
314+
315+
toDoList.addEventListener('click', (event) => {
316+
const target = event.target;
317+
318+
// if (target.tagName === 'LI') {
319+
// target.classList.toggle('completed');
320+
// }
321+
322+
const li = target.closest('li'); // исключение проблем, если в li есть ещё/появятся другие элементы
323+
324+
if (li) {
325+
li.classList.toggle('completed');
326+
}
327+
});
328+
329+
// !! Задание 13:
330+
// Напиши две утилитарные функции для работы с localStorage.
331+
// Требования:
332+
// 1. Функция saveToStorage(key, data):
333+
// - Принимает ключ (строка) и данные (массив или объект).
334+
// - Преобразует данные в строку формата JSON (используй JSON.stringify).
335+
// - Сохраняет в localStorage.
336+
// 2. Функция loadFromStorage(key):
337+
// - Принимает ключ.
338+
// - Получает данные из localStorage.
339+
// - Если данных нет (вернулся null) — возвращает пустой массив [] (чтобы код не падал).
340+
// - Если данные есть — преобразует их обратно из JSON в объект/массив (используй JSON.parse).
341+
342+
const myData = [{ id: 1, text: 'Code' }];
343+
344+
function saveToStorage(key, data) {
345+
if (!key || typeof key !== 'string') {
346+
return [];
347+
}
348+
349+
if (!data || !Array.isArray(data)) {
350+
return [];
351+
}
352+
353+
localStorage.setItem(key, JSON.stringify(data));
354+
}
355+
356+
function loadFromStorage(key) {
357+
if (!key || typeof key !== 'string') {
358+
return [];
359+
}
360+
361+
return JSON.parse(localStorage.getItem(key) || '[]'); // очень важное ИЛИ.. указание [] не даст упасть в ошибку
362+
}
363+
364+
saveToStorage('tasks', myData);
365+
366+
const loaded = loadFromStorage('tasks');
367+
console.log(loaded); // [{ id: 1, text: 'Code' }]

0 commit comments

Comments
 (0)