Live-coding masalalar — yechim va fikrlash jarayoni bilan
Ushbu fayl full-stack dasturchi uchun eng ko'p uchraydigan live-coding masalalarini o'z ichiga oladi. Har bir masala uchun faqat javob emas, balki qanday fikrlash kerakligi, toza va ishlaydigan kod, murakkablik tahlili, chekka holatlar va follow-up savollar berilgan.
Live-coding maslahatlari (avval o'qing)
Live-coding — bu sizning kodni yodlab olganingizni emas, qanday fikrlashingizni ko'rsatadigan jarayon. Quyidagi tartibga amal qiling:
- Savolni takrorlang va aniqlashtiring. Darrov kod yozishga tashlanmang. "Kirish qanday bo'ladi? Bo'sh massiv kelsa nima qilamiz? Katta-kichik harf farqi muhimmi?" degan savollar sizni professional ko'rsatadi.
- Ovoz chiqarib fikrlang. Intervyu oluvchi sizning miyangizni ko'rmoqchi. Jim o'tirib to'g'ri javob yozgandan ko'ra, ovoz chiqarib noto'g'ri yo'ldan qaytgan nomzod ko'proq ball oladi.
- Avval sodda ishlaydigan yechim, keyin optimallashtirish. "Brute-force" yechimni aytib qo'ying, keyin "buni O(n) ga tushirsak bo'ladi" deb yaxshilang.
- Chekka holatlarni ovoz chiqarib sanang.
null, bo'sh qiymat, takrorlanuvchi elementlar, juda katta kirish, manfiy sonlar. - Kodni gapirib yozing. O'zgaruvchi nomlari mazmunli bo'lsin (
i,xemas, balkiindex,total). - Yozib bo'lgach, o'zingiz "dry-run" qiling. 1-2 misolni qo'lda kodingiz orqali o'tkazib ko'rsating.
- Murakkablikni ayting. Har doim vaqt va xotira murakkabligini (Big-O) tilga oling.
Belgilar: oson · o'rta · murakkab
1-kategoriya. JavaScript amaliy masalalari
Masala 1 : debounce funksiyasini yozing
Foydalanuvchi tez-tez chaqiradigan funksiyani (masalan, qidiruv maydonidagi har bir harf bosilganda) oxirgi chaqiruvdan keyin belgilangan vaqt o'tgachgina bir marta ishlaydigan qilib o'rab bering.
Fikrlash: Har chaqiruvda oldingi taymerni bekor qilib, yangi taymer qo'yamiz. Faqat oxirgi chaqiruvdan keyin sokinlik davri bo'lganda funksiya ishlaydi. this va argumentlarni saqlash uchun oddiy funksiya (arrow emas) va apply dan foydalanamiz.
Yechim:
function debounce(fn, delay) {
let timerId; // taymer identifikatori shu closure ichida saqlanadi
return function (...args) {
// Har yangi chaqiruvda oldingi kutilayotgan chaqiruvni bekor qilamiz
clearTimeout(timerId);
// this ni saqlaymiz, chunki setTimeout ichida u yo'qoladi
const context = this;
timerId = setTimeout(() => {
fn.apply(context, args);
}, delay);
};
}
// Foydalanish:
const onSearch = debounce((text) => console.log('Qidiruv:', text), 300);
onSearch('a');
onSearch('ab');
onSearch('abc'); // faqat shu, 300ms dan keyin ishlaydiMurakkablik: O(1) — har chaqiruv o'zgarmas vaqt oladi.
Chekka holatlar:
delay0 bo'lsa — baribir keyingi tick da ishlaydi.- Funksiya
thisga bog'liq bo'lsa (obyekt metodi),applyorqali kontekst uzatilishi shart.
Follow-up: "Leading edge" variantini qo'sha olasizmi — ya'ni birinchi chaqiruvda darrov ishlab, keyin bloklovchi variant? (immediate flagi qo'shiladi.) Kutilayotgan chaqiruvni bekor qiladigan cancel() metodini ham qo'shing.
Masala 2 : throttle funksiyasini yozing
Funksiya har qancha chaqirilsa ham, belgilangan vaqt oralig'ida ko'pi bilan bir marta ishlasin (masalan, scroll hodisasi uchun).
Fikrlash: debounce dan farqi: throttle muntazam oraliqda ishlaydi, debounce esa faqat sokinlikdan keyin. Oxirgi ishga tushgan vaqtni saqlab, now - last >= interval bo'lgandagina chaqiramiz.
Yechim:
function throttle(fn, interval) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
fn.apply(this, args);
}
// Aks holda chaqiruv shunchaki e'tiborsiz qoldiriladi
};
}
// Foydalanish:
const onScroll = throttle(() => console.log('scroll pozitsiyasi'), 200);
window.addEventListener('scroll', onScroll);Murakkablik: O(1).
Chekka holatlar:
- Birinchi chaqiruv darrov ishlaydi (
lastTime = 0bo'lgani uchun). - Oxirgi chaqiruv "yo'qolishi" mumkin — trailing chaqiruv kerak bo'lsa taymer qo'shiladi.
Follow-up: Trailing chaqiruvni (oxirgi tashlangan chaqiruvni interval oxirida ishlatish) qanday qo'shasiz? setTimeout bilan oxirgi argumentlarni saqlab qo'yiladi.
Masala 3 : Chuqur nusxa (deepClone) yozing
Ichma-ich joylashgan obyekt/massivning to'liq mustaqil nusxasini yarating — nusxadagi o'zgarish asl obyektga ta'sir qilmasin.
Fikrlash: Object.assign yoki spread faqat sayoz (shallow) nusxa oladi. Rekursiya bilan har bir qatlamni aylanib chiqamiz. Massiv, Date, oddiy obyekt holatlarini alohida ko'ramiz. Doiraviy (circular) havolalar uchun WeakMap bilan ko'rilgan obyektlarni eslab qolamiz.
Yechim:
function deepClone(value, seen = new WeakMap()) {
// Primitivlar (null ham) shunchaki qaytariladi
if (value === null || typeof value !== 'object') {
return value;
}
// Doiraviy havolani aniqlash: agar oldin ko'rgan bo'lsak, saqlangan nusxani qaytaramiz
if (seen.has(value)) {
return seen.get(value);
}
// Maxsus obyekt turlari
if (value instanceof Date) {
return new Date(value);
}
if (value instanceof RegExp) {
return new RegExp(value.source, value.flags);
}
// Massiv yoki oddiy obyekt uchun bo'sh nusxa yaratamiz
const copy = Array.isArray(value) ? [] : {};
seen.set(value, copy); // rekursiyadan oldin ro'yxatga olamiz
for (const key of Object.keys(value)) {
copy[key] = deepClone(value[key], seen);
}
return copy;
}
// Foydalanish:
const original = { a: 1, b: { c: [2, 3] }, d: new Date() };
const clone = deepClone(original);
clone.b.c.push(4);
console.log(original.b.c); // [2, 3] — asl obyekt buzilmadiMurakkablik: O(n), bu yerda n — barcha qatlamlardagi elementlar soni.
Chekka holatlar:
- Doiraviy havola (
obj.self = obj) —WeakMapbo'lmasa cheksiz rekursiya bo'ladi. Map,Set, funksiyalar — bu yechim ularni to'liq qamramaydi (kerak bo'lsa alohida shart qo'shiladi).
Follow-up: structuredClone() zamonaviy brauzerlarda tayyor turibdi — nega baribir qo'lda yozish so'raladi? (Funksiyalarni tashlab yuboradi, ba'zi turlarni qamramaydi, va sizning tushunchangizni tekshirish uchun.)
Masala 4 : Massivni tekislash (flatten)
Ichma-ich joylashgan massivni bir qatlamli massivga aylantiring. Chuqurlik (depth) parametri bo'lsin.
Fikrlash: Har elementni ko'ramiz: agar massiv bo'lsa va chuqurlik qolgan bo'lsa — rekursiya bilan ichiga kiramiz, aks holda natijaga qo'shamiz. reduce bilan chiroyli chiqadi.
Yechim:
function flatten(arr, depth = Infinity) {
return arr.reduce((result, item) => {
if (Array.isArray(item) && depth > 0) {
// Chuqurlikni bittaga kamaytirib, ichkariga kiramiz
result.push(...flatten(item, depth - 1));
} else {
result.push(item);
}
return result;
}, []);
}
// Foydalanish:
console.log(flatten([1, [2, [3, [4]]]])); // [1, 2, 3, 4]
console.log(flatten([1, [2, [3, [4]]]], 1)); // [1, 2, [3, [4]]]Murakkablik: O(n), n — barcha ichki elementlar soni.
Chekka holatlar:
- Juda chuqur massiv — rekursiya stack limitiga urilishi mumkin. Bunda iterativ (stack asosidagi) yechim kerak.
- Bo'sh massivlar — natijaga hech narsa qo'shmaydi.
Follow-up: Rekursiyasiz, stack (yig'ma) bilan iterativ yechim yozing:
function flattenIterative(arr) {
const stack = [...arr];
const result = [];
while (stack.length) {
const next = stack.pop();
if (Array.isArray(next)) stack.push(...next);
else result.push(next);
}
return result.reverse(); // pop tartibni teskari qilgani uchun
}Masala 5 : groupBy funksiyasini yozing
Massiv elementlarini berilgan funksiya qaytargan kalit bo'yicha guruhlarga bo'ling.
Fikrlash: Bo'sh obyekt yaratamiz, har element uchun kalitni hisoblaymiz, o'sha kalit ostidagi massivga qo'shamiz. Kalit hali yo'q bo'lsa, avval bo'sh massiv yaratamiz.
Yechim:
function groupBy(arr, keyFn) {
return arr.reduce((groups, item) => {
const key = keyFn(item);
// Kalit birinchi marta uchrasa, bo'sh massiv bilan boshlaymiz
(groups[key] ||= []).push(item);
return groups;
}, {});
}
// Foydalanish:
const users = [
{ name: 'Ali', role: 'admin' },
{ name: 'Vali', role: 'user' },
{ name: 'Guli', role: 'admin' },
];
console.log(groupBy(users, (u) => u.role));
// { admin: [Ali, Guli], user: [Vali] }Murakkablik: O(n).
Chekka holatlar:
keyFnundefinedqaytarsa — kalit "undefined" satriga aylanadi.- Obyekt kaliti har doim satrga aylanadi; noyob kalitlar kerak bo'lsa
Mapishlatiladi.
Follow-up: Kalit sifatida satr emas, obyekt kerak bo'lsa? Map bilan qayta yozing. Bu — Array.prototype.groupBy (yangi standart) ning aynan o'zi.
Masala 6 : curry funksiyasini yozing
curry(fn) — sum(a, b, c) ni sum(a)(b)(c) yoki sum(a, b)(c) kabi qismlarga bo'lib chaqirsa bo'ladigan qilib aylantirsin.
Fikrlash: Argumentlar yig'ilib boradi. Agar yig'ilgan argumentlar soni asl funksiyaning length (kutilayotgan argumentlar soni) ga yetsa — chaqiramiz. Aks holda qolganini kutuvchi yangi funksiya qaytaramiz.
Yechim:
function curry(fn) {
return function curried(...args) {
// Yetarli argument yig'ilgan bo'lsa — asl funksiyani chaqiramiz
if (args.length >= fn.length) {
return fn.apply(this, args);
}
// Aks holda: yangi argumentlarni oldingilariga qo'shib kutamiz
return function (...next) {
return curried.apply(this, [...args, ...next]);
};
};
}
// Foydalanish:
const sum = (a, b, c) => a + b + c;
const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3)); // 6
console.log(curriedSum(1, 2)(3)); // 6
console.log(curriedSum(1, 2, 3)); // 6Murakkablik: O(n) argumentlar soniga nisbatan.
Chekka holatlar:
- Standart qiymatli argumentlar
fn.lengthni kamaytiradi (default va rest parametrlarlengthga kirmaydi). - 0 argumentli funksiya — darrov chaqiriladi.
Follow-up: Cheksiz curry — add(1)(2)(3)() kabi, oxirida bo'sh chaqiruv natijani qaytaradigan variantni qanday yozasiz? (Funksiyaning valueOf yoki bo'sh chaqiruvidan foydalaniladi.)
Masala 7 : promisify funksiyasini yozing
Callback uslubidagi funksiyani (fn(arg, callback) — bu yerda callback(err, result)) Promise qaytaradigan funksiyaga aylantiring.
Fikrlash: Node.js "error-first callback" konvensiyasi: callback birinchi argumenti xato, ikkinchisi natija. Yangi Promise yaratamiz; xato bo'lsa reject, bo'lmasa resolve qilamiz.
Yechim:
function promisify(fn) {
return function (...args) {
return new Promise((resolve, reject) => {
// Asl funksiyaga argumentlar + o'zimizning callback ni uzatamiz
fn.call(this, ...args, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
};
}
// Foydalanish:
function readFileFake(path, cb) {
setTimeout(() => {
if (!path) cb(new Error('Yo\'l yo\'q'));
else cb(null, `${path} mazmuni`);
}, 100);
}
const readFileAsync = promisify(readFileFake);
readFileAsync('a.txt').then(console.log).catch(console.error);Murakkablik: O(1) o'rash bo'yicha.
Chekka holatlar:
- Callback bir necha natija qaytarsa — bu yechim faqat birinchisini oladi.
thiskontekstga bog'liq metodlar —fn.call(this, ...)orqali saqlanadi.
Follow-up: util.promisify Node'da bor. Callback error-first bo'lmasa (masalan, faqat natija uzatsa) qanday moslashtirasiz?
Masala 8 : Event Emitter (pub/sub) yozing
on, off, emit, once metodlariga ega hodisa tizimini yarating.
Fikrlash: Har hodisa nomi uchun tinglovchilar (listener) massivini Map da saqlaymiz. on — qo'shadi, off — o'chiradi, emit — hammasini chaqiradi, once — bir marta ishlab, o'zini o'chiradigan o'ramdan foydalanadi.
Yechim:
class EventEmitter {
constructor() {
this.events = new Map(); // hodisa nomi -> tinglovchilar massivi
}
on(name, listener) {
if (!this.events.has(name)) this.events.set(name, []);
this.events.get(name).push(listener);
return this; // zanjir chaqiruv uchun
}
off(name, listener) {
const listeners = this.events.get(name);
if (!listeners) return this;
// Aynan shu funksiyani massivdan olib tashlaymiz
this.events.set(name, listeners.filter((l) => l !== listener));
return this;
}
emit(name, ...args) {
const listeners = this.events.get(name);
if (!listeners) return false;
// Nusxa bo'yicha aylandik — chaqiruv paytida massiv o'zgarsa xato bo'lmasin
[...listeners].forEach((listener) => listener(...args));
return true;
}
once(name, listener) {
const wrapper = (...args) => {
listener(...args);
this.off(name, wrapper); // ishlagach o'zini o'chiradi
};
return this.on(name, wrapper);
}
}
// Foydalanish:
const bus = new EventEmitter();
const handler = (data) => console.log('Qabul qilindi:', data);
bus.on('message', handler);
bus.emit('message', 'salom'); // Qabul qilindi: salom
bus.off('message', handler);
bus.emit('message', 'yana'); // hech narsa chiqmaydiMurakkablik: on/emit O(k), k — tinglovchilar soni. off O(k) (filter tufayli).
Chekka holatlar:
emitpaytida bir tinglovchi yangisini qo'shsa — nusxa bo'yicha aylanish buni himoya qiladi.- Bir xil funksiyani ikki marta
onqilsa — ikki marta chaqiriladi.
Follow-up: Xotira sizishini (memory leak) qanday oldini olasiz — hech qachon off qilinmagan tinglovchilar? once ni to'g'ri o'chirish nega muhim?
Masala 9 : Eksponensial backoff bilan retry
Xato bergan async funksiyani, har urinishda kutish vaqtini oshirib borib, bir necha marta qayta urinib ko'radigan o'ram yozing.
Fikrlash: Tsikl ichida try/catch bilan chaqiramiz. Xato bo'lsa — kutamiz (delay), keyingi urinishda kutishni ikki barobar oshiramiz. Urinishlar tugasa — oxirgi xatoni tashlaymiz.
Yechim:
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function retry(fn, { retries = 3, baseDelay = 200 } = {}) {
let lastError;
for (let attempt = 0; attempt <= retries; attempt++) {
try {
return await fn(); // muvaffaqiyat — darrov qaytamiz
} catch (err) {
lastError = err;
if (attempt === retries) break; // urinishlar tugadi
// Eksponensial: 200ms, 400ms, 800ms ...
const delay = baseDelay * 2 ** attempt;
console.log(`Urinish ${attempt + 1} muvaffaqiyatsiz, ${delay}ms kutamiz`);
await sleep(delay);
}
}
throw lastError; // hamma urinish barbod bo'ldi
}
// Foydalanish:
let count = 0;
const flaky = async () => {
if (++count < 3) throw new Error('vaqtinchalik xato');
return 'muvaffaqiyat';
};
retry(flaky).then(console.log).catch(console.error);Murakkablik: O(retries).
Chekka holatlar:
retries = 0— faqat bir marta urinadi.- Ba'zi xatolar qayta urinishga arzimaydi (masalan, 404) —
shouldRetryfunksiyasi qo'shilishi kerak.
Follow-up: "Jitter" (tasodifiy qo'shimcha kechikish) nega qo'shiladi? (Ko'p klient bir vaqtda qayta urinsa, serverni yana bosib qo'ymasligi uchun — "thundering herd" muammosi.)
Masala 10 : memoize funksiyasini yozing
Bir xil argumentlar bilan chaqirilganda natijani qayta hisoblamay, keshdan qaytaradigan o'ram yozing.
Fikrlash: Map da natijalarni saqlaymiz. Kalit sifatida argumentlarni serializatsiya qilamiz (oddiy holatda JSON.stringify). Kesh bor bo'lsa — undan qaytaramiz.
Yechim:
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args); // oddiy kalit strategiyasi
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
// Foydalanish:
const slowSquare = (n) => {
for (let i = 0; i < 1e6; i++) {} // sekin hisob taqlidi
return n * n;
};
const fastSquare = memoize(slowSquare);
console.log(fastSquare(5)); // hisoblanadi
console.log(fastSquare(5)); // keshdan — tezMurakkablik: Kesh urishida O(1); kalit hisoblash argument o'lchamiga bog'liq.
Chekka holatlar:
JSON.stringifyfunksiya/undefinedni yo'qotadi, kalit tartibi muhim.- Kesh cheksiz o'sishi mumkin — xotira muammosi. LRU strategiyasi kerak bo'lishi mumkin.
Follow-up: Argumentlar obyekt bo'lsa JSON.stringify ishonchsiz. WeakMap bilan bitta obyekt-argumentli funksiyalar uchun qanday memoize qilasiz?
2-kategoriya. Massiv va string masalalari
Masala 11 : Takrorlanmas elementlar (unique)
Massivdan takrorlarni olib tashlang.
Fikrlash: Eng qisqa yo'l — Set (u faqat noyob qiymatlarni saqlaydi). Obyektlar uchun bu ishlamaydi (havola bo'yicha solishtiradi) — u holda kalit funksiyasi kerak.
Yechim:
// Primitivlar uchun:
const unique = (arr) => [...new Set(arr)];
console.log(unique([1, 2, 2, 3, 3, 3])); // [1, 2, 3]
// Obyektlar uchun kalit bo'yicha:
function uniqueBy(arr, keyFn) {
const seen = new Set();
return arr.filter((item) => {
const key = keyFn(item);
if (seen.has(key)) return false;
seen.add(key);
return true;
});
}
console.log(uniqueBy([{ id: 1 }, { id: 1 }, { id: 2 }], (x) => x.id));
// [{ id: 1 }, { id: 2 }]Murakkablik: O(n).
Chekka holatlar:
NaN—Setuni to'g'ri (bitta) deb hisoblaydi.{}va{}— ikki alohida obyekt,Setularni farqli deb biladi.
Follow-up: Set ishlatmasdan, faqat filter va indexOf bilan qilsangiz murakkablik qanchaga oshadi? (O(n²) — chunki har element uchun indexOf ichki tsikl.)
Masala 12 : Massivni bo'laklarga bo'lish (chunk)
Massivni belgilangan o'lchamdagi kichik massivlarga ajrating.
Fikrlash: size qadam bilan aylanamiz, har qadamda slice bilan bo'lakni ajratamiz.
Yechim:
function chunk(arr, size) {
if (size <= 0) throw new Error("O'lcham musbat bo'lishi kerak");
const result = [];
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size));
}
return result;
}
// Foydalanish:
console.log(chunk([1, 2, 3, 4, 5], 2)); // [[1, 2], [3, 4], [5]]Murakkablik: O(n).
Chekka holatlar:
sizemassivdan katta — bitta bo'lak qaytadi.- Oxirgi bo'lak to'liq bo'lmasligi mumkin (yuqoridagi
[5]). size <= 0— cheksiz tsikldan himoya kerak.
Follow-up: Pagination (sahifalash) da bu funksiya qanday ishlatiladi?
Masala 13 : Ikki massiv kesishmasi (intersection)
Ikki massivda ham mavjud bo'lgan elementlarni toping.
Fikrlash: Bittasini Set ga aylantirib, ikkinchisini filtrlaymiz. Set.has — O(1), shu tufayli umumiy O(n).
Yechim:
function intersection(a, b) {
const setB = new Set(b);
return [...new Set(a)].filter((item) => setB.has(item));
}
console.log(intersection([1, 2, 3, 4], [2, 4, 6])); // [2, 4]Murakkablik: O(n + m).
Chekka holatlar:
- Takror elementlar — tashqi
new Set(a)natijada takror bo'lmasligini ta'minlaydi. - Bo'sh massivlardan biri — natija bo'sh.
Follow-up: Farq (difference) va birlashma (union) ni ham yozing. Uch massiv kesishmasini qanday umumlashtirasiz?
Masala 14 : Element chastotasi (frequency)
Massivdagi har elementning necha marta uchraganini sanang.
Fikrlash: Obyekt (yoki Map) da hisoblagich yuritamiz.
Yechim:
function frequency(arr) {
return arr.reduce((counts, item) => {
counts[item] = (counts[item] || 0) + 1;
return counts;
}, {});
}
console.log(frequency(['a', 'b', 'a', 'c', 'a']));
// { a: 3, b: 1, c: 1 }
// Eng ko'p uchragan elementni topish:
function mostFrequent(arr) {
const counts = frequency(arr);
return Object.keys(counts).reduce((a, b) =>
counts[a] >= counts[b] ? a : b
);
}Murakkablik: O(n).
Chekka holatlar:
- Sonlar kalit sifatida satrga aylanadi (
counts[1]vacounts['1']bir xil). - Bo'sh massiv —
mostFrequentxato beradi, tekshirish kerak.
Follow-up: Eng ko'p uchragan K ta elementni topish (topK)? (Chastotani hisoblab, saralaymiz yoki heap ishlatamiz.)
Masala 15 : Anagramma tekshirish
Ikki so'z bir xil harflardan (bir xil sonda) tashkil topganmi, aniqlang.
Fikrlash: Ikki yondashuv: (1) ikkalasini saralab solishtirish — O(n log n); (2) harf chastotasini sanab solishtirish — O(n). Ikkinchisi tezroq.
Yechim:
function isAnagram(a, b) {
// Normallashtirish: bo'shliq va katta-kichik harf farqini olib tashlaymiz
const clean = (s) => s.toLowerCase().replace(/\s/g, '');
a = clean(a);
b = clean(b);
if (a.length !== b.length) return false;
const counts = {};
for (const ch of a) counts[ch] = (counts[ch] || 0) + 1;
for (const ch of b) {
if (!counts[ch]) return false; // b da ortiqcha harf bor
counts[ch]--;
}
return true;
}
console.log(isAnagram('listen', 'silent')); // true
console.log(isAnagram('hello', 'world')); // falseMurakkablik: O(n).
Chekka holatlar:
- Katta-kichik harf, bo'shliqlar — normallashtirish hal qiladi.
- Unicode/emoji —
for...ofkod nuqtalari bo'yicha to'g'ri aylanadi.
Follow-up: Massivdagi anagrammalarni guruhlash (groupAnagrams)? Har so'zni saralangan holatiga kalit qilib groupBy ishlatiladi.
Masala 16 : Palindrom tekshirish
So'z teskari o'qiganda ham bir xil bo'lsa — palindrom.
Fikrlash: Ikki ko'rsatkich (two-pointer): bosh va oxirdan yurib, o'rtada uchrashguncha solishtiramiz. Bu reverse dan xotira jihatidan tejamli.
Yechim:
function isPalindrome(str) {
// Faqat harf-raqamlarni qoldiramiz, kichik harfga o'tkazamiz
const s = str.toLowerCase().replace(/[^a-z0-9]/g, '');
let left = 0;
let right = s.length - 1;
while (left < right) {
if (s[left] !== s[right]) return false;
left++;
right--;
}
return true;
}
console.log(isPalindrome('A man, a plan, a canal: Panama')); // true
console.log(isPalindrome('salom')); // falseMurakkablik: O(n) vaqt, O(1) qo'shimcha xotira (tozalashdan tashqari).
Chekka holatlar:
- Bo'sh satr yoki bitta belgi — palindrom (
true). - Tinish belgilari va probellar — regex bilan olib tashlanadi.
Follow-up: Bitta harfni olib tashlab palindrom qilsa bo'ladimi, degan variant (valid palindrome II) qanday yechiladi?
3-kategoriya. Asinxron masalalar
Masala 17 : Promise.all ni o'zingiz yozing
Bir nechta promise ni parallel bajarib, hammasi tugagach natijalar massivini qaytaradigan funksiya yozing. Bittasi rad etilsa — darrov rad eting.
Fikrlash: Hisoblagich yuritamiz. Har promise tugaganda natijani to'g'ri indeksga joylaymiz (tugash tartibi turlicha bo'lgani uchun). Hammasi tugaganda resolve. Har qanday biri xato bersa darrov reject.
Yechim:
function promiseAll(promises) {
return new Promise((resolve, reject) => {
const results = [];
let completed = 0;
if (promises.length === 0) {
return resolve([]); // bo'sh kirish uchun
}
promises.forEach((promise, index) => {
// Promise bo'lmagan qiymatni ham qo'llab-quvvatlaymiz
Promise.resolve(promise)
.then((value) => {
results[index] = value; // indeks bo'yicha tartibni saqlaymiz
completed++;
if (completed === promises.length) {
resolve(results);
}
})
.catch(reject); // birinchi xato butun natijani rad etadi
});
});
}
// Foydalanish:
promiseAll([
Promise.resolve(1),
new Promise((r) => setTimeout(() => r(2), 100)),
Promise.resolve(3),
]).then(console.log); // [1, 2, 3] — tartib saqlanadiMurakkablik: O(n) promise ni ro'yxatga olish.
Chekka holatlar:
- Bo'sh massiv — darrov
[]bilan resolve bo'lishi kerak. - Natija tartibi kirish tartibida bo'lishi shart (tugash tartibida emas).
Follow-up: Promise.allSettled (hech qachon rad etmaydi, har biri {status, value/reason} qaytaradi) va Promise.race ni ham yozing.
Masala 18 : Ketma-ket va parallel bajarish
Async funksiyalar massivini (a) ketma-ket (biri tugagach ikkinchisi) va (b) parallel bajaring. Farqni tushuntiring.
Fikrlash: Ketma-ket — for...of ichida await, har biri oldingisini kutadi. Parallel — hammasini birdan boshlab Promise.all bilan kutamiz. Parallel tezroq, lekin resurslarni bir vaqtda ko'proq band qiladi.
Yechim:
// Ketma-ket: biri tugamaguncha keyingisi boshlanmaydi
async function runSequential(tasks) {
const results = [];
for (const task of tasks) {
results.push(await task()); // har biri navbatma-navbat
}
return results;
}
// Parallel: hammasi bir vaqtda boshlanadi
async function runParallel(tasks) {
return Promise.all(tasks.map((task) => task()));
}
// Farqni ko'rsatish:
const makeTask = (id, ms) => () =>
new Promise((r) => setTimeout(() => r(id), ms));
const tasks = [makeTask('a', 300), makeTask('b', 300), makeTask('c', 300)];
// runSequential ~900ms, runParallel ~300msMurakkablik: Ketma-ket — vaqtlar yig'indisi; parallel — eng uzun vaqt.
Chekka holatlar:
- Parallelda serverga bir vaqtda ko'p so'rov — rate limit muammosi.
- Ketma-ketda biri xato bersa — qolganlari ishlamaydi.
Follow-up: tasks.map(async ...) ni oddiy for tsiklda await bilan yozsangiz nima farq bo'ladi? (.map da callback lar darrov parallel boshlanadi, for await esa ketma-ket.)
Masala 19 : Bir vaqtdagi so'rovlarni cheklovchi (rate limiter / concurrency pool)
Ko'p vazifani, lekin bir vaqtda ko'pi bilan N tasini ishlatib bajaring (masalan, 100 ta URL, lekin bir vaqtda 5 tadan yuklab olish).
Fikrlash: "Ishchi hovuz" (worker pool) g'oyasi. N ta "ishchi" bir vaqtda navbatdan vazifa oladi. Ishchi vazifani tugatgach navbatdan keyingisini oladi. Barcha ishchilar tugaganda ish bitadi.
Yechim:
async function limitConcurrency(tasks, limit) {
const results = new Array(tasks.length);
let nextIndex = 0;
async function worker() {
// Har ishchi navbatdagi vazifani olib boradi
while (nextIndex < tasks.length) {
const current = nextIndex++;
results[current] = await tasks[current]();
}
}
// limit ta ishchini parallel ishga tushiramiz
const workers = Array.from({ length: Math.min(limit, tasks.length) }, worker);
await Promise.all(workers);
return results;
}
// Foydalanish:
const tasks = Array.from({ length: 10 }, (_, i) => () =>
new Promise((r) => setTimeout(() => r(i), 100))
);
limitConcurrency(tasks, 3).then(console.log); // bir vaqtda 3 tadanMurakkablik: O(n) vazifa, bir vaqtda limit ta faol.
Chekka holatlar:
limitvazifalar sonidan katta —Math.minbilan cheklaymiz.- Bitta vazifa xato bersa —
Promise.allbutun ishni rad etadi (kerak bo'lsaallSettledishlatiladi).
Follow-up: Yangi vazifalarni yugurayotgan hovuzga dinamik qo'shish (queue push) qanday amalga oshiriladi?
Masala 20 : Asinxron navbat (AsyncQueue)
Vazifalarni navbatga qo'yib, ularni ketma-ket (yoki cheklangan parallellik bilan) bajaradigan sinf yozing.
Fikrlash: Ichki massivda navbatni saqlaymiz. add vazifani qo'shadi va agar hozir band bo'lmasa process ni ishga tushiradi. process navbat bo'shaguncha bittalab oladi. add Promise qaytarib, chaqiruvchi natijani kuta olishi kerak.
Yechim:
class AsyncQueue {
constructor(concurrency = 1) {
this.concurrency = concurrency;
this.running = 0;
this.queue = [];
}
add(task) {
return new Promise((resolve, reject) => {
// Vazifani va uning natija "portlari"ni navbatga qo'yamiz
this.queue.push({ task, resolve, reject });
this.process();
});
}
process() {
// Bo'sh joy va navbatda vazifa bo'lsa — keyingisini ishga tushiramiz
while (this.running < this.concurrency && this.queue.length) {
const { task, resolve, reject } = this.queue.shift();
this.running++;
Promise.resolve()
.then(task)
.then(resolve, reject)
.finally(() => {
this.running--;
this.process(); // joy bo'shadi — keyingisini olamiz
});
}
}
}
// Foydalanish:
const queue = new AsyncQueue(2); // bir vaqtda 2 ta
for (let i = 1; i <= 5; i++) {
queue
.add(() => new Promise((r) => setTimeout(() => r(i), 200)))
.then((res) => console.log('Tugadi:', res));
}Murakkablik: Har vazifa uchun O(1) navbatga qo'yish/olish.
Chekka holatlar:
- Vazifa xato bersa —
rejectorqali chaqiruvchiga yetkaziladi, navbat to'xtamaydi. processni har bo'shashda chaqirish navbatning oqib turishini ta'minlaydi.
Follow-up: Ustuvorlikli (priority) navbat kerak bo'lsa? (queue ni ustuvorlik bo'yicha saralangan holda saqlash yoki heap ishlatish.)
4-kategoriya. React masalalari (JSX)
Masala 21 : Counter komponenti
Qiymatni oshirish, kamaytirish va nolga qaytarish tugmalari bo'lgan hisoblagich.
Fikrlash: useState bilan holat, uch tugma uchun uch handler. Funksional yangilash (prev => prev + 1) ketma-ket chaqiruvlarda ishonchli.
Yechim:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<h2>Hisob: {count}</h2>
{/* Funksional yangilash — oldingi holatga tayanadi */}
<button onClick={() => setCount((prev) => prev - 1)}>-</button>
<button onClick={() => setCount(0)}>Tozalash</button>
<button onClick={() => setCount((prev) => prev + 1)}>+</button>
</div>
);
}
export default Counter;Murakkablik: O(1) har yangilanishda.
Chekka holatlar:
- Manfiy qiymatni cheklash kerakmi? Masalaga bog'liq.
setCount(count + 1)ni tez ketma-ket ikki marta chaqirsangiz — eskicountga tayanadi va yo'qoladi; funksional variant buni hal qiladi.
Follow-up: Qadamni (step) prop qilib qo'shing. Chegaralar (min/max) qanday qo'yiladi?
Masala 22 : Todo ro'yxati
Vazifa qo'shish, o'chirish va bajarilgan deb belgilash imkoniyati bilan.
Fikrlash: Vazifalar massivini holatda saqlaymiz, har biri { id, text, done }. Qo'shishda noyob id (masalan, Date.now() yoki crypto.randomUUID()). O'chirish — filter, belgilash — map bilan yangi massiv (immutable yangilash).
Yechim:
import { useState } from 'react';
function TodoList() {
const [todos, setTodos] = useState([]);
const [text, setText] = useState('');
const addTodo = () => {
const trimmed = text.trim();
if (!trimmed) return; // bo'sh vazifa qo'shilmasin
setTodos((prev) => [
...prev,
{ id: crypto.randomUUID(), text: trimmed, done: false },
]);
setText('');
};
const toggleTodo = (id) =>
setTodos((prev) =>
prev.map((t) => (t.id === id ? { ...t, done: !t.done } : t))
);
const removeTodo = (id) =>
setTodos((prev) => prev.filter((t) => t.id !== id));
return (
<div>
<input
value={text}
onChange={(e) => setText(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && addTodo()}
placeholder="Yangi vazifa"
/>
<button onClick={addTodo}>Qo'shish</button>
<ul>
{todos.map((todo) => (
<li key={todo.id}>
<span
onClick={() => toggleTodo(todo.id)}
style={{ textDecoration: todo.done ? 'line-through' : 'none' }}
>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)}>O'chirish</button>
</li>
))}
</ul>
</div>
);
}
export default TodoList;Murakkablik: O(n) har yangilanishda (map/filter).
Chekka holatlar:
- Bo'sh matn —
trimbilan bloklanadi. keysifatidaindexishlatmang — o'chirishda xato render bo'ladi. Barqaroridishlatiladi.
Follow-up: Ro'yxatni localStorage da saqlash? Filtrlash (hammasi/faol/bajarilgan)? Katta ro'yxatda virtualizatsiya?
Masala 23 : useFetch custom hook
Ma'lumot yuklovchi, loading, error, data holatlarini qaytaradigan qayta ishlatiluvchi hook.
Fikrlash: useEffect da so'rov qilamiz. Uch holat: yuklanmoqda, xato, ma'lumot. Muhim: komponent unmount bo'lsa yoki url o'zgarsa — eski so'rovni bekor qilish (AbortController), aks holda "state update on unmounted component" muammosi.
Yechim:
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
async function fetchData() {
setLoading(true);
setError(null);
try {
const res = await fetch(url, { signal: controller.signal });
if (!res.ok) throw new Error(`HTTP xato: ${res.status}`);
const json = await res.json();
setData(json);
} catch (err) {
// Bekor qilish xatosini e'tiborsiz qoldiramiz
if (err.name !== 'AbortError') setError(err.message);
} finally {
setLoading(false);
}
}
fetchData();
return () => controller.abort(); // tozalash: eski so'rovni bekor qilamiz
}, [url]);
return { data, loading, error };
}
// Foydalanish:
function UserProfile({ id }) {
const { data, loading, error } = useFetch(`/api/users/${id}`);
if (loading) return <p>Yuklanmoqda...</p>;
if (error) return <p>Xato: {error}</p>;
return <h2>{data.name}</h2>;
}Murakkablik: Tarmoq so'roviga bog'liq; hook logikasi O(1).
Chekka holatlar:
- Tez o'zgaruvchan
url— race condition;AbortControlleryechadi. res.oktekshirilmasa, 404 ham "muvaffaqiyat" deb qabul qilinadi.
Follow-up: Keshlash, qayta urinish, refetch funksiyasini qanday qo'shasiz? Nega production da React Query / SWR afzal ko'riladi?
Masala 24 : Debounce bilan qidiruv
Foydalanuvchi yozishni to'xtatgach so'rov yuboradigan qidiruv maydoni.
Fikrlash: Har harfda so'rov yubormaslik uchun debounce kerak. useEffect ichida setTimeout qo'yib, tozalashda uni bekor qilamiz — bu React uslubidagi debounce.
Yechim:
import { useState, useEffect } from 'react';
function SearchBox() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
useEffect(() => {
if (!query.trim()) {
setResults([]);
return;
}
// 400ms sokinlikdan keyin so'rov yuboriladi
const timerId = setTimeout(async () => {
const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
const data = await res.json();
setResults(data);
}, 400);
// Har yangi harfda oldingi taymer bekor qilinadi
return () => clearTimeout(timerId);
}, [query]);
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Qidirish..."
/>
<ul>
{results.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
}
export default SearchBox;Murakkablik: So'rov chastotasi kamayadi; render O(n) natijalar bo'yicha.
Chekka holatlar:
- Bo'sh so'rov — so'rov yubormaymiz.
- Race condition: eski so'rov kechroq qaytsa — natijani buzishi mumkin.
AbortControlleryoki "eng so'nggi so'rov" tekshiruvi kerak.
Follow-up: Klaviatura bilan natijalar bo'ylab yurish (yuqori/pastki strelka) qanday qo'shiladi? Bu — 30-masaladagi autocomplete ga o'tadi.
Masala 25 : Boshqariladigan forma (controlled form)
Bir nechta maydonli, validatsiyali forma yozing.
Fikrlash: Bitta values obyektida barcha maydonlarni saqlaymiz — bitta handleChange name atributi orqali kerakli maydonni yangilaydi. Yuborishda validatsiya qilamiz.
Yechim:
import { useState } from 'react';
function SignupForm() {
const [values, setValues] = useState({ email: '', password: '' });
const [errors, setErrors] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
// name orqali dinamik maydon yangilanadi
setValues((prev) => ({ ...prev, [name]: value }));
};
const validate = () => {
const next = {};
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values.email))
next.email = 'Email noto\'g\'ri';
if (values.password.length < 6)
next.password = 'Parol kamida 6 ta belgi';
return next;
};
const handleSubmit = (e) => {
e.preventDefault();
const validationErrors = validate();
setErrors(validationErrors);
if (Object.keys(validationErrors).length === 0) {
console.log('Yuborildi:', values);
}
};
return (
<form onSubmit={handleSubmit}>
<input
name="email"
value={values.email}
onChange={handleChange}
placeholder="Email"
/>
{errors.email && <span>{errors.email}</span>}
<input
name="password"
type="password"
value={values.password}
onChange={handleChange}
placeholder="Parol"
/>
{errors.password && <span>{errors.password}</span>}
<button type="submit">Ro'yxatdan o'tish</button>
</form>
);
}
export default SignupForm;Murakkablik: O(maydonlar soni) validatsiyada.
Chekka holatlar:
e.preventDefault()bo'lmasa — sahifa qayta yuklanadi.- Har bosishda emas, yuborishda validatsiya — foydalanuvchi tajribasi yaxshiroq (yoki
onBlurda).
Follow-up: Maydonlar ko'payib ketsa qanday boshqarasiz? Nega Formik / React Hook Form ishlatiladi?
Masala 26 : useLocalStorage hook
Holatni localStorage bilan avtomatik sinxronlab turadigan hook.
Fikrlash: useState ni o'raymiz. Boshlang'ich qiymatni localStorage dan o'qiymiz (funksiya orqali — lazy init). Har o'zgarishda useEffect bilan saqlaymiz. JSON bilan serializatsiya.
Yechim:
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
// Lazy init: faqat birinchi renderda o'qiymiz
try {
const stored = localStorage.getItem(key);
return stored !== null ? JSON.parse(stored) : initialValue;
} catch {
return initialValue; // buzilgan JSON dan himoya
}
});
useEffect(() => {
try {
localStorage.setItem(key, JSON.stringify(value));
} catch {
// Kvota tugashi yoki maxfiy rejim xatosini e'tiborsiz qoldiramiz
}
}, [key, value]);
return [value, setValue];
}
// Foydalanish:
function ThemeToggle() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Joriy mavzu: {theme}
</button>
);
}Murakkablik: O(qiymat o'lchami) serializatsiyada.
Chekka holatlar:
- Buzilgan JSON —
try/catchbilan boshlang'ich qiymatga qaytamiz. - SSR (Next.js) —
localStorageserverda yo'q;typeof windowtekshiruvi kerak. - Kvota tugashi — saqlash
try/catchda.
Follow-up: Bir nechta tab o'rtasida sinxronlash uchun storage hodisasini qanday tinglaysiz?
Masala 27 : Modal (dialog) komponenti
Portal orqali render bo'ladigan, tashqarisiga bosilganda va Escape bosilganda yopiladigan modal.
Fikrlash: createPortal bilan DOM daraxtining boshqa joyiga render qilamiz (z-index/overflow muammolarini yechadi). Escape uchun useEffect da global tinglovchi. Fon (overlay) bosilganda yopamiz, lekin ichki bosishda emas.
Yechim:
import { useEffect } from 'react';
import { createPortal } from 'react-dom';
function Modal({ isOpen, onClose, children }) {
useEffect(() => {
if (!isOpen) return;
const onKey = (e) => e.key === 'Escape' && onClose();
document.addEventListener('keydown', onKey);
return () => document.removeEventListener('keydown', onKey);
}, [isOpen, onClose]);
if (!isOpen) return null;
return createPortal(
// Overlay: bosilsa yopamiz
<div className="overlay" onClick={onClose}>
{/* stopPropagation: ichki bosish overlay ga o'tmasin */}
<div className="modal" onClick={(e) => e.stopPropagation()}>
<button onClick={onClose}>Yopish</button>
{children}
</div>
</div>,
document.body
);
}
// Foydalanish:
// <Modal isOpen={open} onClose={() => setOpen(false)}>
// <p>Modal mazmuni</p>
// </Modal>Murakkablik: O(1).
Chekka holatlar:
- Ichki bosish modalni yopmasligi kerak —
stopPropagation. - Ochilganda fon scroll ni bloklash (
bodyoverflow) kerak bo'lishi mumkin. - Fokus qamovi (focus trap) — a11y uchun muhim.
Follow-up: Klaviatura fokusini modal ichida ushlab turish (focus trap) va ochilganda birinchi elementga fokus berishni qanday qilasiz?
Masala 28 : Sahifalash (pagination)
Ma'lumotni sahifalarga bo'lib ko'rsatuvchi komponent.
Fikrlash: Joriy sahifa raqamini holatda saqlaymiz. Ko'rsatiladigan bo'lakni slice((page-1)*size, page*size) bilan olamiz. Umumiy sahifalar soni — Math.ceil(total/size).
Yechim:
import { useState } from 'react';
function PaginatedList({ items, pageSize = 5 }) {
const [page, setPage] = useState(1);
const totalPages = Math.ceil(items.length / pageSize);
// Joriy sahifadagi elementlar
const start = (page - 1) * pageSize;
const visible = items.slice(start, start + pageSize);
return (
<div>
<ul>
{visible.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
<div>
<button disabled={page === 1} onClick={() => setPage((p) => p - 1)}>
Oldingi
</button>
<span>
{page} / {totalPages}
</span>
<button
disabled={page === totalPages}
onClick={() => setPage((p) => p + 1)}
>
Keyingi
</button>
</div>
</div>
);
}
export default PaginatedList;Murakkablik: O(pageSize) render bo'yicha.
Chekka holatlar:
- Bo'sh ro'yxat —
totalPages = 0, tugmalar disabled bo'lishi kerak. - Elementlar o'chib, joriy sahifa mavjud bo'lmay qolsa — sahifani tuzatish kerak.
Follow-up: Server tomonli sahifalash (server-side pagination) mijoz tomonlidan qanday farq qiladi? Katta ma'lumotlar uchun qaysi biri to'g'ri?
5-kategoriya. DOM masalalari
Masala 29 : Hodisa delegatsiyasi (event delegation)
Ko'p elementga alohida tinglovchi qo'ymasdan, bitta ota-elementga bitta tinglovchi qo'yib, bosilgan bolani aniqlang.
Fikrlash: Hodisa "ko'pikchi" (bubbling) tabiati tufayli bola elementdagi bosish ota-elementga ham yetadi. event.target — aynan bosilgan element. closest bilan kerakli elementni topamiz. Bu ayniqsa dinamik qo'shiladigan elementlar uchun foydali.
Yechim:
const list = document.querySelector('#todo-list');
// Bitta tinglovchi — barcha <li> lar uchun
list.addEventListener('click', (event) => {
// Bosilgan joydan eng yaqin <li> ni topamiz
const item = event.target.closest('li');
if (!item || !list.contains(item)) return;
// O'chirish tugmasi bosildimi?
if (event.target.matches('.delete-btn')) {
item.remove();
return;
}
// Aks holda: bajarilgan deb belgilaymiz
item.classList.toggle('done');
});
// Yangi element qo'shsak ham — tinglovchi qayta kerak emasMurakkablik: Xotira O(1) (bitta tinglovchi), N ta alohida tinglovchidan tejamli.
Chekka holatlar:
event.targetchuqurroq ichki element bo'lishi mumkin —closestshu uchun.- Ota-element tashqarisidagi bosishlarni
list.containsbilan tekshiramiz.
Follow-up: Nega dinamik ro'yxatlarda delegatsiya har elementga tinglovchi qo'yishdan afzal? (Kam xotira, yangi elementlarga avtomatik ishlaydi.)
Masala 30 : Cheksiz aylantirish (infinite scroll)
Foydalanuvchi sahifa oxiriga yaqinlashganda avtomatik yangi ma'lumot yuklang.
Fikrlash: Ikki yondashuv: (1) scroll hodisasini throttle bilan tinglab pozitsiyani hisoblash; (2) zamonaviy IntersectionObserver — "sentinel" element ko'rinadigan bo'lsa yuklash. Ikkinchisi tejamliroq va aniqroq.
Yechim (IntersectionObserver bilan):
let page = 1;
let loading = false;
async function loadMore() {
if (loading) return;
loading = true;
const res = await fetch(`/api/items?page=${page}`);
const items = await res.json();
renderItems(items); // DOM ga qo'shish
page++;
loading = false;
}
// "Sentinel" — ro'yxat oxiridagi ko'rinmas element
const sentinel = document.querySelector('#sentinel');
const observer = new IntersectionObserver(
(entries) => {
// Sentinel ko'rinsa — keyingi sahifani yuklaymiz
if (entries[0].isIntersecting) {
loadMore();
}
},
{ rootMargin: '200px' } // oxiriga 200px qolganda oldindan yuklaymiz
);
observer.observe(sentinel);Murakkablik: Har yuklashda O(yangi elementlar).
Chekka holatlar:
loadingbayrog'i bo'lmasa — bir vaqtda ko'p so'rov ketadi.- Ma'lumot tugaganda
observer.disconnect()chaqirilishi kerak. - Juda ko'p element DOM ni sekinlashtiradi — virtualizatsiya kerak bo'lishi mumkin.
Follow-up: scroll hodisasi bilan qilsangiz nega throttle shart? IntersectionObserver nega afzal? Katta ro'yxatda "windowing" (virtualizatsiya) qanday qo'llaniladi?
6-kategoriya. Algoritmik masalalar
Masala 31 : Two Sum
Massivdan yig'indisi berilgan songa teng bo'lgan ikki element indeksini toping.
Fikrlash: Brute-force — har juftni tekshirish O(n²). Yaxshiroq: hash-map da ko'rilgan sonlarni saqlab, har son uchun "menga qanday son kerak" (target - num) ni tekshiramiz — O(n).
Yechim:
function twoSum(nums, target) {
const seen = new Map(); // qiymat -> indeks
for (let i = 0; i < nums.length; i++) {
const need = target - nums[i];
// Kerakli to'ldiruvchi son oldin ko'rilganmi?
if (seen.has(need)) {
return [seen.get(need), i];
}
seen.set(nums[i], i);
}
return null; // yechim yo'q
}
console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1]
console.log(twoSum([3, 2, 4], 6)); // [1, 2]Murakkablik: O(n) vaqt, O(n) xotira.
Chekka holatlar:
- Bir xil elementni ikki marta ishlatmaslik — biz oldin ko'rilganni tekshirgani uchun to'g'ri.
- Yechim topilmasa —
null. - Takror sonlar (
[3, 3], target 6) — to'g'ri ishlaydi.
Follow-up: Massiv saralangan bo'lsa, xotirasiz (two-pointer) qanday yechasiz? Barcha juftlarni topish kerak bo'lsa-chi?
Masala 32 : FizzBuzz
1 dan N gacha chop eting: 3 ga bo'linsa "Fizz", 5 ga "Buzz", ikkalasiga "FizzBuzz".
Fikrlash: Klassik masala — asosiy maqsad toza shart mantiqini ko'rsatish. 15 (3 va 5 ning eng kichik umumiy karrasi) ni birinchi tekshirish yoki satr yig'ish usuli.
Yechim:
function fizzBuzz(n) {
const result = [];
for (let i = 1; i <= n; i++) {
let output = '';
if (i % 3 === 0) output += 'Fizz';
if (i % 5 === 0) output += 'Buzz';
// output bo'sh bo'lsa — sonning o'zi
result.push(output || i);
}
return result;
}
console.log(fizzBuzz(15));
// [1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz', 11, 'Fizz', 13, 14, 'FizzBuzz']Murakkablik: O(n).
Chekka holatlar:
- Satr yig'ish usuli takroriy
if/else ifdan tozaroq va kengaytiriladigan. n <= 0— bo'sh massiv.
Follow-up: Yangi qoida qo'shish oson bo'lishi uchun (masalan, 7 ga "Bazz") qanday umumlashtirasiz? (Qoidalar massivi: [[3,'Fizz'],[5,'Buzz']].)
Masala 33 : Fibonachchi (memoizatsiya bilan)
N-chi Fibonachchi sonini samarali hisoblang.
Fikrlash: Sodda rekursiya O(2ⁿ) — juda sekin, chunki bir xil qiymatlar qayta hisoblanadi. Memoizatsiya bilan O(n). Yana yaxshirog'i — iterativ, O(1) xotira.
Yechim:
// Memoizatsiya bilan (yuqoridan pastga):
function fibMemo(n, cache = new Map()) {
if (n <= 1) return n;
if (cache.has(n)) return cache.get(n);
const result = fibMemo(n - 1, cache) + fibMemo(n - 2, cache);
cache.set(n, result);
return result;
}
// Iterativ (pastdan yuqoriga) — O(1) xotira:
function fibIterative(n) {
if (n <= 1) return n;
let prev = 0;
let curr = 1;
for (let i = 2; i <= n; i++) {
[prev, curr] = [curr, prev + curr]; // ikkovini yangilaymiz
}
return curr;
}
console.log(fibMemo(10)); // 55
console.log(fibIterative(10)); // 55Murakkablik: Memo — O(n) vaqt/xotira; iterativ — O(n) vaqt, O(1) xotira.
Chekka holatlar:
- Katta
n— sonNumber.MAX_SAFE_INTEGERdan oshadi;BigIntkerak. - Manfiy
n— masalaga qarab aniqlashtiriladi.
Follow-up: Nega iterativ yechim rekursivdan afzal? (Stack overflow yo'q, O(1) xotira.) Matritsa darajasi bilan O(log n) yechimni bilasizmi?
Masala 34 : To'g'ri qavslar (valid parentheses)
()[]{} qavslardan iborat satr to'g'ri yopilganmi, tekshiring.
Fikrlash: Stek (yig'ma) klassik yechimi. Ochuvchi qavsni stekka qo'yamiz. Yopuvchi kelsa — stek tepasi mos ochuvchi bo'lishi kerak. Oxirida stek bo'sh bo'lsa — to'g'ri.
Yechim:
function isValid(str) {
const stack = [];
const pairs = { ')': '(', ']': '[', '}': '{' };
for (const ch of str) {
if (ch === '(' || ch === '[' || ch === '{') {
stack.push(ch); // ochuvchini saqlaymiz
} else if (ch in pairs) {
// Yopuvchi: tepadagi element mos ochuvchi bo'lishi shart
if (stack.pop() !== pairs[ch]) return false;
}
}
return stack.length === 0; // barcha qavs yopilganmi?
}
console.log(isValid('()[]{}')); // true
console.log(isValid('(]')); // false
console.log(isValid('([)]')); // false
console.log(isValid('{[]}')); // trueMurakkablik: O(n) vaqt, O(n) xotira.
Chekka holatlar:
- Bo'sh satr — to'g'ri (
true). - Faqat yopuvchi bilan boshlanish (
)) —stack.pop()undefinedqaytaradi,false. - Yakunda stekda qolgan ochuvchilar —
false.
Follow-up: Eng uzun to'g'ri qavslar ketma-ketligi uzunligini topish (longest valid parentheses) qanday yechiladi?
7-kategoriya. Real-world (amaliy) masalalar
Masala 35 : Forma validatsiyasi (qayta ishlatiluvchi)
Qoidalar asosida ishlaydigan, kengaytiriluvchi validatsiya funksiyasini yozing.
Fikrlash: Har maydon uchun validator funksiyalar massivi. Har validator xato xabari yoki null qaytaradi. Barcha maydonlarni aylanib, xatolarni yig'amiz. Bu — deklarativ va qayta ishlatiluvchi yondashuv.
Yechim:
// Qayta ishlatiluvchi qoidalar:
const required = (msg = 'Majburiy maydon') => (v) =>
v == null || v.trim() === '' ? msg : null;
const minLength = (n, msg) => (v) =>
v.length < n ? msg || `Kamida ${n} ta belgi` : null;
const isEmail = (msg = 'Email noto\'g\'ri') => (v) =>
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) ? null : msg;
function validate(values, schema) {
const errors = {};
for (const field in schema) {
for (const rule of schema[field]) {
const error = rule(values[field] ?? '');
if (error) {
errors[field] = error; // birinchi xatoda to'xtaymiz
break;
}
}
}
return errors;
}
// Foydalanish:
const schema = {
email: [required(), isEmail()],
password: [required(), minLength(6)],
};
console.log(validate({ email: 'noto-gri', password: '123' }, schema));
// { email: 'Email noto\'g\'ri', password: 'Kamida 6 ta belgi' }Murakkablik: O(maydonlar × qoidalar).
Chekka holatlar:
null/undefinedqiymatlar —?? ''bilan himoyalanadi.- Bir maydonda bir nechta xato — birinchisida to'xtaymiz (yoki hammasini yig'ish variantini tanlaymiz).
Follow-up: Asinxron validatsiya (masalan, "bu email band emasmi" serverdan tekshirish) qanday qo'shiladi? Zod / Yup kabi kutubxonalar nega ishlatiladi?
Masala 36 : Savat (cart) mantiqi
Mahsulot qo'shish, sonini o'zgartirish, o'chirish va umumiy summani hisoblovchi savat mantiqi.
Fikrlash: Savatni id -> {product, qty} ko'rinishida saqlaymiz. Qo'shishda mavjud bo'lsa sonini oshiramiz. Immutable yangilash muhim (React bilan mos). Umumiy summa — reduce.
Yechim:
function createCart() {
let items = {}; // id -> { product, qty }
return {
add(product, qty = 1) {
const existing = items[product.id];
items = {
...items,
[product.id]: {
product,
qty: (existing?.qty || 0) + qty, // bor bo'lsa oshiramiz
},
};
},
updateQty(id, qty) {
if (qty <= 0) return this.remove(id); // 0 bo'lsa o'chiramiz
items = { ...items, [id]: { ...items[id], qty } };
},
remove(id) {
const next = { ...items };
delete next[id];
items = next;
},
getTotal() {
return Object.values(items).reduce(
(sum, { product, qty }) => sum + product.price * qty,
0
);
},
getItems() {
return Object.values(items);
},
};
}
// Foydalanish:
const cart = createCart();
cart.add({ id: 1, name: 'Kitob', price: 50000 }, 2);
cart.add({ id: 1, name: 'Kitob', price: 50000 }); // qty 3 bo'ladi
console.log(cart.getTotal()); // 150000Murakkablik: Qo'shish/o'chirish O(1); summa O(n).
Chekka holatlar:
- Bir mahsulotni qayta qo'shish — sonini oshiradi, dublikat yaratmaydi.
- Son 0 yoki manfiy — mahsulotni o'chiramiz.
- Suzuvchi nuqtali narx — pulni tiyin/sent (butun son) da saqlash yaxshiroq.
Follow-up: Chegirma (promo-kod), soliq, yetkazib berish hisobini qanday qo'shasiz? Nega pulni suzuvchi nuqtada saqlash xavfli?
Masala 37 : Autocomplete (avtoto'ldirish)
Yozilayotgan matnga mos takliflarni ko'rsatuvchi, klaviatura bilan boshqariladigan komponent.
Fikrlash: Debounce bilan takliflar olamiz. Klaviatura: yuqori/pastki strelka bilan tanlangan indeksni o'zgartiramiz, Enter bilan tanlaymiz, Escape bilan yopamiz. Tanlangan indeks holatda saqlanadi.
Yechim:
import { useState, useEffect } from 'react';
function Autocomplete({ fetchSuggestions }) {
const [query, setQuery] = useState('');
const [items, setItems] = useState([]);
const [activeIndex, setActiveIndex] = useState(-1);
useEffect(() => {
if (!query.trim()) {
setItems([]);
return;
}
const timer = setTimeout(async () => {
const results = await fetchSuggestions(query);
setItems(results);
setActiveIndex(-1); // yangi natijalarda tanlovni tozalaymiz
}, 300);
return () => clearTimeout(timer);
}, [query, fetchSuggestions]);
const handleKeyDown = (e) => {
if (e.key === 'ArrowDown') {
// Ro'yxat bo'ylab pastga (oxirida to'xtaydi)
setActiveIndex((i) => Math.min(i + 1, items.length - 1));
} else if (e.key === 'ArrowUp') {
setActiveIndex((i) => Math.max(i - 1, 0));
} else if (e.key === 'Enter' && activeIndex >= 0) {
setQuery(items[activeIndex].label);
setItems([]); // tanlangach ro'yxatni yopamiz
} else if (e.key === 'Escape') {
setItems([]);
}
};
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Yozing..."
/>
{items.length > 0 && (
<ul>
{items.map((item, index) => (
<li
key={item.id}
// Tanlangan elementni ajratib ko'rsatamiz
style={{
background: index === activeIndex ? '#dfe' : 'transparent',
}}
onMouseEnter={() => setActiveIndex(index)}
onClick={() => {
setQuery(item.label);
setItems([]);
}}
>
{item.label}
</li>
))}
</ul>
)}
</div>
);
}
export default Autocomplete;Murakkablik: Render O(takliflar soni).
Chekka holatlar:
- Race condition: eski so'rov kechroq qaytishi —
AbortControlleryoki so'rov identifikatori kerak. - Klaviatura va sichqoncha o'rtasida tanlovni muvofiqlashtirish (
onMouseEnter). - Ro'yxatdan tashqariga bosilganda yopish (
onBluryoki tashqi bosish tinglovchisi). - A11y:
aria-activedescendant,role="listbox"kabi atributlar.
Follow-up: Natijalarni keshlash, tanlangan elementni ekranda ko'rinadigan qilib scroll qilish (scrollIntoView) va to'liq a11y ni qanday qo'shasiz?
Yakuniy strategiya — live-coding intervyuni qanday yutish
Masalalarni yechish texnikasidan tashqari, jarayonni to'g'ri boshqarish ham xuddi shunchalik muhim. Quyidagi umumiy strategiyaga amal qiling:
1. UMPIRE tizimi
- Understand — savolni to'liq tushuning, misol so'rang.
- Match — bu qaysi ma'lum masala turiga o'xshaydi? (hash-map, two-pointer, stack...)
- Plan — yechim rejasini ovoz chiqarib ayting.
- Implement — kod yozing.
- Review — dry-run bilan tekshiring.
- Evaluate — murakkablik va yaxshilash imkoniyatlarini ayting.
2. Sukut saqlamang
Eng katta xato — jim qolish. Intervyu oluvchi sizning fikringizni eshitmasa, baholay olmaydi. Hatto "hozir qanday yondashish haqida o'ylayapman" deyish ham yaxshi.
3. Tiqilib qolsangiz
- Sodda misolni qo'lda yeching va naqshni izlang.
- Brute-force yechimni aytib, keyin yaxshilashni taklif qiling.
- Yordam so'rashdan qo'rqmang: "Shu yo'nalish to'g'rimi?" — bu zaiflik emas, hamkorlik belgisi.
4. Chekka holatlarni oldindan ayting
Kod yozishdan oldin yoki keyin: "Bo'sh kirish, null, takror qiymatlar, juda katta kirish bilan nima bo'ladi?" — buni aytish sizni sinior darajasida ko'rsatadi.
5. Toza kod yozing
- Mazmunli o'zgaruvchi nomlari.
- Kichik, bitta vazifani bajaruvchi funksiyalar.
- Sehrli sonlar (magic numbers) o'rniga nomlangan konstantalar.
6. Vaqtni boshqaring
Bitta qismda tiqilib qolsangiz, "buni hozircha shunday deb qo'yaylik, keyin qaytamiz" deb oldinga siljing. To'liq bo'lmagan, lekin ishlaydigan yechim — mukammal, lekin tugallanmagan yechimdan yaxshiroq.
7. Amaliyot manbalari
- LeetCode — algoritmik masalalar.
- Frontend Mentor / JavaScript30 — real UI masalalari.
- BFE.dev / GreatFrontEnd — aynan shu faylning mavzularidagi frontend live-coding masalalari.
Eng muhim maslahat: Live-coding — bu imtihon emas, hamkorlikdagi ish suhbatidir. Intervyu oluvchi sizni "qulab tushishingiz"ni emas, sizning haqiqiy ish sharoitida qanday ishlashingizni ko'rmoqchi. Xotirjam bo'ling, ovoz chiqarib fikrlang va jarayondan zavq oling.
Omad tilaymiz! Ushbu masalalarni bir necha marta, oldingi yechimga qaramasdan qaytadan yechib chiqing — shundagina bilim barqarorlashadi.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!