Live Coding

Memeriksa apakah Objek Kosong

Bagaimana cara memeriksa apakah suatu objek JavaScript kosong?

Penjelasan: Sebuah objek kosong jika tidak memiliki properti enumerable sendiri. Kita bisa menggunakan Object.keys(obj) yang mengembalikan array dari nama-nama properti enumerable milik objek yang diberikan. Jika panjang array ini adalah 0, objek tersebut kosong.

function isEmpty(obj) { return Object.keys(obj).length === 0; } console.log(isEmpty({})); // true console.log(isEmpty({ a: 1 })); // false

Membalik String

Tulis fungsi untuk membalikkan string yang diberikan.

Penjelasan: Cara paling sederhana adalah mengubah string menjadi array karakter, menggunakan metode reverse() bawaan untuk array, lalu menggabungkan kembali karakter-karakter tersebut menjadi sebuah string.

function reverseString(str) { return str.split('').reverse().join(''); } console.log(reverseString('hello')); // 'olleh'

Pengecekan Palindrom

Tulis fungsi yang memeriksa apakah string yang diberikan adalah palindrom.

Penjelasan: Palindrom adalah kata atau frasa yang terbaca sama dari depan maupun belakang. Kita bisa memeriksa ini dengan membalikkan string (mengabaikan huruf besar/kecil dan karakter non-alfanumerik untuk pemeriksaan yang lebih kuat, tetapi di sini kita melakukan pemeriksaan sederhana) dan membandingkannya dengan aslinya.

function isPalindrome(str) { const reversed = str.split('').reverse().join(''); return str === reversed; } console.log(isPalindrome('racecar')); // true console.log(isPalindrome('hello')); // false

Mencari Angka Maksimum dalam Array

Tulis fungsi untuk menemukan angka terbesar dalam array angka.

Penjelasan: Anda dapat mengulang array, melacak angka terbesar yang ditemukan sejauh ini. Atau, Anda dapat menggunakan fungsi Math.max() bersama dengan sintaks spread (...) untuk meneruskan elemen array sebagai argumen.

function findMaxNumber(arr) { if (arr.length === 0) return undefined; // Atau lemparkan error return Math.max(...arr); } console.log(findMaxNumber([1, 5, 2, 9, 3])); // 9

FizzBuzz

Tulis program yang mencetak angka dari 1 hingga n. Tetapi untuk kelipatan tiga, cetak 'Fizz' alih-alih angka, dan untuk kelipatan lima, cetak 'Buzz'. Untuk angka yang merupakan kelipatan tiga dan lima, cetak 'FizzBuzz'.

Penjelasan: Masalah klasik ini menguji logika dasar loop dan kondisi. Anda perlu mengulang dari 1 hingga n dan menggunakan operator modulo (%) untuk memeriksa keterbagian oleh 3 dan 5.

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

Menghapus Duplikat dari Array

Tulis fungsi yang menghapus elemen duplikat dari array.

Penjelasan: Cara modern dan ringkas untuk mencapai ini adalah dengan menggunakan Set. Set hanya menyimpan nilai unik. Anda dapat mengubah array menjadi Set dan kemudian kembali menjadi array.

function removeDuplicates(arr) { return [...new Set(arr)]; } console.log(removeDuplicates([1, 2, 2, 3, 4, 4, 5])); // [1, 2, 3, 4, 5]

Pengecekan Anagram

Tulis fungsi untuk memeriksa apakah dua string adalah anagram satu sama lain.

Penjelasan: Anagram adalah kata-kata yang dibentuk dengan menyusun ulang huruf-huruf dari kata lain. Untuk memeriksa, kita dapat membersihkan, mengurutkan, dan membandingkan string. Pembersihan melibatkan penghapusan karakter non-alfanumerik dan konversi ke kasus yang konsisten.

function isAnagram(str1, str2) { const clean = (str) => str.replace(/[^a-z0-9]/gi, '').toLowerCase(); const sorted = (str) => clean(str).split('').sort().join(''); return sorted(str1) === sorted(str2); } console.log(isAnagram('listen', 'silent')); // true console.log(isAnagram('hello', 'world')); // false

Menghitung Faktorial

Tulis fungsi untuk menghitung faktorial dari bilangan bulat non-negatif.

Penjelasan: Faktorial (n!) dari sebuah bilangan adalah hasil kali semua bilangan bulat positif yang kurang dari atau sama dengan n. Kita dapat menghitung ini secara iteratif dengan mengalikan bilangan dari 1 hingga n.

function factorial(n) { if (n < 0) return undefined; // Faktorial tidak didefinisikan untuk bilangan negatif if (n === 0) return 1; let result = 1; for (let i = 1; i <= n; i++) { result *= i; } return result; } console.log(factorial(5)); // 120

Menjumlahkan Semua Angka dalam Array

Tulis fungsi yang mengembalikan jumlah semua angka dalam array.

Penjelasan: Anda dapat menggunakan metode reduce, yang ideal untuk mengakumulasi nilai tunggal dari sebuah array. Metode ini mengambil fungsi callback dan nilai awal (0 untuk penjumlahan).

function sumArray(arr) { return arr.reduce((accumulator, currentValue) => accumulator + currentValue, 0); } console.log(sumArray([1, 2, 3, 4])); // 10

Meratakan Array Bertingkat

Tulis fungsi untuk meratakan array bertingkat. Untuk kesederhanaan, asumsikan hanya satu tingkat tingkatan.

Penjelasan: Untuk satu tingkat tingkatan, Anda dapat menggunakan Array.prototype.concat() dengan operator spread atau metode flat() (ES2019).

function flattenArray(arr) { // Menggunakan flat() (lebih sederhana jika ES2019+ bisa) // return arr.flat(); // Menggunakan reduce dan concat return arr.reduce((acc, val) => acc.concat(val), []); } console.log(flattenArray([1, [2, 3], 4, [5]])); // [1, 2, 3, 4, 5]

Menghitung Vokal dalam String

Tulis fungsi yang menghitung jumlah vokal (a, e, i, o, u) dalam string yang diberikan.

Penjelasan: Iterasi string (tidak peka huruf besar/kecil) dan periksa apakah setiap karakter adalah vokal. Pertahankan penghitung.

function countVowels(str) { const vowels = 'aeiou'; let count = 0; for (let char of str.toLowerCase()) { if (vowels.includes(char)) { count++; } } return count; } console.log(countVowels('Hello World')); // 3

Mengubah Kalimat menjadi Title Case

Tulis fungsi yang mengubah kalimat menjadi title case (huruf pertama setiap kata dikapitalisasi).

Penjelasan: Pisahkan kalimat menjadi kata-kata, lalu ulangi setiap kata, mengkapitalisasi huruf pertama dan membuat sisanya huruf kecil. Terakhir, gabungkan kembali kata-kata tersebut.

function titleCase(str) { return str.toLowerCase().split(' ').map(word => { return word.charAt(0).toUpperCase() + word.slice(1); }).join(' '); } console.log(titleCase('i am a little tea pot')); // 'I Am A Little Tea Pot'

Masalah Dua Jumlah

Diberikan sebuah array bilangan bulat nums dan sebuah bilangan bulat target, kembalikan indeks dari dua bilangan sehingga jika dijumlahkan menghasilkan target.

Penjelasan: Pendekatan umum adalah menggunakan hash map (atau objek JavaScript) untuk menyimpan angka dan indeksnya saat Anda mengulang. Untuk setiap angka, periksa apakah target - current_number ada di peta.

function twoSum(nums, target) { const map = {}; for (let i = 0; i < nums.length; i++) { const complement = target - nums[i]; if (map[complement] !== undefined) { return [map[complement], i]; } map[nums[i]] = i; } return []; // Atau null, atau lemparkan error jika tidak ada solusi } console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1]

Mengimplementasikan Penghitung menggunakan Closures

Buat fungsi yang mengembalikan fungsi penghitung. Setiap kali fungsi yang dikembalikan dipanggil, ia harus bertambah dan mengembalikan hitungan.

Penjelasan: Ini menunjukkan closure. Fungsi dalam memiliki akses ke variabel count dari lingkup fungsi luar, bahkan setelah fungsi luar selesai dieksekusi.

function createCounter() { let count = 0; return function() { count++; return count; }; } const counter1 = createCounter(); console.log(counter1()); // 1 console.log(counter1()); // 2 const counter2 = createCounter(); console.log(counter2()); // 1

Mencari Karakter Non-Pengulangan Pertama

Tulis fungsi yang menemukan karakter non-pengulangan pertama dalam sebuah string.

Penjelasan: Anda dapat menggunakan hash map untuk menghitung frekuensi karakter. Pertama, ulangi string untuk membangun peta frekuensi. Kemudian, ulangi string lagi dan kembalikan karakter pertama dengan hitungan 1.

function firstNonRepeatingChar(str) { const charCount = {}; for (const char of str) { charCount[char] = (charCount[char] || 0) + 1; } for (const char of str) { if (charCount[char] === 1) { return char; } } return null; // Atau beberapa indikator jika semuanya berulang } console.log(firstNonRepeatingChar('aabbcdeeff')); // 'c' console.log(firstNonRepeatingChar('swiss')); // 'w'

Pengelompokan Array

Tulis fungsi yang membagi array menjadi kelompok-kelompok dengan ukuran tertentu.

Penjelasan: Iterasi melalui array, mengambil irisan dengan ukuran yang ditentukan dan mendorongnya ke array baru. Tangani kasus di mana potongan terakhir mungkin lebih kecil.

function chunkArray(arr, size) { const chunked = []; let index = 0; while (index < arr.length) { chunked.push(arr.slice(index, index + size)); index += size; } return chunked; } console.log(chunkArray([1, 2, 3, 4, 5, 6, 7], 3)); // [[1, 2, 3], [4, 5, 6], [7]]

Memeriksa apakah Angka Prima

Tulis fungsi untuk menentukan apakah angka yang diberikan adalah bilangan prima.

Penjelasan: Bilangan prima adalah bilangan asli lebih besar dari 1 yang tidak memiliki pembagi positif selain 1 dan dirinya sendiri. Iterasi dari 2 hingga akar kuadrat dari bilangan, periksa keterbagian. Tangani kasus ekstrem seperti 1 dan 2.

function isPrime(num) { if (num <= 1) return false; if (num <= 3) return true; if (num % 2 === 0 || num % 3 === 0) return false; for (let i = 5; i * i <= num; i = i + 6) { if (num % i === 0 || num % (i + 2) === 0) return false; } return true; } console.log(isPrime(7)); // true console.log(isPrime(10)); // false

Mencari Kata Terpanjang dalam String

Tulis fungsi yang menemukan kata terpanjang dalam sebuah kalimat.

Penjelasan: Pisahkan string menjadi array kata-kata. Kemudian, ulangi array, melacak kata terpanjang yang ditemukan sejauh ini (atau panjangnya).

function findLongestWord(str) { const words = str.split(' '); let longestWord = ''; for (const word of words) { // Bersihkan kata jika diperlukan (misalnya, hapus tanda baca) if (word.length > longestWord.length) { longestWord = word; } } return longestWord; } console.log(findLongestWord('The quick brown fox jumped over the lazy dog')); // 'jumped'

Mengimplementasikan `Array.prototype.map`

Implementasikan versi Anda sendiri dari fungsi Array.prototype.map.

Penjelasan: Fungsi map mengambil callback dan membuat array baru dengan menerapkan callback ke setiap elemen dari array asli. Fungsi Anda harus mengulang array dan membangun array baru.

function myMap(arr, callback) { const mappedArray = []; for (let i = 0; i < arr.length; i++) { mappedArray.push(callback(arr[i], i, arr)); } return mappedArray; } const numbers = [1, 4, 9]; const roots = myMap(numbers, Math.sqrt); console.log(roots); // [1, 2, 3]

Mengimplementasikan `Array.prototype.filter`

Implementasikan versi Anda sendiri dari fungsi Array.prototype.filter.

Penjelasan: Fungsi filter mengambil callback dan membuat array baru dengan semua elemen yang melewati pengujian yang diimplementasikan oleh fungsi callback yang disediakan.

function myFilter(arr, callback) { const filteredArray = []; for (let i = 0; i < arr.length; i++) { if (callback(arr[i], i, arr)) { filteredArray.push(arr[i]); } } return filteredArray; } const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']; const result = myFilter(words, word => word.length > 6); console.log(result); // ['exuberant', 'destruction', 'present']

Mengimplementasikan `Array.prototype.reduce`

Implementasikan versi Anda sendiri dari fungsi Array.prototype.reduce.

Penjelasan: Fungsi reduce mengeksekusi fungsi callback 'reducer' yang disediakan pengguna pada setiap elemen array, meneruskan nilai pengembalian dari perhitungan pada elemen sebelumnya. Hasil akhir dari menjalankan reducer di semua elemen array adalah nilai tunggal. Tangani argumen nilai awal.

function myReduce(arr, callback, initialValue) { let accumulator = initialValue; let startIndex = 0; if (initialValue === undefined) { if (arr.length === 0) throw new TypeError('Reduce of empty array with no initial value'); accumulator = arr[0]; startIndex = 1; } for (let i = startIndex; i < arr.length; i++) { accumulator = callback(accumulator, arr[i], i, arr); } return accumulator; } const array1 = [1, 2, 3, 4]; const sum = myReduce(array1, (acc, curr) => acc + curr, 0); console.log(sum); // 10

Memoization - Urutan Fibonacci

Implementasikan fungsi Fibonacci menggunakan memoization untuk mengoptimalkan kinerja.

Penjelasan: Urutan Fibonacci melibatkan perhitungan berulang. Memoization menyimpan hasil panggilan fungsi yang mahal dan mengembalikan hasil yang di-cache saat input yang sama terjadi lagi.

function memoizedFib() { const cache = {}; function fib(n) { if (n in cache) { return cache[n]; } if (n <= 1) { return n; } const result = fib(n - 1) + fib(n - 2); cache[n] = result; return result; } return fib; } const fibonacci = memoizedFib(); console.log(fibonacci(10)); // 55 console.log(fibonacci(40)); // 102334155 (jauh lebih cepat daripada yang tidak di-memoized)

Memeriksa Kurung Seimbang

Tulis fungsi untuk memeriksa apakah string yang berisi kurung {}[]() seimbang.

Penjelasan: Gunakan tumpukan (stack). Saat Anda menemukan kurung buka, dorong ke tumpukan. Saat Anda menemukan kurung tutup, periksa apakah tumpukan kosong atau apakah elemen teratas adalah kurung buka yang cocok. Jika cocok, pop tumpukan. Jika tidak, atau jika tumpukan kosong, itu tidak seimbang. Pada akhirnya, tumpukan kosong berarti seimbang.

function isBalanced(str) { const stack = []; const map = { '(': ')', '{': '}', '[': ']' }; for (let char of str) { if (map[char]) { stack.push(char); } else if (Object.values(map).includes(char)) { if (stack.length === 0) return false; const lastOpen = stack.pop(); if (map[lastOpen] !== char) return false; } } return stack.length === 0; } console.log(isBalanced('({[]})')); // true console.log(isBalanced('([)]')); // false

Mengimplementasikan Antrean Sederhana

Implementasikan struktur data Antrean (Queue) dengan metode enqueue (tambah ke belakang) dan dequeue (hapus dari depan).

Penjelasan: Antrean mengikuti prinsip First-In, First-Out (FIFO). Array dapat digunakan, dengan push untuk enqueue dan shift untuk dequeue.

class Queue { constructor() { this.items = []; } enqueue(element) { this.items.push(element); } dequeue() { if (this.isEmpty()) return 'Underflow'; return this.items.shift(); } front() { if (this.isEmpty()) return 'No elements in Queue'; return this.items[0]; } isEmpty() { return this.items.length === 0; } } const q = new Queue(); q.enqueue(10); q.enqueue(20); console.log(q.dequeue()); // 10 console.log(q.front()); // 20

Mengimplementasikan Tumpukan Sederhana

Implementasikan struktur data Tumpukan (Stack) dengan metode push (tambah ke atas) dan pop (hapus dari atas).

Penjelasan: Tumpukan mengikuti prinsip Last-In, First-Out (LIFO). Array dapat digunakan, dengan metode push dan pop.

class Stack { constructor() { this.items = []; } push(element) { this.items.push(element); } pop() { if (this.isEmpty()) return 'Underflow'; return this.items.pop(); } peek() { return this.items[this.items.length - 1]; } isEmpty() { return this.items.length === 0; } } const s = new Stack(); s.push(10); s.push(20); console.log(s.pop()); // 20 console.log(s.peek()); // 10

Mencari Angka yang Hilang dalam Urutan

Diberikan sebuah array yang berisi n bilangan berbeda yang diambil dari 0, 1, 2, ..., n, temukan bilangan yang hilang dari array tersebut.

Penjelasan: Jumlah bilangan dari 0 hingga n dapat dihitung menggunakan rumus n*(n+1)/2. Jumlah sebenarnya dari elemen array dapat dihitung. Selisih antara kedua jumlah ini akan menjadi bilangan yang hilang.

function findMissingNumber(nums) { const n = nums.length; const expectedSum = n * (n + 1) / 2; const actualSum = nums.reduce((sum, num) => sum + num, 0); return expectedSum - actualSum; } console.log(findMissingNumber([3, 0, 1])); // 2 console.log(findMissingNumber([9, 6, 4, 2, 3, 5, 7, 0, 1])); // 8

Fungsi Debounce

Implementasikan fungsi debounce. Debouncing memastikan bahwa suatu fungsi tidak dipanggil lagi sampai jangka waktu tertentu telah berlalu tanpa dipanggil.

Penjelasan: Gunakan setTimeout dan clearTimeout. Setiap kali fungsi yang di-debounce dipanggil, hapus timeout sebelumnya dan atur timeout baru. Panggilan fungsi yang sebenarnya hanya terjadi ketika timeout selesai.

function debounce(func, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => { func.apply(this, args); }, delay); }; } // Contoh penggunaan: const sayHello = () => console.log('Hello!'); const debouncedHello = debounce(sayHello, 1000); debouncedHello(); // Dipanggil setelah 1 detik (jika tidak dipanggil lagi) debouncedHello(); // Mereset timer debouncedHello(); // Mereset timer, akan benar-benar berjalan 1 detik setelah panggilan ini.

Fungsi Throttle

Implementasikan fungsi throttle. Throttling memastikan bahwa suatu fungsi dipanggil paling banyak sekali dalam interval waktu tertentu.

Penjelasan: Gunakan flag untuk menunjukkan apakah panggilan diizinkan. Saat dipanggil, jika diizinkan, jalankan fungsi, atur flag ke false, dan gunakan setTimeout untuk mereset flag setelah interval.

function throttle(func, limit) { let inThrottle = false; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } // Contoh penggunaan: const logScroll = () => console.log('Scrolling...'); const throttledScroll = throttle(logScroll, 1000); // window.addEventListener('scroll', throttledScroll); // Akan mencatat paling banyak sekali per detik.

Fungsi Currying

Tulis fungsi yang mengambil fungsi dengan dua argumen dan mengembalikan versi curried-nya.

Penjelasan: Currying mengubah fungsi dengan banyak argumen menjadi urutan fungsi, masing-masing mengambil satu argumen. f(a, b) menjadi f(a)(b).

function curry(fn) { return function(a) { return function(b) { return fn(a, b); }; }; } function add(a, b) { return a + b; } const curriedAdd = curry(add); const add5 = curriedAdd(5); console.log(add5(3)); // 8 console.log(curriedAdd(10)(20)); // 30

Deep Clone Objek

Tulis fungsi untuk melakukan deep clone dari objek JavaScript.

Penjelasan: Shallow clone hanya menyalin properti tingkat atas. Deep clone secara rekursif menyalin semua properti, termasuk objek dan array bertingkat. Cara sederhana (dengan batasan, mis. tidak menangani fungsi, tanggal, regex dengan baik) adalah menggunakan JSON.parse(JSON.stringify(obj)). Pendekatan rekursif lebih kuat.

function deepClone(obj) { // Versi sederhana (dengan batasan) try { return JSON.parse(JSON.stringify(obj)); } catch (e) { console.error("Kloning gagal:", e); return null; } // Rekursif yang lebih kuat (contoh dasar): /* if (obj === null || typeof obj !== 'object') { return obj; } let clone = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { clone[key] = deepClone(obj[key]); } } return clone; */ } const original = { a: 1, b: { c: 2 } }; const cloned = deepClone(original); cloned.b.c = 3; console.log(original.b.c); // 2 (membuktikan itu deep clone) console.log(cloned.b.c); // 3

Mendapatkan Kunci Objek

Bagaimana cara mendapatkan array kunci dari sebuah objek?

Penjelasan: Gunakan Object.keys(obj).

function getKeys(obj) { return Object.keys(obj); } console.log(getKeys({a: 1, b: 2})); // ['a', 'b']

Mendapatkan Nilai Objek

Bagaimana cara mendapatkan array nilai dari sebuah objek?

Penjelasan: Gunakan Object.values(obj).

function getValues(obj) { return Object.values(obj); } console.log(getValues({a: 1, b: 2})); // [1, 2]

Memeriksa apakah Array Menyertakan Nilai

Bagaimana cara memeriksa apakah sebuah array berisi nilai tertentu?

Penjelasan: Gunakan Array.prototype.includes(value).

function checkIncludes(arr, val) { return arr.includes(val); } console.log(checkIncludes([1, 2, 3], 2)); // true

Menggabungkan Dua Array

Bagaimana cara menggabungkan dua array menjadi satu?

Penjelasan: Gunakan sintaks spread (...) atau Array.prototype.concat().

function mergeArrays(arr1, arr2) { return [...arr1, ...arr2]; } console.log(mergeArrays([1, 2], [3, 4])); // [1, 2, 3, 4]

Menjelaskan 'this' dalam Lingkup Global

Apa yang dimaksud this dalam lingkup global di browser?

Penjelasan: Dalam konteks eksekusi global (di luar fungsi apa pun), this mengacu pada objek global, yaitu window di browser web.

console.log(this === window); // true (di lingkungan browser)

Menjelaskan 'this' dalam Metode Objek

Apa yang dimaksud this saat digunakan di dalam metode objek?

Penjelasan: Ketika sebuah fungsi dipanggil sebagai metode dari sebuah objek, this mengacu pada objek tempat metode tersebut dipanggil.

const myObject = { prop: 'Hello', greet() { return this.prop; } }; console.log(myObject.greet()); // 'Hello'

Menjelaskan 'this' dengan Fungsi Panah (Arrow Functions)

Bagaimana fungsi panah menangani this?

Penjelasan: Fungsi panah tidak memiliki konteks this sendiri. Sebaliknya, mereka mewarisi this dari cakupan (leksikal) di sekitarnya tempat mereka didefinisikan.

function MyClass() { this.value = 42; setTimeout(() => { console.log(this.value); // Mencatat 42 karena 'this' diwarisi }, 100); } new MyClass();

Perbedaan antara `let`, `const`, dan `var`

Apa perbedaan utama antara let, const, dan var?

Penjelasan: var memiliki cakupan fungsi dan di-hoist. let dan const memiliki cakupan blok dan di-hoist tetapi berada dalam 'zona mati temporal' sampai deklarasi. const harus diinisialisasi dan tidak dapat ditetapkan ulang.

function scopeTest() { var a = 1; let b = 2; const c = 3; if (true) { var a = 10; // Mendeklarasikan ulang dan memengaruhi 'a' luar let b = 20; // 'b' baru di dalam blok // const c = 30; // Akan menjadi 'c' baru console.log(a, b, c); // 10, 20, 3 } console.log(a, b, c); // 10, 2, 3 } scopeTest();

Apa itu Promise?

Jelaskan apa itu Promise dalam JavaScript.

Penjelasan: Promise adalah objek yang mewakili penyelesaian (atau kegagalan) operasi asinkron dan nilai hasilnya. Ini dapat berada dalam salah satu dari tiga keadaan: pending, fulfilled, atau rejected.

const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve('Sukses!'); // reject('Error!'); }, 1000); }); myPromise .then(result => console.log(result)) .catch(error => console.error(error));

Menggunakan `async/await`

Bagaimana async dan await menyederhanakan bekerja dengan Promise?

Penjelasan: async/await menyediakan sintaks gula di atas Promise, membuat kode asinkron terlihat dan berperilaku sedikit lebih seperti kode sinkron. Fungsi async selalu mengembalikan Promise, dan await menjeda eksekusi sampai Promise terselesaikan.

function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function run() { console.log('Memulai...'); await delay(1000); console.log('Menunggu 1 detik.'); await delay(500); console.log('Menunggu 0,5 detik lagi.'); return 'Selesai!'; } run().then(console.log);

Mengubah String menjadi Angka

Bagaimana cara mengubah string menjadi angka?

Penjelasan: Gunakan parseInt(), parseFloat(), atau operator plus unary (+).

const str = '123.45'; console.log(parseInt(str)); // 123 console.log(parseFloat(str)); // 123.45 console.log(+str); // 123.45

Mengubah Angka menjadi String

Bagaimana cara mengubah angka menjadi string?

Penjelasan: Gunakan String(), number.toString(), atau konkatenasi string.

const num = 123; console.log(String(num)); // '123' console.log(num.toString()); // '123' console.log('' + num); // '123'

Apa itu `JSON.stringify`?

Apa yang dilakukan JSON.stringify?

Penjelasan: Ini mengubah objek atau nilai JavaScript menjadi string JSON.

const obj = { name: 'Alice', age: 30 }; const jsonString = JSON.stringify(obj); console.log(jsonString); // '{"name":"Alice","age":30}'

Apa itu `JSON.parse`?

Apa yang dilakukan JSON.parse?

Penjelasan: Ini menguraikan string JSON, membangun nilai atau objek JavaScript yang dijelaskan oleh string.

const jsonString = '{"name":"Alice","age":30}'; const obj = JSON.parse(jsonString); console.log(obj.name); // 'Alice'

Operator Spread dalam Array

Bagaimana operator spread digunakan dengan array?

Penjelasan: Ini memungkinkan iterable (seperti array) untuk diperluas di tempat-tempat di mana nol atau lebih argumen atau elemen diharapkan. Berguna untuk menyalin, menggabungkan, dan menambahkan elemen.

const arr1 = [1, 2]; const arr2 = [3, 4]; const combined = [...arr1, ...arr2]; // [1, 2, 3, 4] const copy = [...arr1]; // [1, 2]

Operator Spread dalam Objek

Bagaimana operator spread digunakan dengan objek?

Penjelasan: Ini memungkinkan penyalinan dan penggabungan properti objek.

const obj1 = { a: 1, b: 2 }; const obj2 = { b: 3, c: 4 }; const merged = { ...obj1, ...obj2 }; // { a: 1, b: 3, c: 4 } const copy = { ...obj1 }; // { a: 1, b: 2 }

Destrukturisasi Array

Jelaskan destrukturisasi array dengan sebuah contoh.

Penjelasan: Ini adalah sintaks yang memungkinkan untuk membongkar nilai dari array ke dalam variabel yang berbeda.

const [a, b] = [10, 20]; console.log(a); // 10 console.log(b); // 20 const [x, , z] = [1, 2, 3]; console.log(z); // 3

Destrukturisasi Objek

Jelaskan destrukturisasi objek dengan sebuah contoh.

Penjelasan: Ini memungkinkan untuk membongkar properti dari objek ke dalam variabel yang berbeda.

const person = { name: 'Bob', age: 25 }; const { name, age } = person; console.log(name); // 'Bob' console.log(age); // 25 const { name: personName } = person; console.log(personName); // 'Bob'

Mengimplementasikan `call`

Bagaimana Anda bisa mengimplementasikan versi dasar dari Function.prototype.call?

Penjelasan: call memanggil fungsi dengan nilai this yang ditentukan dan argumen yang disediakan secara individual. Anda dapat melampirkan fungsi ke konteks this, memanggilnya, dan kemudian menghapusnya.

Function.prototype.myCall = function(context, ...args) { context = context || window; const uniqueId = Symbol(); // Gunakan kunci unik context[uniqueId] = this; const result = context[uniqueId](...args); delete context[uniqueId]; return result; } function greet(greeting, punctuation) { console.log(`${greeting}, ${this.name}${punctuation}`); } greet.myCall({ name: 'Charlie' }, 'Hi', '!'); // Hi, Charlie!

Mengimplementasikan `apply`

Bagaimana Anda bisa mengimplementasikan versi dasar dari Function.prototype.apply?

Penjelasan: apply mirip dengan call, tetapi menerima argumen sebagai array.

Function.prototype.myApply = function(context, argsArray) { context = context || window; const uniqueId = Symbol(); context[uniqueId] = this; const result = context[uniqueId](...(argsArray || [])); delete context[uniqueId]; return result; } function greet(greeting, punctuation) { console.log(`${greeting}, ${this.name}${punctuation}`); } greet.myApply({ name: 'David' }, ['Hello', '.']); // Hello, David.

Jelaskan Event Loop

Jelaskan secara singkat JavaScript Event Loop.

Penjelasan: JavaScript adalah single-threaded, tetapi mencapai konkurensi menggunakan event loop. Call stack menangani kode sinkron. Web API menangani operasi asinkron (seperti setTimeout, fetch). Ketika operasi asinkron selesai, callback-nya masuk ke antrean callback (atau antrean microtask untuk Promise). Event loop terus-menerus memeriksa apakah call stack kosong; jika ya, ia memindahkan callback berikutnya dari antrean ke stack untuk dieksekusi.

console.log('Mulai'); setTimeout(() => { console.log('Callback Timeout'); // Masuk ke Callback Queue }, 0); Promise.resolve().then(() => { console.log('Promise Diselesaikan'); // Masuk ke Microtask Queue }); console.log('Selesai'); // Urutan Output: Mulai, Selesai, Promise Diselesaikan, Callback Timeout // (Microtasks berjalan sebelum Macrotasks/Callbacks)

Pencarian Biner (Binary Search)

Implementasikan fungsi pencarian biner untuk array yang diurutkan.

Penjelasan: Pencarian biner secara efisien menemukan item dalam array yang diurutkan dengan berulang kali membagi interval pencarian menjadi dua. Jika nilai kunci pencarian lebih kecil dari item di tengah interval, persempit interval ke setengah bagian bawah. Jika tidak, persempit ke setengah bagian atas. Anda melakukan ini sampai nilai ditemukan atau interval kosong.

function binarySearch(sortedArray, key) { let start = 0; let end = sortedArray.length - 1; while (start <= end) { let middle = Math.floor((start + end) / 2); if (sortedArray[middle] === key) { return middle; // Ditemukan } else if (sortedArray[middle] < key) { start = middle + 1; // Cari setengah kanan } else { end = middle - 1; // Cari setengah kiri } } return -1; // Tidak ditemukan } console.log(binarySearch([1, 3, 5, 7, 9, 11], 7)); // 3 console.log(binarySearch([1, 3, 5, 7, 9, 11], 2)); // -1

Merge Sort

Implementasikan algoritma Merge Sort.

Penjelasan: Merge Sort adalah algoritma divide-and-conquer. Ini membagi array masukan menjadi dua bagian, memanggil dirinya sendiri untuk kedua bagian, dan kemudian menggabungkan dua bagian yang diurutkan. Fungsi penggabungan sangat penting; ini menggabungkan dua sub-array yang diurutkan menjadi satu array yang diurutkan.

function mergeSort(arr) { if (arr.length <= 1) return arr; const mid = Math.floor(arr.length / 2); const left = mergeSort(arr.slice(0, mid)); const right = mergeSort(arr.slice(mid)); return merge(left, right); } function merge(left, right) { let result = []; let leftIndex = 0; let rightIndex = 0; while (leftIndex < left.length && rightIndex < right.length) { if (left[leftIndex] < right[rightIndex]) { result.push(left[leftIndex]); leftIndex++; } else { result.push(right[rightIndex]); rightIndex++; } } return result.concat(left.slice(leftIndex)).concat(right.slice(rightIndex)); } console.log(mergeSort([38, 27, 43, 3, 9, 82, 10])); // [3, 9, 10, 27, 38, 43, 82]

Quick Sort

Implementasikan algoritma Quick Sort.

Penjelasan: Quick Sort juga merupakan algoritma divide-and-conquer. Ia memilih elemen sebagai pivot dan mempartisi array yang diberikan di sekitar pivot yang dipilih. Elemen yang lebih kecil dari pivot pergi ke kiri, dan elemen yang lebih besar pergi ke kanan. Kemudian secara rekursif mengurutkan sub-array.

function quickSort(arr) { if (arr.length <= 1) return arr; const pivot = arr[arr.length - 1]; const left = []; const right = []; for (let i = 0; i < arr.length - 1; i++) { if (arr[i] < pivot) { left.push(arr[i]); } else { right.push(arr[i]); } } return [...quickSort(left), pivot, ...quickSort(right)]; } console.log(quickSort([10, 8, 2, 1, 6, 3, 9, 4, 7, 5])); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Bubble Sort

Implementasikan algoritma Bubble Sort.

Penjelasan: Bubble Sort adalah algoritma pengurutan sederhana yang berulang kali melangkah melalui daftar, membandingkan elemen-elemen yang berdekatan, dan menukarnya jika urutannya salah. Lintasan melalui daftar diulang sampai daftar diurutkan.

function bubbleSort(arr) { let n = arr.length; let swapped; do { swapped = false; for (let i = 0; i < n - 1; i++) { if (arr[i] > arr[i + 1]) { [arr[i], arr[i + 1]] = [arr[i + 1], arr[i]]; // Tukar swapped = true; } } n--; // Optimasi: elemen terakhir sudah pada tempatnya } while (swapped); return arr; } console.log(bubbleSort([64, 34, 25, 12, 22, 11, 90])); // [11, 12, 22, 25, 34, 64, 90]

Substr Berturut-turut Terpanjang Tanpa Karakter Berulang

Diberikan sebuah string, temukan panjang substring terpanjang tanpa karakter berulang.

Penjelasan: Gunakan teknik 'sliding window'. Pertahankan jendela (substring) dan set karakter dalam jendela itu. Perluas jendela dengan menggerakkan penunjuk kanan. Jika karakter berulang ditemukan, kecilkan jendela dari kiri hingga karakter berulang dihapus.

function lengthOfLongestSubstring(s) { let maxLength = 0; let start = 0; const charMap = {}; for (let end = 0; end < s.length; end++) { const char = s[end]; if (charMap[char] >= start) { start = charMap[char] + 1; } charMap[char] = end; maxLength = Math.max(maxLength, end - start + 1); } return maxLength; } console.log(lengthOfLongestSubstring('abcabcbb')); // 3 ('abc') console.log(lengthOfLongestSubstring('bbbbb')); // 1 ('b') console.log(lengthOfLongestSubstring('pwwkew')); // 3 ('wke')

Mengimplementasikan Linked List

Implementasikan Singly Linked List dengan metode add dan print.

Penjelasan: Linked list adalah struktur data linier di mana elemen tidak disimpan di lokasi memori yang berdekatan. Setiap elemen (node) menunjuk ke elemen berikutnya. Anda membutuhkan kelas Node dan kelas LinkedList untuk mengelola head dan menambahkan node baru.

class Node { constructor(data, next = null) { this.data = data; this.next = next; } } class LinkedList { constructor() { this.head = null; } add(data) { const newNode = new Node(data); if (!this.head) { this.head = newNode; } else { let current = this.head; while (current.next) { current = current.next; } current.next = newNode; } } print() { let current = this.head; let list = ''; while (current) { list += current.data + ' -> '; current = current.next; } console.log(list + 'null'); } } const list = new LinkedList(); list.add(10); list.add(20); list.add(30); list.print(); // 10 -> 20 -> 30 -> null

Mengimplementasikan Pohon Pencarian Biner (BST)

Implementasikan Pohon Pencarian Biner dengan metode insert dan find.

Penjelasan: BST adalah struktur data pohon biner berbasis node yang memiliki properti berikut: Subpohon kiri sebuah node hanya berisi node dengan kunci yang lebih kecil dari kunci node. Subpohon kanan sebuah node hanya berisi node dengan kunci yang lebih besar dari kunci node. Kedua subpohon juga harus berupa pohon pencarian biner.

class Node { constructor(data) { this.data = data; this.left = null; this.right = null; } } class BST { constructor() { this.root = null; } insert(data) { const newNode = new Node(data); if (!this.root) { this.root = newNode; return; } this._insertNode(this.root, newNode); } _insertNode(node, newNode) { if (newNode.data < node.data) { if (!node.left) node.left = newNode; else this._insertNode(node.left, newNode); } else { if (!node.right) node.right = newNode; else this._insertNode(node.right, newNode); } } find(data) { return this._findNode(this.root, data); } _findNode(node, data) { if (!node) return null; if (data < node.data) return this._findNode(node.left, data); else if (data > node.data) return this._findNode(node.right, data); else return node; } } const bst = new BST(); bst.insert(10); bst.insert(5); bst.insert(15); bst.insert(7); console.log(bst.find(7)); // Node { data: 7, left: null, right: null } console.log(bst.find(12)); // null

Memutar Array

Tulis fungsi untuk memutar array ke kanan sebanyak k langkah.

Penjelasan: Memutar array berarti memindahkan elemen-elemennya. Untuk rotasi kanan, elemen terakhir menjadi yang pertama. Mengulang ini sebanyak k kali berfungsi tetapi tidak efisien. Cara yang lebih baik adalah menggunakan manipulasi array atau menghitung posisi baru.

function rotateArray(nums, k) { k = k % nums.length; // Tangani kasus di mana k > panjang if (k === 0) return nums; const partToMove = nums.splice(nums.length - k); nums.unshift(...partToMove); return nums; } console.log(rotateArray([1, 2, 3, 4, 5, 6, 7], 3)); // [5, 6, 7, 1, 2, 3, 4]

Mencari Irisan Dua Array

Diberikan dua array, tulis fungsi untuk menghitung irisannya (elemen yang umum untuk keduanya).

Penjelasan: Pendekatan yang baik adalah mengubah salah satu array menjadi Set untuk pencarian waktu rata-rata O(1). Kemudian, ulangi array kedua dan periksa apakah setiap elemen ada di set. Kumpulkan yang cocok.

function intersection(nums1, nums2) { const set1 = new Set(nums1); const resultSet = new Set(); for (const num of nums2) { if (set1.has(num)) { resultSet.add(num); } } return [...resultSet]; } console.log(intersection([1, 2, 2, 1], [2, 2])); // [2] console.log(intersection([4, 9, 5], [9, 4, 9, 8, 4])); // [9, 4]

Mengelompokkan Anagram

Diberikan sebuah array string, kelompokkan anagram menjadi satu.

Penjelasan: Kuncinya adalah menemukan tanda tangan unik untuk setiap kelompok anagram. Cara umum adalah mengurutkan setiap kata secara alfabetis. Kata-kata yang menghasilkan string terurut yang sama adalah anagram. Gunakan hash map di mana kunci adalah kata-kata yang diurutkan dan nilai adalah array kata-kata asli.

function groupAnagrams(strs) { const map = {}; for (const str of strs) { const sortedStr = str.split('').sort().join(''); if (!map[sortedStr]) { map[sortedStr] = []; } map[sortedStr].push(str); } return Object.values(map); } console.log(groupAnagrams(['eat', 'tea', 'tan', 'ate', 'nat', 'bat'])); // Output: [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]

Pindahkan Nol ke Akhir

Diberikan sebuah array nums, tulis fungsi untuk memindahkan semua angka 0 ke akhir sambil mempertahankan urutan relatif elemen non-nol.

Penjelasan: Gunakan pendekatan 'dua-penunjuk' atau 'bola salju'. Satu penunjuk melacak posisi di mana elemen non-nol berikutnya harus ditempatkan. Ulangi array; jika sebuah elemen non-nol, tempatkan pada posisi penunjuk dan tingkatkan penunjuk. Terakhir, isi sisanya dengan nol.

function moveZeroes(nums) { let nonZeroIndex = 0; // Pindahkan semua elemen non-nol ke depan for (let i = 0; i < nums.length; i++) { if (nums[i] !== 0) { nums[nonZeroIndex] = nums[i]; nonZeroIndex++; } } // Isi sisanya dengan nol for (let i = nonZeroIndex; i < nums.length; i++) { nums[i] = 0; } return nums; } console.log(moveZeroes([0, 1, 0, 3, 12])); // [1, 3, 12, 0, 0]

Sub-array Maksimum (Algoritma Kadane)

Diberikan sebuah array bilangan bulat nums, temukan sub-array yang berdekatan (mengandung setidaknya satu angka) yang memiliki jumlah terbesar dan kembalikan jumlahnya.

Penjelasan: Algoritma Kadane adalah cara yang efisien untuk menyelesaikan ini. Ulangi array, lacak jumlah currentMax yang berakhir pada posisi saat ini dan jumlah globalMax yang ditemukan sejauh ini. Jika currentMax menjadi negatif, reset ke 0 (atau lebih tepatnya, elemen saat ini).

function maxSubArray(nums) { let globalMax = -Infinity; let currentMax = 0; for (let i = 0; i < nums.length; i++) { currentMax = Math.max(nums[i], currentMax + nums[i]); if (currentMax > globalMax) { globalMax = currentMax; } } return globalMax; } console.log(maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4])); // 6 (dari [4, -1, 2, 1])

Permutasi String

Tulis fungsi untuk menghasilkan semua permutasi dari string yang diberikan.

Penjelasan: Ini biasanya diselesaikan menggunakan rekursi dan backtracking. Untuk setiap karakter, perbaiki dan hasilkan permutasi secara rekursif untuk sisa string. Kasus dasar: ketika string hanya memiliki satu karakter, kembalikan itu.

function stringPermutations(str) { if (str.length === 0) return ['']; if (str.length === 1) return [str]; const results = []; for (let i = 0; i < str.length; i++) { const char = str[i]; const remainingChars = str.slice(0, i) + str.slice(i + 1); const perms = stringPermutations(remainingChars); for (const perm of perms) { results.push(char + perm); } } return [...new Set(results)]; // Gunakan Set untuk menangani karakter duplikat jika diperlukan } console.log(stringPermutations('abc')); // ['abc', 'acb', 'bac', 'bca', 'cab', 'cba'] console.log(stringPermutations('aab')); // ['aab', 'aba', 'baa']

Pembagi Persekutuan Terbesar (GCD)

Tulis fungsi untuk menemukan Pembagi Persekutuan Terbesar (GCD) dari dua bilangan.

Penjelasan: Algoritma Euclidean adalah metode yang efisien. Jika b adalah 0, a adalah GCD. Jika tidak, GCD dari a dan b sama dengan GCD dari b dan a % b (sisa dari a dibagi b).

function gcd(a, b) { while (b !== 0) { let temp = b; b = a % b; a = temp; } return a; } console.log(gcd(48, 18)); // 6 console.log(gcd(101, 103)); // 1

Kelipatan Persekutuan Terkecil (LCM)

Tulis fungsi untuk menemukan Kelipatan Persekutuan Terkecil (LCM) dari dua bilangan.

Penjelasan: LCM dapat dihitung menggunakan GCD dengan rumus: LCM(a, b) = |a * b| / GCD(a, b).

function gcd(a, b) { // Pembantu dari masalah sebelumnya while (b !== 0) { let temp = b; b = a % b; a = temp; } return a; } function lcm(a, b) { if (a === 0 || b === 0) return 0; return Math.abs(a * b) / gcd(a, b); } console.log(lcm(15, 20)); // 60 console.log(lcm(7, 5)); // 35

Implementasikan Promise.all

Implementasikan fungsi yang berperilaku seperti Promise.all.

Penjelasan: Promise.all mengambil array promise dan mengembalikan promise tunggal yang akan diselesaikan ketika semua promise input telah diselesaikan, atau ditolak jika ada promise input yang ditolak. Nilai yang diselesaikan adalah array dari nilai-nilai yang diselesaikan.

function myPromiseAll(promises) { return new Promise((resolve, reject) => { const results = []; let completedCount = 0; const numPromises = promises.length; if (numPromises === 0) { resolve([]); return; } promises.forEach((promise, index) => { Promise.resolve(promise) .then(value => { results[index] = value; completedCount++; if (completedCount === numPromises) { resolve(results); } }) .catch(reject); // Tolak segera jika ada error }); }); } // Contoh penggunaan: const p1 = Promise.resolve(3); const p2 = 42; const p3 = new Promise((resolve) => setTimeout(resolve, 100, 'foo')); myPromiseAll([p1, p2, p3]).then(values => console.log(values)); // [3, 42, 'foo']

Membalik Pohon Biner

Tulis fungsi untuk membalik pohon biner.

Penjelasan: Untuk membalik pohon biner, Anda menukar anak kiri dan kanan untuk setiap node. Ini dapat dilakukan secara rekursif atau iteratif (menggunakan antrean atau tumpukan).

class Node { constructor(val, left = null, right = null) { this.val = val; this.left = left; this.right = right; } } function invertTree(root) { if (root === null) { return null; } // Tukar anak-anak [root.left, root.right] = [root.right, root.left]; // Rekursif untuk anak kiri dan kanan invertTree(root.left); invertTree(root.right); return root; } // Contoh: 4 -> [2, 7] -> [1, 3, 6, 9] // Menjadi: 4 -> [7, 2] -> [9, 6, 3, 1]

Kedalaman Maksimum Pohon Biner

Diberikan pohon biner, temukan kedalaman maksimumnya.

Penjelasan: Kedalaman maksimum adalah jumlah node di sepanjang jalur terpanjang dari node akar hingga node daun terjauh. Ini dapat ditemukan secara rekursif: MaxDepth = 1 + max(MaxDepth(kiri), MaxDepth(kanan)). Kasus dasar adalah ketika sebuah node adalah null, kedalamannya adalah 0.

class Node { constructor(val, left = null, right = null) { this.val = val; this.left = left; this.right = right; } } function maxDepth(root) { if (root === null) { return 0; } const leftDepth = maxDepth(root.left); const rightDepth = maxDepth(root.right); return Math.max(leftDepth, rightDepth) + 1; } // Contoh: 3 -> [9, 20] -> [null, null, 15, 7] const tree = new Node(3, new Node(9), new Node(20, new Node(15), new Node(7))); console.log(maxDepth(tree)); // 3

Waktu Terbaik untuk Membeli/Menjual Saham

Anda diberikan array prices di mana prices[i] adalah harga saham pada hari ke-i. Anda ingin memaksimalkan keuntungan Anda dengan memilih satu hari untuk membeli satu saham dan memilih hari lain di masa depan untuk menjual saham tersebut. Kembalikan keuntungan maksimum.

Penjelasan: Iterasi melalui harga, melacak harga minimum yang ditemui sejauh ini (minPrice) dan keuntungan maksimum yang ditemukan sejauh ini (maxProfit). Untuk setiap hari, hitung potensi keuntungan jika Anda menjual hari ini (harga saat ini - minPrice) dan perbarui maxProfit jika lebih tinggi.

function maxProfit(prices) { let minPrice = Infinity; let maxProfit = 0; for (let i = 0; i < prices.length; i++) { if (prices[i] < minPrice) { minPrice = prices[i]; } else if (prices[i] - minPrice > maxProfit) { maxProfit = prices[i] - minPrice; } } return maxProfit; } console.log(maxProfit([7, 1, 5, 3, 6, 4])); // 5 (Beli di 1, Jual di 6) console.log(maxProfit([7, 6, 4, 3, 1])); // 0 (Tidak ada keuntungan yang mungkin)

Angka Tunggal

Diberikan array bilangan bulat nums yang tidak kosong, setiap elemen muncul dua kali kecuali satu. Temukan satu-satunya itu.

Penjelasan: Cara yang sangat efisien untuk menyelesaikan ini adalah dengan menggunakan operator bitwise XOR (^). Meng-XOR-kan sebuah bilangan dengan dirinya sendiri menghasilkan 0. Meng-XOR-kan sebuah bilangan dengan 0 menghasilkan bilangan itu sendiri. Jika Anda meng-XOR-kan semua bilangan dalam array, pasangan-pasangan akan saling menghilangkan (menjadi 0), menyisakan hanya bilangan tunggal.

function singleNumber(nums) { let result = 0; for (const num of nums) { result ^= num; } return result; } console.log(singleNumber([2, 2, 1])); // 1 console.log(singleNumber([4, 1, 2, 1, 2])); // 4

Elemen Mayoritas

Diberikan array nums berukuran n, kembalikan elemen mayoritas. Elemen mayoritas adalah elemen yang muncul lebih dari ⌊n / 2⌋ kali.

Penjelasan: Algoritma Pemungutan Suara Boyer-Moore adalah cara yang efisien. Inisialisasi candidate dan count. Iterasi melalui array. Jika count adalah 0, setel elemen saat ini sebagai candidate. Jika elemen saat ini cocok dengan candidate, tingkatkan count; jika tidak, kurangi count.

function majorityElement(nums) { let candidate = null; let count = 0; for (const num of nums) { if (count === 0) { candidate = num; } count += (num === candidate) ? 1 : -1; } return candidate; } console.log(majorityElement([3, 2, 3])); // 3 console.log(majorityElement([2, 2, 1, 1, 1, 2, 2])); // 2

Mendaki Tangga

Anda sedang mendaki tangga. Dibutuhkan n langkah untuk mencapai puncak. Setiap kali Anda bisa mendaki 1 atau 2 langkah. Ada berapa cara berbeda Anda bisa mendaki ke puncak?

Penjelasan: Ini adalah masalah pemrograman dinamis klasik, sangat mirip dengan urutan Fibonacci. Jumlah cara untuk mencapai langkah n adalah jumlah cara untuk mencapai langkah n-1 (dengan mengambil 1 langkah) dan cara untuk mencapai langkah n-2 (dengan mengambil 2 langkah). dp[n] = dp[n-1] + dp[n-2].

function climbStairs(n) { if (n <= 2) return n; let oneStepBefore = 2; let twoStepsBefore = 1; let allWays = 0; for (let i = 3; i <= n; i++) { allWays = oneStepBefore + twoStepsBefore; twoStepsBefore = oneStepBefore; oneStepBefore = allWays; } return allWays; } console.log(climbStairs(2)); // 2 (1+1, 2) console.log(climbStairs(3)); // 3 (1+1+1, 1+2, 2+1) console.log(climbStairs(4)); // 5

Produk Array Kecuali Dirinya Sendiri

Diberikan array bilangan bulat nums, kembalikan array answer sedemikian rupa sehingga answer[i] sama dengan produk semua elemen nums kecuali nums[i]. Jangan gunakan operator pembagian.

Penjelasan: Anda dapat menyelesaikan ini dengan menghitung produk prefiks dan produk sufiks. Hasil pada indeks i adalah produk semua elemen sebelum i dikalikan dengan produk semua elemen setelah i.

function productExceptSelf(nums) { const n = nums.length; const answer = new Array(n).fill(1); // Hitung produk prefiks let prefix = 1; for (let i = 0; i < n; i++) { answer[i] = prefix; prefix *= nums[i]; } // Hitung produk sufiks dan kalikan dengan prefiks let suffix = 1; for (let i = n - 1; i >= 0; i--) { answer[i] *= suffix; suffix *= nums[i]; } return answer; } console.log(productExceptSelf([1, 2, 3, 4])); // [24, 12, 8, 6]

Jumlah Pulau

Diberikan peta grid 2D yang terdiri dari '1' (daratan) dan '0' (air), hitung jumlah pulau. Sebuah pulau dikelilingi oleh air dan dibentuk dengan menghubungkan daratan yang berdekatan secara horizontal atau vertikal.

Penjelasan: Iterasi melalui setiap sel grid. Jika Anda menemukan '1' (daratan), tingkatkan jumlah pulau dan mulai Depth First Search (DFS) atau Breadth First Search (BFS) dari sel itu. Selama pencarian, tandai semua '1' yang terhubung sebagai '0' (atau dikunjungi) untuk menghindari penghitungan ulang.

function numIslands(grid) { if (!grid || grid.length === 0) return 0; let count = 0; const rows = grid.length; const cols = grid[0].length; function dfs(r, c) { if (r < 0 || c < 0 || r >= rows || c >= cols || grid[r][c] === '0') { return; } grid[r][c] = '0'; // Tandai sebagai dikunjungi dfs(r + 1, c); dfs(r - 1, c); dfs(r, c + 1); dfs(r, c - 1); } for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { if (grid[r][c] === '1') { count++; dfs(r, c); } } } return count; } const grid1 = [ ['1', '1', '1', '1', '0'], ['1', '1', '0', '1', '0'], ['1', '1', '0', '0', '0'], ['0', '0', '0', '0', '0'] ]; // console.log(numIslands(grid1)); // 1 (Perlu dijalankan pada salinan atau pulihkan grid)

Cache LRU

Rancang dan implementasikan struktur data untuk Cache yang Paling Jarang Digunakan (LRU). Ini harus mendukung operasi get dan put.

Penjelasan: Cache LRU mengeluarkan item yang paling jarang digunakan ketika kapasitas tercapai. Implementasi umum menggunakan Map (atau hash map) untuk pencarian O(1) waktu rata-rata dan Doubly Linked List untuk pembaruan O(1) status 'baru-baru ini digunakan'. Map JavaScript mempertahankan urutan penyisipan, yang dapat menyederhanakan implementasi dasar.

class LRUCache { constructor(capacity) { this.capacity = capacity; this.cache = new Map(); } get(key) { if (!this.cache.has(key)) return -1; const value = this.cache.get(key); this.cache.delete(key); // Hapus untuk menyisipkan kembali di 'akhir' (paling baru) this.cache.set(key, value); return value; } put(key, value) { if (this.cache.has(key)) { this.cache.delete(key); } else if (this.cache.size >= this.capacity) { const oldestKey = this.cache.keys().next().value; this.cache.delete(oldestKey); } this.cache.set(key, value); } } const cache = new LRUCache(2); cache.put(1, 1); cache.put(2, 2); console.log(cache.get(1)); // 1 cache.put(3, 3); // Mengusir 2 console.log(cache.get(2)); // -1

Hasilkan Tanda Kurung

Diberikan n pasang tanda kurung, tulis fungsi untuk menghasilkan semua kombinasi tanda kurung yang terbentuk dengan baik.

Penjelasan: Ini adalah masalah backtracking klasik. Pertahankan hitungan untuk tanda kurung buka dan tutup. Anda dapat menambahkan tanda kurung buka jika open < n. Anda dapat menambahkan tanda kurung tutup jika close < open. Kasus dasar adalah ketika panjang string mencapai 2 * n.

function generateParenthesis(n) { const results = []; function backtrack(currentString, openCount, closeCount) { if (currentString.length === n * 2) { results.push(currentString); return; } if (openCount < n) { backtrack(currentString + '(', openCount + 1, closeCount); } if (closeCount < openCount) { backtrack(currentString + ')', openCount, closeCount + 1); } } backtrack('', 0, 0); return results; } console.log(generateParenthesis(3)); // ['((()))', '(()())', '(())()', '()(())', '()()()']

Wadah dengan Air Terbanyak

Diberikan n bilangan bulat non-negatif a1, a2, ..., an, di mana masing-masing mewakili titik pada koordinat (i, ai). n garis vertikal ditarik sedemikian rupa sehingga dua titik ujung garis i berada pada (i, ai) dan (i, 0). Temukan dua garis, yang, bersama dengan sumbu x, membentuk wadah, sehingga wadah tersebut berisi air terbanyak.

Penjelasan: Gunakan pendekatan dua-penunjuk. Mulai dengan satu penunjuk di awal dan satu di akhir. Hitung luas. Luas dibatasi oleh garis yang lebih pendek. Untuk berpotensi menemukan luas yang lebih besar, gerakkan penunjuk yang menunjuk ke garis yang lebih pendek ke dalam.

function maxArea(height) { let max = 0; let left = 0; let right = height.length - 1; while (left < right) { const h = Math.min(height[left], height[right]); const w = right - left; max = Math.max(max, h * w); if (height[left] < height[right]) { left++; } else { right--; } } return max; } console.log(maxArea([1, 8, 6, 2, 5, 4, 8, 3, 7])); // 49

3 Jumlah

Diberikan sebuah array nums dari n bilangan bulat, apakah ada elemen a, b, c di nums sedemikian rupa sehingga a + b + c = 0? Temukan semua triplet unik dalam array yang memberikan jumlah nol.

Penjelasan: Pertama, urutkan array. Kemudian, ulangi array dengan satu penunjuk i. Untuk setiap i, gunakan dua penunjuk lagi, left (mulai dari i+1) dan right (mulai dari akhir), untuk menemukan dua bilangan yang jumlahnya -nums[i]. Tangani duplikat untuk memastikan triplet unik.

function threeSum(nums) { nums.sort((a, b) => a - b); const results = []; for (let i = 0; i < nums.length - 2; i++) { if (i > 0 && nums[i] === nums[i - 1]) continue; // Lewati duplikat untuk i let left = i + 1; let right = nums.length - 1; let target = -nums[i]; while (left < right) { let sum = nums[left] + nums[right]; if (sum === target) { results.push([nums[i], nums[left], nums[right]]); while (left < right && nums[left] === nums[left + 1]) left++; // Lewati duplikat while (left < right && nums[right] === nums[right - 1]) right--; // Lewati duplikat left++; right--; } else if (sum < target) { left++; } else { right--; } } } return results; } console.log(threeSum([-1, 0, 1, 2, -1, -4])); // [[-1, -1, 2], [-1, 0, 1]]

Posisi Penyisipan Pencarian

Diberikan array bilangan bulat berbeda yang diurutkan dan nilai target, kembalikan indeks jika target ditemukan. Jika tidak, kembalikan indeks di mana ia akan berada jika disisipkan secara berurutan.

Penjelasan: Ini adalah variasi dari pencarian biner. Lakukan pencarian biner, tetapi jika elemen tidak ditemukan, penunjuk start akan berakhir pada posisi di mana elemen harus disisipkan.

function searchInsert(nums, target) { let start = 0; let end = nums.length - 1; while (start <= end) { let mid = Math.floor((start + end) / 2); if (nums[mid] === target) { return mid; } else if (nums[mid] < target) { start = mid + 1; } else { end = mid - 1; } } return start; // Jika tidak ditemukan, start adalah titik penyisipan } console.log(searchInsert([1, 3, 5, 6], 5)); // 2 console.log(searchInsert([1, 3, 5, 6], 2)); // 1 console.log(searchInsert([1, 3, 5, 6], 7)); // 4

Gabungkan Dua Daftar Terurut (Linked Lists)

Gabungkan dua daftar tertaut yang diurutkan dan kembalikan sebagai daftar terurut baru. Daftar baru harus dibuat dengan menggabungkan node dari dua daftar pertama.

Penjelasan: Gunakan node kepala dummy untuk menyederhanakan proses. Gunakan penunjuk untuk membangun daftar baru. Bandingkan node saat ini dari kedua daftar input dan tambahkan yang lebih kecil ke daftar baru, majukan penunjuk daftar tersebut. Ulangi sampai salah satu daftar habis, lalu tambahkan sisa daftar yang lain.

class ListNode { constructor(val = 0, next = null) { this.val = val; this.next = next; } } function mergeTwoLists(l1, l2) { let dummyHead = new ListNode(); let current = dummyHead; while (l1 !== null && l2 !== null) { if (l1.val < l2.val) { current.next = l1; l1 = l1.next; } else { current.next = l2; l2 = l2.next; } current = current.next; } current.next = l1 !== null ? l1 : l2; return dummyHead.next; } // Contoh: 1->2->4 dan 1->3->4 => 1->1->2->3->4->4

Pohon Simetris

Diberikan sebuah pohon biner, periksa apakah itu adalah cerminan dari dirinya sendiri (yaitu, simetris di sekitar pusatnya).

Penjelasan: Ini dapat diselesaikan secara rekursif. Sebuah pohon simetris jika sub-pohon kiri adalah bayangan cermin dari sub-pohon kanan. Buat fungsi pembantu isMirror(t1, t2) yang memeriksa apakah dua pohon adalah cermin. Fungsi ini harus memverifikasi: 1. t1.val === t2.val. 2. t1.left adalah cerminan dari t2.right. 3. t1.right adalah cerminan dari t2.left.

class Node { constructor(val, left = null, right = null) { this.val = val; this.left = left; this.right = right; } } function isSymmetric(root) { if (!root) return true; function isMirror(t1, t2) { if (!t1 && !t2) return true; if (!t1 || !t2 || t1.val !== t2.val) return false; return isMirror(t1.left, t2.right) && isMirror(t1.right, t2.left); } return isMirror(root.left, root.right); } // Contoh: 1 -> [2, 2] -> [3, 4, 4, 3] adalah simetris.

Penelusuran Urutan Tingkat (BST/Pohon)

Diberikan sebuah pohon biner, kembalikan penelusuran urutan tingkat dari nilai-nilai simpulnya. (yaitu, dari kiri ke kanan, tingkat demi tingkat).

Penjelasan: Ini adalah Breadth-First Search (BFS). Gunakan antrean (queue). Mulai dengan menambahkan akar ke antrean. Selama antrean tidak kosong, proses semua simpul pada tingkat saat ini. Untuk setiap simpul yang diproses, tambahkan anak-anaknya (jika ada) ke antrean untuk tingkat berikutnya.

class Node { constructor(val, left = null, right = null) { this.val = val; this.left = left; this.right = right; } } function levelOrder(root) { if (!root) return []; const result = []; const queue = [root]; while (queue.length > 0) { const levelSize = queue.length; const currentLevel = []; for (let i = 0; i < levelSize; i++) { const node = queue.shift(); currentLevel.push(node.val); if (node.left) queue.push(node.left); if (node.right) queue.push(node.right); } result.push(currentLevel); } return result; } // Contoh: 3 -> [9, 20] -> [null, null, 15, 7] // Keluaran: [[3], [9, 20], [15, 7]]

Konversi Array Terurut ke BST Seimbang Tinggi

Diberikan sebuah array di mana elemen-elemennya diurutkan dalam urutan menaik, konversikan ke pohon pencarian biner (BST) yang seimbang tinggi.

Penjelasan: Untuk membuat BST yang seimbang tinggi, Anda harus memilih elemen tengah dari array sebagai akar. Kemudian, bangun secara rekursif sub-pohon kiri dari paruh kiri array dan sub-pohon kanan dari paruh kanan.

class TreeNode { constructor(val = 0, left = null, right = null) { this.val = val; this.left = left; this.right = right; } } function sortedArrayToBST(nums) { if (!nums || nums.length === 0) return null; function buildBST(start, end) { if (start > end) return null; const mid = Math.floor((start + end) / 2); const node = new TreeNode(nums[mid]); node.left = buildBST(start, mid - 1); node.right = buildBST(mid + 1, end); return node; } return buildBST(0, nums.length - 1); } // Contoh: [-10, -3, 0, 5, 9] // Keluaran: Sebuah pohon seperti [0, -3, 9, -10, null, 5, null]

Implementasi `bind`

Implementasikan versi Anda sendiri dari Function.prototype.bind.

Penjelasan: bind membuat fungsi baru yang, ketika dipanggil, memiliki kata kunci this-nya diatur ke nilai yang diberikan, dengan urutan argumen yang diberikan mendahului argumen apa pun yang diberikan ketika fungsi baru dipanggil. Gunakan apply atau call di dalam fungsi yang dikembalikan, menangani aplikasi parsial (argumen yang sudah diatur sebelumnya).

Function.prototype.myBind = function(context, ...bindArgs) { const originalFunc = this; return function(...callArgs) { return originalFunc.apply(context, [...bindArgs, ...callArgs]); }; } const module = { x: 42, getX: function() { return this.x; } }; const unboundGetX = module.getX; console.log(unboundGetX()); // undefined (this adalah global/window) const boundGetX = unboundGetX.myBind(module); console.log(boundGetX()); // 42

Temukan Elemen Terbesar ke-K

Temukan elemen terbesar ke-k dalam array yang tidak terurut. Perhatikan bahwa itu adalah elemen terbesar ke-k dalam urutan terurut, bukan elemen berbeda ke-k.

Penjelasan: Pendekatan sederhana adalah mengurutkan array dan kemudian memilih elemen pada indeks n - k. Pendekatan yang lebih efisien untuk array besar melibatkan penggunaan min-heap atau algoritma pemilihan seperti Quickselect.

function findKthLargest(nums, k) { // Pendekatan sederhana: Urutkan nums.sort((a, b) => b - a); // Urutkan dalam urutan menurun return nums[k - 1]; // Catatan: Quickselect akan lebih efisien dalam wawancara // tetapi pengurutan lebih mudah diimplementasikan dengan cepat. } console.log(findKthLargest([3, 2, 1, 5, 6, 4], 2)); // 5 console.log(findKthLargest([3, 2, 3, 1, 2, 4, 5, 5, 6], 4)); // 4

Periksa apakah Objek Memiliki Properti

Bagaimana Anda dapat memeriksa apakah sebuah objek memiliki properti tertentu?

Penjelasan: Anda dapat menggunakan operator in (memeriksa properti sendiri dan yang diwarisi), Object.prototype.hasOwnProperty.call(obj, prop) (hanya memeriksa properti sendiri), atau cukup obj.prop !== undefined (bisa rumit dengan nilai undefined).

const person = { name: 'Eve', age: 28 }; function hasProp(obj, prop) { console.log(`Menggunakan 'in': ${prop in obj}`); console.log(`Menggunakan 'hasOwnProperty': ${Object.prototype.hasOwnProperty.call(obj, prop)}`); } hasProp(person, 'name'); // true, true hasProp(person, 'toString'); // true, false (toString diwarisi)

Integer ke Romawi

Tulis fungsi untuk mengkonversi bilangan bulat ke representasi angka Romawinya.

Penjelasan: Buat pemetaan angka Romawi dan nilai-nilai yang sesuai, diurutkan dari yang terbesar hingga terkecil. Iterasi melalui peta ini. Untuk setiap nilai, kurangkan dari angka masukan sebanyak mungkin, tambahkan angka Romawi setiap kali.

function intToRoman(num) { const map = { M: 1000, CM: 900, D: 500, CD: 400, C: 100, XC: 90, L: 50, XL: 40, X: 10, IX: 9, V: 5, IV: 4, I: 1 }; let result = ''; for (let key in map) { while (num >= map[key]) { result += key; num -= map[key]; } } return result; } console.log(intToRoman(3)); // III console.log(intToRoman(58)); // LVIII console.log(intToRoman(1994)); // MCMXCIV

Romawi ke Integer

Tulis fungsi untuk mengkonversi angka Romawi ke bilangan bulat.

Penjelasan: Buat peta simbol Romawi ke nilai. Iterasi melalui string. Jika nilai simbol saat ini kurang dari nilai simbol berikutnya (seperti 'IV' atau 'IX'), kurangkan nilai saat ini; jika tidak, tambahkan.

function romanToInt(s) { const map = { I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000 }; let result = 0; for (let i = 0; i < s.length; i++) { const currentVal = map[s[i]]; const nextVal = map[s[i + 1]]; if (nextVal > currentVal) { result -= currentVal; } else { result += currentVal; } } return result; } console.log(romanToInt('III')); // 3 console.log(romanToInt('LVIII')); // 58 console.log(romanToInt('MCMXCIV')); // 1994

Implementasi `Set`

Implementasikan struktur data Set dasar (tanpa menggunakan Set bawaan) dengan metode add, has, delete, dan size.

Penjelasan: Anda dapat menggunakan objek JavaScript (hash map) sebagai penyimpanan dasar. Kunci akan menjadi elemen set (Anda mungkin perlu menangani cara menyimpan berbagai jenis dan memastikan keunikan sebagai kunci).

class MySet { constructor() { this.items = {}; this._size = 0; } add(element) { if (!this.has(element)) { this.items[element] = element; // Simpan elemen this._size++; return true; } return false; } delete(element) { if (this.has(element)) { delete this.items[element]; this._size--; return true; } return false; } has(element) { return Object.prototype.hasOwnProperty.call(this.items, element); } get size() { return this._size; } } const set = new MySet(); set.add(1); set.add(2); console.log(set.has(1)); // true console.log(set.size); // 2 set.delete(1); console.log(set.has(1)); // false

Segitiga Pascal

Diberikan bilangan bulat numRows, hasilkan numRows pertama dari segitiga Pascal.

Penjelasan: Dalam segitiga Pascal, setiap angka adalah jumlah dari dua angka tepat di atasnya. Mulai dengan [[1]]. Untuk setiap baris berikutnya, mulai dan akhiri dengan 1. Setiap elemen tengah adalah jumlah dari elemen pada indeks yang sama dan indeks sebelumnya dari baris di atasnya.

function generatePascalsTriangle(numRows) { if (numRows === 0) return []; const triangle = [[1]]; for (let i = 1; i < numRows; i++) { const prevRow = triangle[i - 1]; const newRow = [1]; for (let j = 1; j < i; j++) { newRow.push(prevRow[j - 1] + prevRow[j]); } newRow.push(1); triangle.push(newRow); } return triangle; } console.log(generatePascalsTriangle(5)); // [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]]

Pencarian Kata

Diberikan papan 2D dan sebuah kata, temukan apakah kata tersebut ada di dalam kisi. Kata dapat dibangun dari huruf-huruf sel yang berdekatan secara berurutan, di mana sel 'berdekatan' adalah tetangga secara horizontal atau vertikal.

Penjelasan: Ini membutuhkan Depth First Search (DFS) dengan backtracking. Iterasi melalui setiap sel. Jika sebuah sel cocok dengan huruf pertama dari kata, mulailah DFS. Fungsi DFS memeriksa tetangga, memastikan mereka cocok dengan huruf berikutnya dan belum dikunjungi di jalur saat ini. Jika jalur gagal, lakukan backtracking dengan menghapus tanda sel.

function exist(board, word) { const rows = board.length; const cols = board[0].length; function dfs(r, c, index) { if (index === word.length) return true; // Kata ditemukan if (r < 0 || c < 0 || r >= rows || c >= cols || board[r][c] !== word[index]) { return false; } const temp = board[r][c]; board[r][c] = '#'; // Tandai sebagai sudah dikunjungi const found = dfs(r + 1, c, index + 1) || dfs(r - 1, c, index + 1) || dfs(r, c + 1, index + 1) || dfs(r, c - 1, index + 1); board[r][c] = temp; // Backtrack return found; } for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { if (board[r][c] === word[0] && dfs(r, c, 0)) { return true; } } } return false; } const board = [['A','B','C','E'],['S','F','C','S'],['A','D','E','E']]; console.log(exist(board, 'ABCCED')); // true console.log(exist(board, 'SEE')); // true console.log(exist(board, 'ABCB')); // false

Substring Jendela Minimum

Diberikan dua string s dan t, temukan jendela minimum di s yang akan berisi semua karakter di t. Jika tidak ada jendela seperti itu di s yang mencakup semua karakter di t, kembalikan string kosong "".

Penjelasan: Gunakan pendekatan jendela geser dengan dua penunjuk (left dan right) dan peta hash. Satu peta menyimpan jumlah karakter yang dibutuhkan dari t. Peta lain menyimpan jumlah di jendela saat ini. Perluas jendela dengan right. Setelah jendela berisi semua karakter yang dibutuhkan, coba perkecil dengan left untuk menemukan jendela minimum yang mungkin.

function minWindow(s, t) { if (!t || !s || s.length < t.length) return ""; const tMap = {}; for (const char of t) tMap[char] = (tMap[char] || 0) + 1; let required = Object.keys(tMap).length; let formed = 0; const windowMap = {}; let left = 0; let minLen = Infinity; let result = ""; for (let right = 0; right < s.length; right++) { const char = s[right]; windowMap[char] = (windowMap[char] || 0) + 1; if (tMap[char] && windowMap[char] === tMap[char]) { formed++; } while (left <= right && formed === required) { if (right - left + 1 < minLen) { minLen = right - left + 1; result = s.substring(left, right + 1); } const leftChar = s[left]; windowMap[leftChar]--; if (tMap[leftChar] && windowMap[leftChar] < tMap[leftChar]) { formed--; } left++; } } return result; } console.log(minWindow('ADOBECODEBANC', 'ABC')); // 'BANC'

Membalik Daftar Berantai

Diberikan head dari daftar berantai tunggal, balikkan daftar, dan kembalikan daftar yang dibalik.

Penjelasan: Anda perlu mengulang daftar, mengubah penunjuk next dari setiap simpul untuk menunjuk ke simpul sebelumnya. Lacak simpul previous, current, dan next selama iterasi.

class ListNode { constructor(val = 0, next = null) { this.val = val; this.next = next; } } function reverseList(head) { let prev = null; let current = head; let next = null; while (current !== null) { next = current.next; // Simpan simpul berikutnya current.next = prev; // Balikkan penunjuk simpul saat ini prev = current; // Pindahkan prev satu langkah ke depan current = next; // Pindahkan current satu langkah ke depan } return prev; // Kepala baru adalah 'prev' terakhir } // Contoh: 1->2->3->4->5 menjadi 5->4->3->2->1

Deteksi Siklus dalam Daftar Berantai

Diberikan head, kepala dari daftar berantai, tentukan apakah daftar berantai memiliki siklus di dalamnya.

Penjelasan: Gunakan algoritma 'Floyd's Tortoise and Hare'. Miliki dua penunjuk, satu bergerak satu langkah pada satu waktu (slow) dan satu bergerak dua langkah pada satu waktu (fast). Jika ada siklus, penunjuk fast pada akhirnya akan menyusul penunjuk slow.

class ListNode { constructor(val = 0, next = null) { this.val = val; this.next = next; } } function hasCycle(head) { if (!head || !head.next) return false; let slow = head; let fast = head.next; while (slow !== fast) { if (!fast || !fast.next) return false; // Mencapai akhir, tidak ada siklus slow = slow.next; fast = fast.next.next; } return true; // Penunjuk bertemu, siklus ada } // Contoh: 3->2->0->-4 dengan -4 menunjuk kembali ke 2. hasCycle mengembalikan true.

Implementasi `Object.create`

Implementasikan fungsi yang meniru perilaku Object.create(proto).

Penjelasan: Object.create membuat objek baru, menggunakan objek yang ada sebagai prototipe dari objek yang baru dibuat. Anda dapat mencapai ini dengan membuat fungsi konstruktor sementara, mengatur prototipenya ke proto masukan, dan kemudian mengembalikan instans baru darinya.

function myObjectCreate(proto) { if (typeof proto !== 'object' && typeof proto !== 'function') { if (proto !== null) { throw new TypeError('Prototipe objek hanya boleh berupa Objek atau null: ' + proto); } } function F() {} F.prototype = proto; return new F(); } const person = { isHuman: false, printIntroduction: function() { console.log(`Nama saya ${this.name}. Apakah saya manusia? ${this.isHuman}`); } }; const me = myObjectCreate(person); me.name = 'Matthew'; me.isHuman = true; me.printIntroduction(); // Nama saya Matthew. Apakah saya manusia? true console.log(Object.getPrototypeOf(me) === person); // true

Apa itu Hoisting?

Jelaskan hoisting dalam JavaScript dan berikan contoh.

Penjelasan: Hoisting adalah perilaku default JavaScript untuk memindahkan deklarasi ke bagian atas cakupan saat ini (global atau fungsi) sebelum eksekusi kode. Deklarasi variabel (var) di-hoist dan diinisialisasi dengan undefined. Deklarasi fungsi di-hoist sepenuhnya (baik nama maupun isinya). let dan const di-hoist tetapi tidak diinisialisasi, menyebabkan Temporal Dead Zone.

console.log(myVar); // undefined (var di-hoist dan diinisialisasi dengan undefined) // console.log(myLet); // ReferenceError: Tidak dapat mengakses 'myLet' sebelum inisialisasi (TDZ) myFunc(); // 'Halo!' (Deklarasi fungsi di-hoist sepenuhnya) var myVar = 'Saya adalah var'; let myLet = 'Saya adalah let'; function myFunc() { console.log('Halo!'); }

Jelaskan Event Bubbling dan Capturing

Jelaskan Event Bubbling dan Capturing dalam konteks DOM.

Penjelasan: Ini adalah dua fase propagasi event di HTML DOM. Fase Capturing: Event bergerak dari window ke elemen target. Fase Bubbling: Event bergerak naik dari elemen target kembali ke window. Secara default, sebagian besar penangan event terdaftar selama fase bubbling. Anda dapat menggunakan addEventListener(type, listener, useCapture) dengan useCapture = true untuk menangani event selama fase capturing.

// Dalam struktur HTML: <div><p><span>Klik Saya</span></p></div> // Jika Anda mengklik <span>: // Capturing: window -> document -> html -> body -> div -> p -> span // Bubbling: span -> p -> div -> body -> html -> document -> window /* Contoh JS div.addEventListener('click', () => console.log('Div diklik'), true); // Capturing p.addEventListener('click', () => console.log('P diklik'), false); // Bubbling span.addEventListener('click', () => console.log('Span diklik'), false); // Bubbling // Keluaran akan menjadi: Div diklik, Span diklik, P diklik */

Implementasi `JSON.parse` secara manual (dasar)

Coba implementasikan versi yang sangat dasar dari JSON.parse (tangani objek sederhana, array, string, angka).

Penjelasan: Ini adalah tugas yang sangat kompleks secara penuh, tetapi untuk pengaturan pengkodean langsung, Anda mungkin diminta untuk menangani subset yang sangat disederhanakan. Anda perlu mengurai string, mengidentifikasi batas objek {} dan array [], pasangan kunci-nilai ("key": value), dan tipe data dasar. eval atau new Function dapat menipu ini tetapi berbahaya. Parser yang sebenarnya akan menggunakan lexer/tokenizer dan parser.

function basicJsonParse(jsonString) { // PERINGATAN: Menggunakan new Function tidak aman seperti eval. // Ini adalah contoh yang disederhanakan, TIDAK AMAN untuk demonstrasi saja. // Implementasi nyata membutuhkan parser yang tepat. try { return (new Function('return ' + jsonString))(); } catch (e) { throw new SyntaxError('JSON tidak valid: ' + e.message); } } console.log(basicJsonParse('{"a": 1, "b": ["x", "y"]}')); // { a: 1, b: ['x', 'y'] }

Ratakan Array Berlapis Dalam

Tulis fungsi untuk meratakan array yang berlapis dalam.

Penjelasan: Berbeda dengan perataan sebelumnya (satu tingkat), ini perlu menangani tingkat nesting apa pun. Rekursi adalah kecocokan alami. Iterasi melalui array. Jika sebuah elemen adalah array, panggil flatten secara rekursif padanya dan gabungkan hasilnya. Jika tidak, tambahkan elemen.

function deepFlatten(arr) { let flattened = []; arr.forEach(item => { if (Array.isArray(item)) { flattened = flattened.concat(deepFlatten(item)); } else { flattened.push(item); } }); return flattened; // ES2019+ menyediakan cara yang jauh lebih sederhana: // return arr.flat(Infinity); } console.log(deepFlatten([1, [2, [3, 4], 5], 6])); // [1, 2, 3, 4, 5, 6]

Implementasi Trie (Pohon Prefiks)

Implementasikan struktur data Trie dengan metode insert, search, dan startsWith.

Penjelasan: Trie adalah struktur data seperti pohon yang digunakan untuk menyimpan dan mengambil kunci secara efisien dalam kumpulan data string. Setiap simpul mewakili karakter, dan jalur dari akar mewakili kata atau prefiks.

class TrieNode { constructor() { this.children = {}; this.isEndOfWord = false; } } class Trie { constructor() { this.root = new TrieNode(); } insert(word) { let node = this.root; for (const char of word) { if (!node.children[char]) { node.children[char] = new TrieNode(); } node = node.children[char]; } node.isEndOfWord = true; } search(word) { let node = this.root; for (const char of word) { if (!node.children[char]) return false; node = node.children[char]; } return node.isEndOfWord; } startsWith(prefix) { let node = this.root; for (const char of prefix) { if (!node.children[char]) return false; node = node.children[char]; } return true; } } const trie = new Trie(); trie.insert('apple'); console.log(trie.search('apple')); // true console.log(trie.search('app')); // false console.log(trie.startsWith('app')); // true

Mengacak Array (Fisher-Yates)

Tulis fungsi untuk mengacak array di tempat menggunakan algoritma Fisher-Yates (atau Knuth).

Penjelasan: Algoritma Fisher-Yates mengulang array dari elemen terakhir hingga pertama. Dalam setiap iterasi, ia menukar elemen saat ini dengan elemen yang dipilih secara acak dari awal array hingga elemen saat ini (inklusif).

function shuffleArray(arr) { for (let i = arr.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [arr[i], arr[j]] = [arr[j], arr[i]]; // Tukar elemen } return arr; } console.log(shuffleArray([1, 2, 3, 4, 5])); // mis., [3, 5, 1, 2, 4]

Menggabungkan Fungsi

Implementasikan fungsi compose yang mengambil beberapa fungsi dan mengembalikan fungsi baru yang menerapkannya dari kanan ke kiri.

Penjelasan: Komposisi fungsi (f ∘ g)(x) = f(g(x)) menerapkan satu fungsi ke hasil fungsi lainnya. compose(f, g, h) umum akan berarti f(g(h(x))). Gunakan reduceRight untuk implementasi yang elegan.

function compose(...funcs) { return function(initialArg) { return funcs.reduceRight((acc, func) => func(acc), initialArg); }; } const add5 = x => x + 5; const multiply3 = x => x * 3; const subtract2 = x => x - 2; const composedFunc = compose(subtract2, multiply3, add5); console.log(composedFunc(10)); // (10 + 5) * 3 - 2 = 15 * 3 - 2 = 45 - 2 = 43

Pipa Fungsi

Implementasikan fungsi pipe yang mengambil beberapa fungsi dan mengembalikan fungsi baru yang menerapkannya dari kiri ke kanan.

Penjelasan: Mirip dengan compose, tetapi urutan aplikasi dibalik: pipe(f, g, h) berarti h(g(f(x))). Gunakan reduce untuk implementasi.

function pipe(...funcs) { return function(initialArg) { return funcs.reduce((acc, func) => func(acc), initialArg); }; } const add5 = x => x + 5; const multiply3 = x => x * 3; const subtract2 = x => x - 2; const pipedFunc = pipe(add5, multiply3, subtract2); console.log(pipedFunc(10)); // (10 + 5) * 3 - 2 = 15 * 3 - 2 = 45 - 2 = 43

Masalah Kembalian Koin

Diberikan array denominasi koin dan jumlah, temukan jumlah minimum koin untuk membuat jumlah tersebut. Asumsikan pasokan tak terbatas dari setiap koin.

Penjelasan: Ini adalah masalah pemrograman dinamis klasik. Buat array dp di mana dp[i] menyimpan koin minimum yang dibutuhkan untuk jumlah i. dp[i] = min(dp[i - coin]) + 1 untuk semua koin.

function coinChange(coins, amount) { const dp = new Array(amount + 1).fill(Infinity); dp[0] = 0; for (let i = 1; i <= amount; i++) { for (const coin of coins) { if (i - coin >= 0) { dp[i] = Math.min(dp[i], dp[i - coin] + 1); } } } return dp[amount] === Infinity ? -1 : dp[amount]; } console.log(coinChange([1, 2, 5], 11)); // 3 (5 + 5 + 1) console.log(coinChange([2], 3)); // -1

Nenek Moyang Terendah (BST)

Diberikan pohon pencarian biner (BST), temukan nenek moyang terendah (LCA) dari dua simpul yang diberikan dalam BST.

Penjelasan: LCA adalah simpul terdalam yang memiliki kedua simpul yang diberikan sebagai keturunan. Dalam BST, Anda dapat menemukannya dengan melintasi dari akar. Jika kedua simpul lebih kecil dari simpul saat ini, pergi ke kiri. Jika keduanya lebih besar, pergi ke kanan. Jika satu lebih kecil dan satu lebih besar (atau salah satunya adalah simpul saat ini), maka simpul saat ini adalah LCA.

class Node { constructor(val) { this.val = val; this.left = this.right = null; } } function lowestCommonAncestor(root, p, q) { let current = root; while (current) { if (p.val > current.val && q.val > current.val) { current = current.right; } else if (p.val < current.val && q.val < current.val) { current = current.left; } else { return current; // LCA ditemukan } } return null; // Seharusnya tidak terjadi dalam BST yang valid dengan p dan q } // Contoh: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 => LCA adalah 6

Serialisasi dan Deserialisasi Pohon Biner

Rancang algoritma untuk serialisasi dan deserialisasi pohon biner.

Penjelasan: Serialisasi mengonversi pohon menjadi string atau array. Deserialisasi merekonstruksi pohon. Cara umum adalah Penelusuran Pre-order (DFS). Gunakan penanda khusus (misalnya, '#') untuk simpul null untuk mempertahankan struktur.

class Node { constructor(val) { this.val = val; this.left = this.right = null; } } function serialize(root) { const values = []; function dfs(node) { if (!node) { values.push('#'); return; } values.push(node.val); dfs(node.left); dfs(node.right); } dfs(root); return values.join(','); } function deserialize(data) { const values = data.split(','); let index = 0; function buildTree() { if (values[index] === '#') { index++; return null; } const node = new Node(parseInt(values[index])); index++; node.left = buildTree(); node.right = buildTree(); return node; } return buildTree(); } // Contoh: 1 -> [2, 3] -> [null, null, 4, 5] // Diserialisasi: '1,2,#,#,3,4,#,#,5,#,#'

Implementasi `setInterval` dengan `setTimeout`

Implementasikan fungsi mySetInterval yang meniru setInterval tetapi menggunakan setTimeout secara rekursif.

Penjelasan: setInterval menjalankan fungsi berulang kali setiap N milidetik. Anda dapat mencapai ini dengan memiliki fungsi yang memanggil dirinya sendiri dengan setTimeout setelah setiap eksekusi. Anda juga memerlukan cara untuk menghapusnya (myClearInterval).

function mySetInterval(callback, delay, ...args) { const interval = { timerId: null }; function run() { interval.timerId = setTimeout(() => { callback.apply(this, args); run(); // Jadwalkan panggilan berikutnya }, delay); } run(); return interval; // Kembalikan objek untuk memungkinkan penghapusan } function myClearInterval(interval) { clearTimeout(interval.timerId); } // Contoh penggunaan: let count = 0; const intervalId = mySetInterval(() => { console.log(`Halo: ${++count}`); if (count === 3) myClearInterval(intervalId); }, 500);

Penelusuran Graf (BFS & DFS)

Implementasikan Breadth-First Search (BFS) dan Depth-First Search (DFS) untuk graf yang diberikan (representasi daftar kedekatan).

Penjelasan: BFS menjelajahi tetangga terlebih dahulu (menggunakan Antrean), sementara DFS menjelajahi sejauh mungkin di sepanjang setiap cabang (menggunakan Stack atau rekursi). Lacak simpul yang dikunjungi untuk menghindari siklus dan pekerjaan yang berlebihan.

const graph = { A: ['B', 'C'], B: ['D'], C: ['E'], D: [], E: ['F'], F: [] }; function bfs(graph, startNode) { const queue = [startNode]; const visited = new Set(); const result = []; visited.add(startNode); while (queue.length > 0) { const node = queue.shift(); result.push(node); for (const neighbor of graph[node]) { if (!visited.has(neighbor)) { visited.add(neighbor); queue.push(neighbor); } } } return result; } function dfs(graph, startNode) { const stack = [startNode]; const visited = new Set(); const result = []; while (stack.length > 0) { const node = stack.pop(); if (!visited.has(node)) { visited.add(node); result.push(node); // Tambahkan tetangga secara terbalik untuk memprosesnya sesuai urutan nanti (opsional) for (let i = graph[node].length - 1; i >= 0; i--) { stack.push(graph[node][i]); } } } return result; } console.log('BFS:', bfs(graph, 'A')); // ['A', 'B', 'C', 'D', 'E', 'F'] console.log('DFS:', dfs(graph, 'A')); // ['A', 'B', 'D', 'C', 'E', 'F']

Rotasi Gambar (Matriks)

Anda diberikan matriks 2D n x n yang mewakili sebuah gambar. Putar gambar 90 derajat (searah jarum jam) di tempat.

Penjelasan: Cara umum untuk mencapai ini adalah dengan terlebih dahulu transpose matriks (menukar matrix[i][j] dengan matrix[j][i]) dan kemudian membalik setiap baris.

function rotate(matrix) { const n = matrix.length; // Transpose for (let i = 0; i < n; i++) { for (let j = i; j < n; j++) { [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]]; } } // Balikkan setiap baris for (let i = 0; i < n; i++) { matrix[i].reverse(); } return matrix; } const matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; console.log(rotate(matrix)); // [[7, 4, 1], [8, 5, 2], [9, 6, 3]]

Penelusuran Matriks Spiral

Diberikan matriks m x n, kembalikan semua elemen matriks dalam urutan spiral.

Penjelasan: Gunakan empat penunjuk untuk menentukan batas: top, bottom, left, right. Lintasi baris atas, lalu kolom kanan, lalu baris bawah, lalu kolom kiri, perkecil batas setelah setiap lintasan, hingga left melewati right atau top melewati bottom.

function spiralOrder(matrix) { if (!matrix || matrix.length === 0) return []; const rows = matrix.length, cols = matrix[0].length; const result = []; let top = 0, bottom = rows - 1, left = 0, right = cols - 1; while (top <= bottom && left <= right) { for (let i = left; i <= right; i++) result.push(matrix[top][i]); top++; for (let i = top; i <= bottom; i++) result.push(matrix[i][right]); right--; if (top <= bottom) { for (let i = right; i >= left; i--) result.push(matrix[bottom][i]); bottom--; } if (left <= right) { for (let i = bottom; i >= top; i--) result.push(matrix[i][left]); left++; } } return result; } const matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; console.log(spiralOrder(matrix)); // [1, 2, 3, 6, 9, 8, 7, 4, 5]

Atur Matriks Menjadi Nol

Diberikan matriks m x n, jika sebuah elemen adalah 0, atur seluruh baris dan kolomnya menjadi 0. Lakukan secara in-place.

Penjelasan: Pendekatan naif membutuhkan ruang ekstra O(m*n). Untuk melakukannya dalam ruang O(1) (atau O(m+n)), Anda dapat menggunakan baris pertama dan kolom pertama untuk menyimpan informasi tentang baris/kolom mana yang perlu dinolkan. Anda memerlukan flag terpisah untuk apakah baris/kolom pertama itu sendiri perlu dinolkan.

function setZeroes(matrix) { const rows = matrix.length, cols = matrix[0].length; let firstColZero = false; for (let r = 0; r < rows; r++) { if (matrix[r][0] === 0) firstColZero = true; for (let c = 1; c < cols; c++) { if (matrix[r][c] === 0) { matrix[r][0] = 0; matrix[0][c] = 0; } } } for (let r = rows - 1; r >= 0; r--) { for (let c = cols - 1; c >= 1; c--) { if (matrix[r][0] === 0 || matrix[0][c] === 0) { matrix[r][c] = 0; } } if (firstColZero) matrix[r][0] = 0; } return matrix; } // Contoh: [[1,1,1],[1,0,1],[1,1,1]] => [[1,0,1],[0,0,0],[1,0,1]]

Implementasi `Promise.race`

Implementasikan fungsi yang berperilaku seperti Promise.race.

Penjelasan: Promise.race mengambil array promise dan mengembalikan satu promise. Promise yang dikembalikan ini akan diselesaikan (resolve atau reject) segera setelah salah satu dari promise masukan diselesaikan, dengan nilai atau alasan dari promise tersebut.

function myPromiseRace(promises) { return new Promise((resolve, reject) => { if (!promises || promises.length === 0) { return; // Atau resolve/reject tergantung spesifikasi } promises.forEach(promise => { Promise.resolve(promise).then(resolve, reject); }); }); } const p1 = new Promise((resolve) => setTimeout(resolve, 500, 'one')); const p2 = new Promise((resolve) => setTimeout(resolve, 100, 'two')); myPromiseRace([p1, p2]).then(value => console.log(value)); // 'two'

Pola Singleton

Implementasikan pola desain Singleton dalam JavaScript.

Penjelasan: Pola Singleton memastikan bahwa sebuah kelas hanya memiliki satu instans dan menyediakan titik akses global ke dalamnya. Ini dapat dicapai menggunakan penutupan (closure) untuk menampung instans.

const Singleton = (function() { let instance; function createInstance() { // Metode dan variabel privat const privateVar = 'Saya privat'; function privateMethod() { console.log('Privat'); } return { // Metode dan variabel publik publicVar: 'Saya publik', publicMethod: function() { console.log('Publik'); }, getPrivate: function() { return privateVar; } }; } return { getInstance: function() { if (!instance) { instance = createInstance(); } return instance; } }; })(); const instance1 = Singleton.getInstance(); const instance2 = Singleton.getInstance(); console.log(instance1 === instance2); // true console.log(instance1.getPrivate()); // 'Saya privat'

Validasi Alamat IP

Tulis fungsi untuk memeriksa apakah string yang diberikan adalah alamat IPv4 atau IPv6 yang valid.

Penjelasan: Untuk IPv4, periksa 4 angka (0-255) yang dipisahkan oleh titik, tanpa nol di depan (kecuali untuk '0' itu sendiri). Untuk IPv6, periksa 8 grup dari 1-4 digit heksadesimal yang dipisahkan oleh titik dua (tangani variasi seperti '::'). Regex dapat digunakan, tetapi parsing manual seringkali lebih jelas untuk wawancara.

function validIPAddress(IP) { function isIPv4(s) { const parts = s.split('.'); if (parts.length !== 4) return false; for (const part of parts) { if (!/^[0-9]+$/.test(part)) return false; if (part.length > 1 && part.startsWith('0')) return false; const num = parseInt(part); if (num < 0 || num > 255) return false; } return true; } function isIPv6(s) { const parts = s.split(':'); if (parts.length !== 8) return false; // Disederhanakan: Tanpa '::' for (const part of parts) { if (part.length < 1 || part.length > 4) return false; if (!/^[0-9a-fA-F]+$/.test(part)) return false; } return true; } if (IP.includes('.')) return isIPv4(IP) ? 'IPv4' : 'Neither'; if (IP.includes(':')) return isIPv6(IP) ? 'IPv6' : 'Neither'; return 'Neither'; } console.log(validIPAddress('172.16.254.1')); // IPv4 console.log(validIPAddress('2001:0db8:85a3:0000:0000:8a2e:0370:7334')); // IPv6 (Disederhanakan) console.log(validIPAddress('256.256.256.256')); // Bukan Keduanya

Temukan Elemen Puncak

Elemen puncak adalah elemen yang benar-benar lebih besar dari tetangganya. Diberikan array input nums, temukan elemen puncak dan kembalikan indeksnya.

Penjelasan: Karena puncak mana pun akan berhasil, dan nums[-1] dan nums[n] dianggap -Infinity, Anda dapat menggunakan pencarian biner yang dimodifikasi. Jika nums[mid] kurang dari nums[mid+1], puncak harus ada di sebelah kanan. Jika tidak, puncak harus ada di sebelah kiri (atau mid itu sendiri adalah puncak).

function findPeakElement(nums) { let left = 0; let right = nums.length - 1; while (left < right) { let mid = Math.floor((left + right) / 2); if (nums[mid] < nums[mid + 1]) { left = mid + 1; // Puncak ada di kanan } else { right = mid; // Puncak adalah mid atau di kiri } } return left; // 'left' akan menjadi indeks puncak } console.log(findPeakElement([1, 2, 3, 1])); // 2 (indeks 3) console.log(findPeakElement([1, 2, 1, 3, 5, 6, 4])); // 5 (indeks 6) atau 1 (indeks 2)

Menghitung Bit

Diberikan sebuah bilangan bulat n, kembalikan sebuah array ans dengan panjang n + 1 sedemikian rupa sehingga untuk setiap i (0 <= i <= n), ans[i] adalah jumlah 1 dalam representasi biner dari i.

Penjelasan: Anda dapat menyelesaikan ini menggunakan pemrograman dinamis. Perhatikan hubungan: dp[i] = dp[i >> 1] + (i & 1). Jumlah 1 dalam i adalah jumlah 1 dalam i yang digeser ke kanan (yaitu, i/2), ditambah 1 jika i ganjil.

function countBits(n) { const dp = new Array(n + 1).fill(0); for (let i = 1; i <= n; i++) { dp[i] = dp[i >> 1] + (i & 1); // Atau: dp[i] = dp[i & (i - 1)] + 1; (Menghapus bit set terakhir) } return dp; } console.log(countBits(5)); // [0, 1, 1, 2, 1, 2]

Pangkat Dua

Diberikan sebuah bilangan bulat n, kembalikan true jika itu adalah pangkat dua. Jika tidak, kembalikan false.

Penjelasan: Pangkat dua dalam representasi biner memiliki tepat satu bit '1' (misalnya, 1=1, 2=10, 4=100, 8=1000). Trik bitwise yang cerdik adalah memeriksa apakah n > 0 dan (n & (n - 1)) === 0. Jika n adalah pangkat dua, n-1 akan memiliki semua bit di bawah '1' tersebut diatur ke '1'. Meng-AND-kan keduanya menghasilkan 0.

function isPowerOfTwo(n) { return n > 0 && (n & (n - 1)) === 0; } console.log(isPowerOfTwo(16)); // true console.log(isPowerOfTwo(1)); // true console.log(isPowerOfTwo(18)); // false

Gabungkan Interval

Diberikan sebuah array intervals di mana intervals[i] = [starti, endi], gabungkan semua interval yang tumpang tindih, dan kembalikan array interval yang tidak tumpang tindih.

Penjelasan: Pertama, urutkan interval berdasarkan waktu mulainya. Kemudian, iterasi melalui interval yang sudah diurutkan. Jika interval saat ini tumpang tindih dengan interval terakhir dalam daftar hasil, gabungkan dengan memperbarui waktu akhir interval terakhir. Jika tidak, tambahkan interval saat ini ke daftar hasil.

function mergeIntervals(intervals) { if (intervals.length === 0) return []; intervals.sort((a, b) => a[0] - b[0]); const merged = [intervals[0]]; for (let i = 1; i < intervals.length; i++) { const last = merged[merged.length - 1]; const current = intervals[i]; if (current[0] <= last[1]) { // Tumpang tindih: gabungkan last[1] = Math.max(last[1], current[1]); } else { // Tidak tumpang tindih: tambahkan interval baru merged.push(current); } } return merged; } console.log(mergeIntervals([[1, 3], [2, 6], [8, 10], [15, 18]])); // [[1, 6], [8, 10], [15, 18]]

Pemecah Kata

Diberikan sebuah string s dan kamus string wordDict, kembalikan true jika s dapat disegmentasi menjadi urutan yang dipisahkan spasi dari satu atau lebih kata kamus.

Penjelasan: Gunakan pemrograman dinamis. Buat array boolean dp di mana dp[i] adalah true jika substring s[0...i-1] dapat disegmentasi. dp[i] adalah true jika ada j < i sedemikian rupa sehingga dp[j] adalah true dan substring s[j...i-1] ada di kamus.

function wordBreak(s, wordDict) { const wordSet = new Set(wordDict); const n = s.length; const dp = new Array(n + 1).fill(false); dp[0] = true; // Kasus dasar: string kosong for (let i = 1; i <= n; i++) { for (let j = 0; j < i; j++) { if (dp[j] && wordSet.has(s.substring(j, i))) { dp[i] = true; break; } } } return dp[n]; } console.log(wordBreak('leetcode', ['leet', 'code'])); // true console.log(wordBreak('applepenapple', ['apple', 'pen'])); // true console.log(wordBreak('catsandog', ['cats', 'dog', 'sand', 'and', 'cat'])); // false

Implementasi `Array.prototype.flat`

Implementasikan versi Anda sendiri dari Array.prototype.flat, yang membuat array baru dengan semua elemen sub-array digabungkan secara rekursif hingga kedalaman tertentu.

Penjelasan: Gunakan rekursi. Iterasi melalui array. Jika sebuah elemen adalah array dan kedalaman saat ini kurang dari kedalaman yang ditentukan, panggil flatten secara rekursif padanya. Jika tidak, push elemen tersebut. Tangani kedalaman default 1.

function myFlat(arr, depth = 1) { let flattened = []; arr.forEach(item => { if (Array.isArray(item) && depth > 0) { flattened.push(...myFlat(item, depth - 1)); } else { flattened.push(item); } }); return flattened; } console.log(myFlat([1, [2, [3, 4], 5], 6])); // [2, [3, 4], 5] console.log(myFlat([1, [2, [3, 4], 5], 6], 2)); // [1, 2, 3, 4, 5, 6]

Membalik Kata dalam String

Diberikan string input s, balikkan urutan kata-katanya.

Penjelasan: 'Kata' didefinisikan sebagai urutan karakter non-spasi. Potong string, pisahkan dengan satu atau lebih spasi, saring string kosong yang dihasilkan dari beberapa spasi, balikkan array, dan gabungkan kembali dengan spasi tunggal.

function reverseWords(s) { return s.trim().split(/\s+/).reverse().join(' '); } console.log(reverseWords('langit itu biru')); // 'biru itu langit' console.log(reverseWords(' halo dunia ')); // 'dunia halo' console.log(reverseWords('sebuah contoh yang bagus')); // 'bagus yang contoh sebuah'

Parser String Kueri

Tulis fungsi untuk mengurai string kueri URL menjadi sebuah objek.

Penjelasan: Pisahkan string dengan &. Untuk setiap bagian, pisahkan dengan = untuk mendapatkan kunci dan nilai. Dekode komponen URI untuk kunci dan nilai. Tangani kasus di mana kunci mungkin muncul beberapa kali (buat array) atau tidak memiliki nilai.

function parseQueryString(query) { if (query.startsWith('?')) { query = query.substring(1); } const result = {}; if (!query) return result; query.split('&').forEach(pair => { const [key, value] = pair.split('='); const decodedKey = decodeURIComponent(key); const decodedValue = value !== undefined ? decodeURIComponent(value) : true; if (result.hasOwnProperty(decodedKey)) { if (Array.isArray(result[decodedKey])) { result[decodedKey].push(decodedValue); } else { result[decodedKey] = [result[decodedKey], decodedValue]; } } else { result[decodedKey] = decodedValue; } }); return result; } console.log(parseQueryString('?foo=bar&baz=qux&baz=quux&corge')); // { foo: 'bar', baz: ['qux', 'quux'], corge: true }

Gunakan `Proxy` untuk Validasi

Demonstrasikan cara menggunakan objek Proxy untuk validasi properti objek.

Penjelasan: Proxy memungkinkan Anda untuk mencegat dan menyesuaikan operasi yang dilakukan pada suatu objek. Gunakan jebakan set untuk memvalidasi nilai sebelum menetapkannya ke suatu properti. Lemparkan error jika validasi gagal.

const validator = { set: function(obj, prop, value) { if (prop === 'age') { if (!Number.isInteger(value)) { throw new TypeError('Usia bukan bilangan bulat'); } if (value < 0 || value > 150) { throw new RangeError('Usia tampaknya tidak valid'); } } // Perilaku default: Atur properti obj[prop] = value; return true; } }; const person = {}; const personProxy = new Proxy(person, validator); personProxy.age = 30; // OK console.log(personProxy.age); // 30 // personProxy.age = 'tiga puluh'; // Melempar TypeError // personProxy.age = 200; // Melempar RangeError

Ruang Rapat

Diberikan array interval waktu rapat [[start1, end1], [start2, end2], ...], tentukan apakah seseorang dapat menghadiri semua rapat.

Penjelasan: Jika seseorang dapat menghadiri semua rapat, itu berarti tidak ada dua rapat yang tumpang tindih. Untuk memeriksa ini, urutkan interval berdasarkan waktu mulai mereka. Kemudian, iterasi melalui interval yang diurutkan dan periksa apakah waktu mulai rapat saat ini sebelum waktu akhir rapat sebelumnya. Jika ya, ada tumpang tindih, dan orang tersebut tidak dapat menghadiri semua rapat.

function canAttendMeetings(intervals) { if (intervals.length < 2) return true; intervals.sort((a, b) => a[0] - b[0]); for (let i = 1; i < intervals.length; i++) { if (intervals[i][0] < intervals[i - 1][1]) { return false; // Deteksi tumpang tindih } } return true; } console.log(canAttendMeetings([[0, 30], [5, 10], [15, 20]])); // false (5<30) console.log(canAttendMeetings([[7, 10], [2, 4]])); // true

Implementasi `Promise.any`

Implementasikan fungsi yang berperilaku seperti Promise.any.

Penjelasan: Promise.any mengambil array promise dan mengembalikan satu promise. Promise ini akan dipenuhi segera setelah salah satu dari promise masukan dipenuhi. Jika semua promise masukan ditolak, ia akan menolak dengan AggregateError yang berisi semua alasan penolakan.

function myPromiseAny(promises) { return new Promise((resolve, reject) => { const numPromises = promises.length; if (numPromises === 0) { reject(new AggregateError([], 'Semua promise ditolak')); return; } let rejectionCount = 0; const errors = []; promises.forEach((promise, index) => { Promise.resolve(promise) .then(resolve) // Selesaikan segera setelah salah satu terpenuhi .catch(error => { errors[index] = error; rejectionCount++; if (rejectionCount === numPromises) { reject(new AggregateError(errors, 'Semua promise ditolak')); } }); }); }); } // Contoh: myPromiseAny([Promise.reject('err1'), Promise.resolve('ok')]) -> 'ok' // Contoh: myPromiseAny([Promise.reject('err1'), Promise.reject('err2')]) -> AggregateError

Pola Observer

Implementasikan pola desain Observer (Pub/Sub) dalam JavaScript.

Penjelasan: Pola Observer mendefinisikan ketergantungan satu-ke-banyak antara objek. Ketika satu objek (Subjek) mengubah keadaan, semua objek yang bergantung padanya (Observer) diberitahu dan diperbarui secara otomatis. Subjek mempertahankan daftar observer dan menyediakan metode untuk menambah, menghapus, dan memberi tahu mereka.

class Subject { constructor() { this.observers = []; } subscribe(observer) { this.observers.push(observer); } unsubscribe(observer) { this.observers = this.observers.filter(obs => obs !== observer); } notify(data) { this.observers.forEach(observer => observer.update(data)); } } class Observer { constructor(name) { this.name = name; } update(data) { console.log(`${this.name} menerima: ${data}`); } } const subject = new Subject(); const obs1 = new Observer('Observer 1'); const obs2 = new Observer('Observer 2'); subject.subscribe(obs1); subject.subscribe(obs2); subject.notify('Sesuatu terjadi!'); // Observer 1 menerima: Sesuatu terjadi! // Observer 2 menerima: Sesuatu terjadi!

Temukan Nomor Duplikat

Diberikan array bilangan bulat nums yang berisi n + 1 bilangan bulat di mana setiap bilangan bulat berada dalam rentang [1, n] inklusif, temukan satu nomor yang berulang.

Penjelasan: Karena angka berada dalam [1, n] dan ukuran array adalah n+1, itu menjamin setidaknya satu duplikat. Ini dapat dipetakan ke masalah 'Temukan Siklus dalam Daftar Berantai' (Floyd's Tortoise and Hare). Perlakukan nilai array sebagai penunjuk: indeks -> nums[indeks].

function findDuplicate(nums) { let tortoise = nums[0]; let hare = nums[0]; // Fase 1: Temukan titik persimpangan do { tortoise = nums[tortoise]; hare = nums[nums[hare]]; } while (tortoise !== hare); // Fase 2: Temukan pintu masuk ke siklus let ptr1 = nums[0]; let ptr2 = tortoise; while (ptr1 !== ptr2) { ptr1 = nums[ptr1]; ptr2 = nums[ptr2]; } return ptr1; } console.log(findDuplicate([1, 3, 4, 2, 2])); // 2 console.log(findDuplicate([3, 1, 3, 4, 2])); // 3

Pembersih HTML Dasar

Tulis fungsi dasar untuk membersihkan string HTML, menghapus tag yang berpotensi berbahaya (seperti <script>) sambil tetap mempertahankan tag yang aman (seperti <p>, <b>).

Penjelasan: Ini adalah topik yang kompleks, dan pembersih dunia nyata sangat canggih. Untuk wawancara, pendekatan dasar mungkin melibatkan penggunaan Regex untuk menghapus tag tertentu atau hanya mengizinkan daftar putih tag. Ini tidak aman untuk produksi.

function basicSanitize(html) { // PERINGATAN: Ini adalah contoh yang sangat dasar dan tidak aman. // JANGAN gunakan ini dalam produksi. Gunakan pustaka seperti DOMPurify. // Hapus tag script let sanitized = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, ''); // Contoh: Hapus atribut onclick (sangat dasar) sanitized = sanitized.replace(/\s(on\w+)=['"]?[^'"]*['"]?/gi, ''); return sanitized; } console.log(basicSanitize('<p>Halo <script>alert("XSS")</script> <b onclick="danger()">Dunia</b></p>')); // <p>Halo <b >Dunia</b></p>

Jarak Edit

Diberikan dua string word1 dan word2, kembalikan jumlah operasi minimum (sisipkan, hapus, atau ganti) yang diperlukan untuk mengonversi word1 ke word2.

Penjelasan: Ini adalah masalah pemrograman dinamis klasik (jarak Levenshtein). Buat array 2D dp di mana dp[i][j] adalah jarak edit antara i karakter pertama word1 dan j karakter pertama word2. Hubungan rekurensi melibatkan mempertimbangkan biaya penyisipan, penghapusan, dan penggantian.

function minDistance(word1, word2) { const m = word1.length, n = word2.length; const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0)); for (let i = 0; i <= m; i++) dp[i][0] = i; for (let j = 0; j <= n; j++) dp[0][j] = j; for (let i = 1; i <= m; i++) { for (let j = 1; j <= n; j++) { const cost = word1[i - 1] === word2[j - 1] ? 0 : 1; dp[i][j] = Math.min( dp[i - 1][j] + 1, // Penghapusan dp[i][j - 1] + 1, // Penyisipan dp[i - 1][j - 1] + cost // Penggantian/Pencocokan ); } } return dp[m][n]; } console.log(minDistance('kuda', 'mawar')); // 3 console.log(minDistance('niat', 'eksekusi')); // 5

Subsequence Peningkatan Terpanjang (LIS)

Diberikan array bilangan bulat nums, kembalikan panjang subsequence yang strictly increasing terpanjang.

Penjelasan: Gunakan pemrograman dinamis. Biarkan dp[i] menjadi panjang LIS yang berakhir pada indeks i. Untuk menghitung dp[i], temukan semua j < i sedemikian rupa sehingga nums[j] < nums[i], dan ambil dp[i] = 1 + max(dp[j]). Jawaban terakhir adalah nilai maksimum dalam array dp.

function lengthOfLIS(nums) { if (nums.length === 0) return 0; const n = nums.length; const dp = new Array(n).fill(1); let maxLength = 1; for (let i = 1; i < n; i++) { for (let j = 0; j < i; j++) { if (nums[i] > nums[j]) { dp[i] = Math.max(dp[i], dp[j] + 1); } } maxLength = Math.max(maxLength, dp[i]); } return maxLength; } console.log(lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18])); // 4 (2, 3, 7, 101)

Masalah N-Queens

Teka-teki N-Queens adalah masalah menempatkan N ratu catur pada papan catur N×N sehingga tidak ada dua ratu yang saling mengancam. Diberikan N, kembalikan satu solusi yang valid (atau semua).

Penjelasan: Gunakan backtracking. Tempatkan ratu baris demi baris. Untuk setiap baris, coba tempatkan ratu di setiap kolom. Sebelum menempatkan, periksa apakah posisi yang diusulkan aman (tidak diserang oleh ratu di baris sebelumnya). Jika aman, tempatkan dan ulangi untuk baris berikutnya. Jika rekursi gagal, mundur (hapus ratu) dan coba kolom berikutnya.

function solveNQueens(n) { const results = []; const board = Array(n).fill('.').map(() => Array(n).fill('.')); function isSafe(row, col) { for (let i = 0; i < row; i++) if (board[i][col] === 'Q') return false; // Periksa kolom for (let i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) if (board[i][j] === 'Q') return false; // Periksa diagonal kiri atas for (let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) if (board[i][j] === 'Q') return false; // Periksa diagonal kanan atas return true; } function backtrack(row) { if (row === n) { results.push(board.map(r => r.join(''))); return; } for (let col = 0; col < n; col++) { if (isSafe(row, col)) { board[row][col] = 'Q'; backtrack(row + 1); board[row][col] = '.'; // Mundur } } } backtrack(0); return results; } console.log(solveNQueens(4)); // [ [ '.Q..', '...Q', 'Q...', '..Q.' ], [ '..Q.', 'Q...', '...Q', '.Q..' ] ]

Gunakan `WeakMap` untuk Data Pribadi

Demonstrasikan cara menggunakan WeakMap untuk menyimpan data pribadi untuk instans kelas.

Penjelasan: WeakMap memungkinkan Anda mengaitkan data dengan objek sedemikian rupa sehingga tidak mencegah pengumpulan sampah jika objek tidak lagi direferensikan. Ini berguna untuk membuat anggota 'pribadi' dalam kelas JavaScript sebelum bidang pribadi asli tersedia secara luas atau untuk kasus penggunaan tertentu.

const privateData = new WeakMap(); class Person { constructor(name, age) { privateData.set(this, { name: name, age: age }); } getName() { return privateData.get(this).name; } getAge() { return privateData.get(this).age; } celebrateBirthday() { privateData.get(this).age++; } } const person = new Person('Alice', 30); console.log(person.getName()); // Alice person.celebrateBirthday(); console.log(person.getAge()); // 31 // console.log(person.name); // undefined - 'name' bukan properti publik // console.log(privateData.get(person)); // Dapat diakses, tetapi tidak langsung melalui 'person.'

Implementasi `Promise.allSettled`

Implementasikan fungsi yang berperilaku seperti Promise.allSettled.

Penjelasan: Promise.allSettled mengambil array promise dan mengembalikan satu promise. Promise ini akan dipenuhi ketika semua promise masukan telah diselesaikan (baik dipenuhi atau ditolak). Nilai pemenuhan adalah array objek, masing-masing menjelaskan hasil dari satu promise ({status: 'fulfilled', value: ...} atau {status: 'rejected', reason: ...}).

function myPromiseAllSettled(promises) { return new Promise((resolve) => { const numPromises = promises.length; const results = []; let settledCount = 0; if (numPromises === 0) { resolve([]); return; } promises.forEach((promise, index) => { Promise.resolve(promise) .then(value => { results[index] = { status: 'fulfilled', value: value }; }) .catch(reason => { results[index] = { status: 'rejected', reason: reason }; }) .finally(() => { settledCount++; if (settledCount === numPromises) { resolve(results); } }); }); }); } // Contoh: myPromiseAllSettled([Promise.resolve(1), Promise.reject('err')]) // -> [{status: 'fulfilled', value: 1}, {status: 'rejected', reason: 'err'}]

Temukan Median Dua Array yang Diurutkan

Diberikan dua array yang diurutkan nums1 dan nums2 masing-masing berukuran m dan n, kembalikan median dari kedua array yang diurutkan tersebut.

Penjelasan: Ini adalah masalah sulit yang sering diselesaikan dengan pendekatan pencarian biner pada array yang lebih kecil. Tujuannya adalah untuk mempartisi kedua array sedemikian rupa sehingga semua elemen di sisi kiri kurang dari atau sama dengan semua elemen di sisi kanan, dan jumlah elemen di sisi kiri sama dengan (atau satu lebih dari) yang di kanan. Median kemudian dapat dihitung dari elemen batas.

function findMedianSortedArrays(nums1, nums2) { if (nums1.length > nums2.length) [nums1, nums2] = [nums2, nums1]; // Pastikan nums1 lebih kecil const m = nums1.length, n = nums2.length; let low = 0, high = m; while (low <= high) { const partitionX = Math.floor((low + high) / 2); const partitionY = Math.floor((m + n + 1) / 2) - partitionX; const maxX = (partitionX === 0) ? -Infinity : nums1[partitionX - 1]; const minX = (partitionX === m) ? Infinity : nums1[partitionX]; const maxY = (partitionY === 0) ? -Infinity : nums2[partitionY - 1]; const minY = (partitionY === n) ? Infinity : nums2[partitionY]; if (maxX <= minY && maxY <= minX) { if ((m + n) % 2 === 0) { return (Math.max(maxX, maxY) + Math.min(minX, minY)) / 2; } else { return Math.max(maxX, maxY); } } else if (maxX > minY) { high = partitionX - 1; } else { low = partitionX + 1; } } throw new Error('Array input tidak diurutkan'); } console.log(findMedianSortedArrays([1, 3], [2])); // 2.0 console.log(findMedianSortedArrays([1, 2], [3, 4])); // 2.5

Penyelesai Sudoku

Tulis program untuk menyelesaikan teka-teki Sudoku dengan mengisi sel-sel kosong.

Penjelasan: Gunakan backtracking. Temukan sel kosong. Coba isi dengan angka 1 hingga 9. Untuk setiap angka, periksa apakah itu valid (tidak melanggar aturan Sudoku). Jika valid, panggil rekursif penyelesai. Jika rekursi mengembalikan true, solusi ditemukan. Jika tidak, mundur (reset sel) dan coba angka berikutnya.

function solveSudoku(board) { function isValid(row, col, numStr) { for (let i = 0; i < 9; i++) { if (board[row][i] === numStr) return false; // Periksa baris if (board[i][col] === numStr) return false; // Periksa kolom const boxRow = 3 * Math.floor(row / 3) + Math.floor(i / 3); const boxCol = 3 * Math.floor(col / 3) + i % 3; if (board[boxRow][boxCol] === numStr) return false; // Periksa kotak } return true; } function backtrack() { for (let r = 0; r < 9; r++) { for (let c = 0; c < 9; c++) { if (board[r][c] === '.') { for (let n = 1; n <= 9; n++) { const numStr = String(n); if (isValid(r, c, numStr)) { board[r][c] = numStr; if (backtrack()) return true; board[r][c] = '.'; // Mundur } } return false; // Tidak ada nomor valid yang ditemukan } } } return true; // Papan terpecahkan } backtrack(); return board; } // Contoh membutuhkan papan 9x9 dengan '.' untuk sel kosong.

Implementasi Pola Middleware Dasar

Implementasikan pola middleware sederhana yang sering terlihat dalam kerangka kerja web.

Penjelasan: Fungsi middleware memproses permintaan sebelum mencapai penangan akhir. Setiap middleware dapat memodifikasi permintaan/respons atau meneruskan kontrol ke middleware next. Buat kelas atau objek yang mengelola daftar fungsi middleware dan mengeksekusinya secara berurutan.

class App { constructor() { this.middlewares = []; } use(fn) { this.middlewares.push(fn); } handle(request) { let index = 0; const next = () => { if (index < this.middlewares.length) { const middleware = this.middlewares[index++]; middleware(request, next); } }; next(); } } const app = new App(); app.use((req, next) => { console.log('1: Mencatat...'); req.logged = true; next(); }); app.use((req, next) => { console.log('2: Mengotentikasi...'); req.authed = true; next(); }); app.use((req, next) => { console.log('3: Penangan Akhir:', req); }); app.handle({ data: 'beberapa permintaan' }); // 1: Mencatat... // 2: Mengotentikasi... // 3: Penangan Akhir: { data: 'beberapa permintaan', logged: true, authed: true }

Deteksi Siklus dalam Graf Terarah

Diberikan graf terarah, tulis fungsi untuk menentukan apakah graf tersebut mengandung siklus.

Penjelasan: Gunakan Depth First Search (DFS). Pertahankan dua set: visiting (simpul yang sedang dalam tumpukan rekursi) dan visited (simpul yang telah dieksplorasi sepenuhnya). Jika Anda menemukan simpul di set visiting selama DFS, Anda telah menemukan edge kembali, yang berarti ada siklus.

function hasCycleDirected(graph) { const visiting = new Set(); const visited = new Set(); const nodes = Object.keys(graph); function dfs(node) { visiting.add(node); for (const neighbor of graph[node]) { if (visiting.has(neighbor)) return true; // Siklus terdeteksi if (!visited.has(neighbor)) { if (dfs(neighbor)) return true; } } visiting.delete(node); visited.add(node); return false; } for (const node of nodes) { if (!visited.has(node)) { if (dfs(node)) return true; } } return false; } const cyclicGraph = { A: ['B'], B: ['C'], C: ['A'] }; const acyclicGraph = { A: ['B', 'C'], B: ['D'], C: ['E'], D: [], E: [] }; console.log(hasCycleDirected(cyclicGraph)); // true console.log(hasCycleDirected(acyclicGraph)); // false

Implementasikan Perilaku `Object.freeze`

Jelaskan Object.freeze dan implementasikan versi (dangkal).

Penjelasan: Object.freeze mencegah penambahan properti baru, penghapusan properti yang ada, dan perubahan properti yang ada atau enumerabilitas, konfigurabilitas, atau keterbacaannya. Ini membuat objek tidak dapat diubah (dangkal). Anda dapat mengimplementasikannya menggunakan Object.preventExtensions dan Object.defineProperty untuk membuat properti yang ada tidak dapat ditulis dan tidak dapat dikonfigurasi.

function myFreeze(obj) { Object.preventExtensions(obj); // Mencegah penambahan properti baru Object.keys(obj).forEach(key => { Object.defineProperty(obj, key, { writable: false, configurable: false }); }); return obj; } const obj = { a: 1, b: 2 }; myFreeze(obj); // obj.c = 3; // Gagal secara diam-diam (atau melempar di mode ketat) // delete obj.a; // Gagal secara diam-diam (atau melempar di mode ketat) // obj.a = 10; // Gagal secara diam-diam (atau melempar di mode ketat) console.log(obj); // { a: 1, b: 2 }

Gunakan `requestAnimationFrame` untuk Animasi Halus

Jelaskan requestAnimationFrame dan tunjukkan cara menggunakannya untuk loop animasi sederhana.

Penjelasan: requestAnimationFrame (rAF) memberi tahu browser bahwa Anda ingin melakukan animasi dan meminta browser memanggil fungsi tertentu untuk memperbarui animasi sebelum pengecatan ulang berikutnya. Ini lebih efisien dan lebih halus daripada menggunakan setTimeout atau setInterval untuk animasi, karena sinkron dengan kecepatan refresh browser dan berhenti ketika tab tidak terlihat.

// HTML: <div id='box' style='width: 50px; height: 50px; background: red; position: absolute; left: 0px;'></div> const box = document.getElementById('box'); let position = 0; let startTime = null; function animate(currentTime) { if (!startTime) startTime = currentTime; const elapsedTime = currentTime - startTime; // Bergerak 100px dalam 2 detik (50px/detik) position = (elapsedTime / 2000) * 100; if (position < 200) { // Terus animasikan sampai mencapai 200px box.style.left = position + 'px'; requestAnimationFrame(animate); } else { box.style.left = '200px'; } } // Mulai animasi // requestAnimationFrame(animate); // Hapus komentar untuk dijalankan di browser console.log('Gunakan requestAnimationFrame untuk animasi browser.');

Implementasi `Array.prototype.some`

Implementasikan versi Anda sendiri dari Array.prototype.some.

Penjelasan: some menguji apakah setidaknya satu elemen dalam array lolos uji yang diimplementasikan oleh fungsi yang disediakan. Ia mengembalikan true jika menemukan elemen di mana callback mengembalikan true; jika tidak, ia mengembalikan false.

function mySome(arr, callback, thisArg) { for (let i = 0; i < arr.length; i++) { if (i in arr) { // Tangani array jarang if (callback.call(thisArg, arr[i], i, arr)) { return true; } } } return false; } const arr = [1, 2, 3, 4, 5]; const hasEven = mySome(arr, x => x % 2 === 0); console.log(hasEven); // true

Implementasi `Array.prototype.every`

Implementasikan versi Anda sendiri dari Array.prototype.every.

Penjelasan: every menguji apakah semua elemen dalam array lolos uji yang diimplementasikan oleh fungsi yang disediakan. Ia mengembalikan true jika semua elemen lolos (atau jika array kosong), dan false jika ada elemen yang gagal.

function myEvery(arr, callback, thisArg) { for (let i = 0; i < arr.length; i++) { if (i in arr) { if (!callback.call(thisArg, arr[i], i, arr)) { return false; } } } return true; } const arr1 = [1, 30, 39, 29, 10, 13]; const isBelow40 = myEvery(arr1, x => x < 40); console.log(isBelow40); // true const arr2 = [1, 30, 39, 29, 10, 45]; const isBelow40_2 = myEvery(arr2, x => x < 40); console.log(isBelow40_2); // false

Implementasi `Array.prototype.findIndex`

Implementasikan versi Anda sendiri dari Array.prototype.findIndex.

Penjelasan: findIndex mengembalikan indeks elemen pertama dalam array yang memenuhi fungsi pengujian yang disediakan. Jika tidak, ia mengembalikan -1, menunjukkan bahwa tidak ada elemen yang lolos uji.

function myFindIndex(arr, callback, thisArg) { for (let i = 0; i < arr.length; i++) { if (i in arr) { if (callback.call(thisArg, arr[i], i, arr)) { return i; } } } return -1; } const arr = [5, 12, 8, 130, 44]; const isLargeNumber = (element) => element > 13; console.log(myFindIndex(arr, isLargeNumber)); // 3

Apa itu Web Workers?

Jelaskan apa itu Web Workers dan kasus penggunaan utamanya.

Penjelasan: Web Workers adalah cara bagi konten web untuk menjalankan skrip di thread latar belakang. Kasus penggunaan utamanya adalah untuk memindahkan tugas yang berjalan lama atau intensif komputasi dari thread utama (yang menangani pembaruan UI dan interaksi pengguna), sehingga mencegah UI menjadi tidak responsif atau 'membeku'. Worker berkomunikasi dengan thread utama melalui pengiriman pesan (postMessage dan onmessage).

// main.js /* if (window.Worker) { const myWorker = new Worker('worker.js'); myWorker.postMessage([5, 3]); // Kirim data ke worker myWorker.onmessage = function(e) { console.log('Hasil dari worker:', e.data); } } else { console.log('Browser Anda tidak mendukung Web Workers.'); } */ // worker.js /* self.onmessage = function(e) { console.log('Pesan diterima dari skrip utama'); const result = e.data[0] * e.data[1]; console.log('Mengirim hasil kembali ke skrip utama'); self.postMessage(result); } */ console.log('Web Workers menjalankan skrip di thread latar belakang.');

Dekode Cara

Sebuah pesan yang berisi huruf dari A-Z dapat dikodekan menjadi angka menggunakan pemetaan 'A' -> 1, 'B' -> 2, ..., 'Z' -> 26. Diberikan sebuah string s yang hanya berisi digit, kembalikan jumlah cara untuk mendekodenya.

Penjelasan: Gunakan pemrograman dinamis. Misalkan dp[i] adalah jumlah cara untuk mendekode s[0...i-1]. dp[i] bergantung pada dp[i-1] (jika s[i-1] adalah kode 1-digit yang valid) dan dp[i-2] (jika s[i-2...i-1] adalah kode 2-digit yang valid).

function numDecodings(s) { if (!s || s[0] === '0') return 0; const n = s.length; const dp = new Array(n + 1).fill(0); dp[0] = 1; dp[1] = 1; for (let i = 2; i <= n; i++) { const oneDigit = parseInt(s.substring(i - 1, i)); const twoDigits = parseInt(s.substring(i - 2, i)); if (oneDigit >= 1) { dp[i] += dp[i - 1]; } if (twoDigits >= 10 && twoDigits <= 26) { dp[i] += dp[i - 2]; } } return dp[n]; } console.log(numDecodings('12')); // 2 ('AB' atau 'L') console.log(numDecodings('226')); // 3 ('BBF', 'BZ', 'VF') console.log(numDecodings('06')); // 0

Penambahan Bitwise (tanpa `+`)

Tulis fungsi untuk menambahkan dua bilangan bulat tanpa menggunakan operator +.

Penjelasan: Gunakan operator bitwise. Jumlah dapat dihitung sebagai a ^ b (jumlah tanpa carry), dan carry dapat dihitung sebagai (a & b) << 1. Ulangi proses ini sampai carry menjadi 0.

function getSum(a, b) { while (b !== 0) { const carry = (a & b) << 1; // Hitung carry a = a ^ b; // Hitung jumlah tanpa carry b = carry; // Carry menjadi 'b' yang baru } return a; } console.log(getSum(3, 5)); // 8 console.log(getSum(-2, 3)); // 1

Periksa Palindrom (Angka)

Diberikan bilangan bulat x, kembalikan true jika x adalah palindrom, dan false jika tidak.

Penjelasan: Angka negatif bukanlah palindrom. Ubah angka menjadi string dan periksa apakah string tersebut adalah palindrom (dibaca sama dari depan dan belakang). Atau, balikkan angka secara matematis dan bandingkan.

function isPalindromeNumber(x) { if (x < 0) return false; // Pendekatan string // return String(x) === String(x).split('').reverse().join(''); // Pendekatan matematis let original = x; let reversed = 0; while (x > 0) { reversed = reversed * 10 + (x % 10); x = Math.floor(x / 10); } return original === reversed; } console.log(isPalindromeNumber(121)); // true console.log(isPalindromeNumber(-121)); // false console.log(isPalindromeNumber(10)); // false

Pola Pabrik

Implementasikan pola desain Pabrik (Factory) dalam JavaScript.

Penjelasan: Pola Pabrik menyediakan antarmuka untuk membuat objek dalam superclass, tetapi memungkinkan subclass mengubah jenis objek yang akan dibuat. Ini berguna untuk membuat objek tanpa menentukan kelas yang tepat.

class Car { constructor(options) { this.type = 'Car'; this.doors = options.doors || 4; } } class Truck { constructor(options) { this.type = 'Truck'; this.bedSize = options.bedSize || 'long'; } } class VehicleFactory { createVehicle(options) { if (options.vehicleType === 'car') { return new Car(options); } else if (options.vehicleType === 'truck') { return new Truck(options); } else { throw new Error('Jenis kendaraan tidak dikenal'); } } } const factory = new VehicleFactory(); const car = factory.createVehicle({ vehicleType: 'car', doors: 2 }); const truck = factory.createVehicle({ vehicleType: 'truck', bedSize: 'short' }); console.log(car); // Car { type: 'Car', doors: 2 } console.log(truck); // Truck { type: 'Truck', bedSize: 'short' }

Jelaskan `Symbol` dan Kasus Penggunaannya

Jelaskan apa itu Symbol dalam JavaScript dan berikan kasus penggunaan, seperti membuat properti 'pribadi' atau kunci objek yang unik.

Penjelasan: Symbol adalah tipe data primitif yang diperkenalkan di ES6. Instansnya unik dan tidak dapat diubah. Mereka sering digunakan sebagai kunci untuk properti objek untuk menghindari tabrakan nama antara berbagai pustaka atau untuk membuat properti pseudo-pribadi (meskipun tidak benar-benar pribadi, mereka tidak dapat diakses melalui iterasi biasa atau Object.keys).

const _privateName = Symbol('name'); const _privateMethod = Symbol('greet'); class Person { constructor(name) { this[_privateName] = name; } [_privateMethod]() { console.log(`Halo, nama saya ${this[_privateName]}`); } introduce() { this[_privateMethod](); } } const p = new Person('Bob'); p.introduce(); // Halo, nama saya Bob console.log(Object.keys(p)); // [] - Symbol tidak termasuk console.log(p[_privateName]); // Bob (Dapat diakses jika Anda memiliki Symbol)

Konversi `arguments` ke Array Nyata

Bagaimana Anda bisa mengubah objek arguments (tersedia dalam fungsi non-panah) menjadi array JavaScript yang nyata?

Penjelasan: Objek arguments terlihat seperti array tetapi bukan; ia tidak memiliki metode seperti map, filter, dll. Anda dapat mengubahnya menggunakan:

  1. Array.from(arguments) (ES6)
  2. [...arguments] (Sintaks Spread ES6)
  3. Array.prototype.slice.call(arguments) (Cara lama)
function sumAll() { // 1. Array.from const args1 = Array.from(arguments); console.log('Menggunakan Array.from:', args1.reduce((a, b) => a + b, 0)); // 2. Sintaks Spread const args2 = [...arguments]; console.log('Menggunakan Spread:', args2.reduce((a, b) => a + b, 0)); // 3. Slice const args3 = Array.prototype.slice.call(arguments); console.log('Menggunakan Slice:', args3.reduce((a, b) => a + b, 0)); } sumAll(1, 2, 3, 4); // Menggunakan Array.from: 10 // Menggunakan Spread: 10 // Menggunakan Slice: 10