Preparazione ai colloqui

Preparazione ai colloqui

Sfide di live coding

Esercizi pratici JavaScript su algoritmi, DOM, performance, paginazione, alberi di commenti e debounce nella ricerca.

#1

Controllare se un oggetto è vuoto

Come si controlla se un oggetto JavaScript è vuoto?

Spiegazione: Un oggetto è vuoto quando non contiene proprietà proprie enumerabili. Object.keys(obj) restituisce i nomi di queste proprietà: se l’array ha lunghezza 0, l’oggetto è vuoto.

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

#2

Invertire una stringa

Scrivi una funzione che inverte una stringa.

Spiegazione: Il modo più semplice è trasformare la stringa in un array di caratteri, usare reverse() e poi unire di nuovo i caratteri con join().

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

#3

Controllare un palindromo

Scrivi una funzione che controlla se una stringa è un palindromo.

Spiegazione: Un palindromo è una parola o frase che si legge allo stesso modo da sinistra a destra e da destra a sinistra. Qui facciamo un controllo semplice: invertiamo la stringa e la confrontiamo con l’originale.

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

#4

Trovare il numero massimo in un array

Scrivi una funzione che trova il numero più grande in un array.

Spiegazione: Puoi scorrere l’array tenendo traccia del valore massimo trovato finora. In alternativa, puoi usare Math.max() insieme allo spread operator (...).

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

#5

FizzBuzz

Scrivi un programma che stampa i numeri da 1 a n. Per i multipli di tre stampa Fizz, per i multipli di cinque stampa Buzz, e per i numeri divisibili sia per tre sia per cinque stampa FizzBuzz.

Spiegazione: Questo esercizio classico verifica loop e condizioni. Bisogna scorrere i numeri da 1 a n e usare l’operatore modulo (%) per controllare la divisibilità per 3 e 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);

#6

Rimuovere duplicati da un array

Scrivi una funzione che rimuove gli elementi duplicati da un array.

Spiegazione: Un modo moderno e conciso è usare un Set, che conserva solo valori unici. Puoi convertire l’array in un Set e poi di nuovo in un array.

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

#7

Controllare gli anagrammi

Scrivi una funzione che controlla se due stringhe sono anagrammi.

Spiegazione: Gli anagrammi sono parole formate riordinando le lettere di un’altra parola. Per verificarli, pulisci le stringhe, ordinale e confrontale. La pulizia rimuove i caratteri non alfanumerici e normalizza maiuscole e minuscole.

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

#8

Calcolare il fattoriale

Scrivi una funzione che calcola il fattoriale di un intero non negativo.

Spiegazione: Il fattoriale (n!) è il prodotto di tutti gli interi positivi minori o uguali a n. Si può calcolare iterativamente moltiplicando i numeri da 1 a n.

function factorial(n) { if (n < 0) return undefined; // Factorial is not defined for negative numbers if (n === 0) return 1; let result = 1; for (let i = 1; i <= n; i++) { result *= i; } return result; } console.log(factorial(5)); // 120

#9

Somma dei numeri in un array

Scrivi una funzione che restituisce la somma di tutti i numeri in un array.

Spiegazione: Puoi usare reduce, ideale per accumulare un singolo valore a partire da un array. Richiede una callback e un valore iniziale, in questo caso 0.

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

#10

Appiattire un array annidato

Scrivi una funzione che appiattisce un array annidato. Per semplicità, considera un solo livello di annidamento.

Spiegazione: Per un solo livello puoi usare Array.prototype.concat() con lo spread operator oppure flat() (ES2019).

function flattenArray(arr) { // Using flat() (simpler if ES2019+ is okay) // return arr.flat(); // Using reduce and concat return arr.reduce((acc, val) => acc.concat(val), []); } console.log(flattenArray([1, [2, 3], 4, [5]])); // [1, 2, 3, 4, 5]

#11

Contare le vocali in una stringa

Scrivi una funzione che conta le vocali (a, e, i, o, u) in una stringa.

Spiegazione: Scorri la stringa senza distinguere maiuscole e minuscole, controlla se ogni carattere è una vocale e aggiorna un contatore.

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

#12

Convertire una frase in title case

Scrivi una funzione che converte una frase in title case, con la prima lettera di ogni parola maiuscola.

Spiegazione: Dividi la frase in parole, trasforma la prima lettera di ogni parola in maiuscolo, lascia il resto in minuscolo e poi ricomponi la frase.

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'

#13

Problema Two Sum

Dato un array di interi nums e un intero target, restituisci gli indici dei due numeri la cui somma è target.

Spiegazione: Un approccio comune usa una hash map per memorizzare numeri e indici durante lo scorrimento. Per ogni numero, controlla se target - current_number esiste già nella mappa.

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 []; // Or null, or throw error if no solution } console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1]

#14

Implementare un contatore con le closure

Crea una funzione che restituisce una funzione contatore. Ogni chiamata alla funzione restituita deve incrementare e restituire il conteggio.

Spiegazione: L’esempio mostra le closure: la funzione interna può accedere alla variabile count dello scope esterno anche dopo la fine dell’esecuzione della funzione esterna.

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

#15

Trovare il primo carattere non ripetuto

Scrivi una funzione che trova il primo carattere non ripetuto in una stringa.

Spiegazione: Usa una hash map per contare le frequenze dei caratteri. Prima costruisci la mappa, poi scorri di nuovo la stringa e restituisci il primo carattere con conteggio pari a 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; // Or some indicator if all repeat } console.log(firstNonRepeatingChar('aabbcdeeff')); // 'c' console.log(firstNonRepeatingChar('swiss')); // 'w'

#16

Dividere un array in blocchi

Scrivi una funzione che divide un array in gruppi di una dimensione specificata.

Spiegazione: Scorri l’array prendendo slice della dimensione richiesta e inseriscile in un nuovo array. Gestisci anche il caso in cui l’ultimo blocco sia più piccolo.

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]]

#17

Controllare se un numero è primo

Scrivi una funzione che determina se un numero è primo.

Spiegazione: Un numero primo è un numero naturale maggiore di 1 divisibile solo per 1 e per sé stesso. Scorri da 2 fino alla radice quadrata del numero e controlla la divisibilità, includendo casi limite come 1 e 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

#18

Trovare la parola più lunga in una stringa

Scrivi una funzione che trova la parola più lunga in una frase.

Spiegazione: Dividi la stringa in parole e scorri l’array tenendo traccia della parola più lunga trovata finora, oppure della sua lunghezza.

function findLongestWord(str) { const words = str.split(' '); let longestWord = ''; for (const word of words) { // Clean words if needed (e.g., remove punctuation) if (word.length > longestWord.length) { longestWord = word; } } return longestWord; } console.log(findLongestWord('The quick brown fox jumped over the lazy dog')); // 'jumped'

#19

Implementare `Array.prototype.map`

Implementa una tua versione di Array.prototype.map.

Spiegazione: map riceve una callback e crea un nuovo array applicando la callback a ogni elemento dell’array originale. La tua funzione deve scorrere l’array e costruire il nuovo array.

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]

#20

Implementare `Array.prototype.filter`

Implementa una tua versione di Array.prototype.filter.

Spiegazione: filter riceve una callback e crea un nuovo array con tutti gli elementi che superano il test definito dalla callback.

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']

#21

Implementare `Array.prototype.reduce`

Implementa una tua versione di Array.prototype.reduce.

Spiegazione: reduce esegue una callback reducer su ogni elemento dell’array, passando ogni volta il risultato dell’iterazione precedente. Il risultato finale è un singolo valore. Gestisci anche l’argomento del valore iniziale.

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

#22

Memoizzazione della sequenza di Fibonacci

Implementa una funzione Fibonacci usando la memoizzazione per ottimizzare le prestazioni.

Spiegazione: La sequenza di Fibonacci ripete molti calcoli. La memoizzazione salva i risultati delle chiamate costose e restituisce il valore in cache quando si ripresentano gli stessi input.

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 (much faster than non-memoized)

#23

Controllare parentesi bilanciate

Scrivi una funzione che controlla se una stringa contenente parentesi {}[]() è bilanciata.

Spiegazione: Usa uno stack. Quando incontri una parentesi aperta, inseriscila nello stack. Quando incontri una parentesi chiusa, controlla che lo stack non sia vuoto e che l’ultimo elemento sia la parentesi aperta corrispondente. Alla fine, uno stack vuoto indica una stringa bilanciata.

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

#24

Implementare una coda semplice

Implementa una struttura dati Queue con i metodi enqueue (aggiunge in fondo) e dequeue (rimuove dall’inizio).

Spiegazione: Una coda segue il principio First-In, First-Out (FIFO). Puoi usare un array con push per enqueue e shift per 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

#25

Implementare uno stack semplice

Implementa una struttura dati Stack con i metodi push (aggiunge in cima) e pop (rimuove dalla cima).

Spiegazione: Uno stack segue il principio Last-In, First-Out (LIFO). Puoi usare un array con i metodi push e 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

#26

Trovare il numero mancante in una sequenza

Dato un array con n numeri distinti presi da 0, 1, 2, ..., n, trova il numero mancante.

Spiegazione: La somma dei numeri da 0 a n si calcola con la formula n*(n+1)/2. Calcola anche la somma reale degli elementi dell’array: la differenza tra le due somme è il numero mancante.

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

#27

Funzione debounce

Implementa una funzione debounce. Il debounce impedisce a una funzione di essere richiamata finché non è passato un certo intervallo senza nuove chiamate.

Spiegazione: Usa setTimeout e clearTimeout. A ogni chiamata della funzione debounced, cancella il timeout precedente e impostane uno nuovo. La funzione reale viene eseguita solo quando il timeout termina.

function debounce(func, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => { func.apply(this, args); }, delay); }; } // Example usage: const sayHello = () => console.log('Hello!'); const debouncedHello = debounce(sayHello, 1000); debouncedHello(); // Called after 1 sec (if not called again) debouncedHello(); // Resets timer debouncedHello(); // Resets timer, will actually run 1 sec after this call.

#28

Funzione throttle

Implementa una funzione throttle. Il throttle assicura che una funzione venga chiamata al massimo una volta in un intervallo specificato.

Spiegazione: Usa un flag per indicare se la chiamata è consentita. Se lo è, esegui la funzione, imposta il flag a false e usa setTimeout per ripristinarlo dopo l’intervallo.

function throttle(func, limit) { let inThrottle = false; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } // Example usage: const logScroll = () => console.log('Scrolling...'); const throttledScroll = throttle(logScroll, 1000); // window.addEventListener('scroll', throttledScroll); // Will log at most once per second.

#29

Funzione currying

Scrivi una funzione che prende una funzione con due argomenti e restituisce una versione currificata.

Spiegazione: Il currying trasforma una funzione con più argomenti in una sequenza di funzioni, ognuna con un solo argomento. f(a, b) diventa 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

#30

Clonare profondamente un oggetto

Scrivi una funzione che esegue una deep clone di un oggetto JavaScript.

Spiegazione: Una copia superficiale copia solo le proprietà di primo livello. Una copia profonda copia ricorsivamente tutte le proprietà, inclusi oggetti e array annidati. JSON.parse(JSON.stringify(obj)) è una soluzione semplice ma limitata; un approccio ricorsivo è più solido.

function deepClone(obj) { // Simple version (with limitations) try { return JSON.parse(JSON.stringify(obj)); } catch (e) { console.error("Cloning failed:", e); return null; } // More robust recursive (basic example): /* 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 (proves it's a deep clone) console.log(cloned.b.c); // 3

#31

Ottenere le chiavi di un oggetto

Come si ottiene un array di chiavi da un oggetto?

Spiegazione: Usa Object.keys(obj).

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

#32

Ottenere i valori di un oggetto

Come si ottiene un array di valori da un oggetto?

Spiegazione: Usa Object.values(obj).

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

#33

Controllare se un array contiene un valore

Come si controlla se un array contiene un valore specifico?

Spiegazione: Usa Array.prototype.includes(value).

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

#34

Unire due array

Come si uniscono due array in uno solo?

Spiegazione: Usa lo spread operator (...) oppure Array.prototype.concat().

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

#35

Spiegare `this` nello scope globale

A cosa si riferisce this nello scope globale di un browser?

Spiegazione: Nel contesto di esecuzione globale, quindi fuori da qualsiasi funzione, this si riferisce all’oggetto globale, che nei browser è window.

console.log(this === window); // true (in a browser environment)

#36

Spiegare `this` in un metodo di oggetto

A cosa si riferisce this dentro un metodo di un oggetto?

Spiegazione: Quando una funzione viene chiamata come metodo di un oggetto, this si riferisce all’oggetto su cui il metodo è stato chiamato.

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

#37

Spiegare `this` con le arrow function

Come gestiscono this le arrow function?

Spiegazione: Le arrow function non hanno un proprio contesto this. Ereditano this dallo scope lessicale in cui sono definite.

function MyClass() { this.value = 42; setTimeout(() => { console.log(this.value); // Logs 42 because 'this' is inherited }, 100); } new MyClass();

#38

Differenza tra `let`, `const` e `var`

Quali sono le principali differenze tra let, const e var?

Spiegazione: var ha scope di funzione ed è soggetto a hoisting. let e const hanno scope di blocco e sono soggetti a hoisting, ma restano nella temporal dead zone fino alla dichiarazione. const deve essere inizializzato e non può essere riassegnato.

function scopeTest() { var a = 1; let b = 2; const c = 3; if (true) { var a = 10; // Re-declares and affects outer 'a' let b = 20; // New 'b' within block // const c = 30; // Would be a new 'c' console.log(a, b, c); // 10, 20, 3 } console.log(a, b, c); // 10, 2, 3 } scopeTest();

#39

Che cos’è una Promise?

Spiega che cos’è una Promise in JavaScript.

Spiegazione: Una Promise è un oggetto che rappresenta il completamento, o il fallimento, di un’operazione asincrona e il suo valore finale. Può trovarsi in tre stati: pending, fulfilled o rejected.

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

#40

Usare `async/await`

In che modo async e await semplificano il lavoro con le Promise?

Spiegazione: async/await è zucchero sintattico sopra le Promise e rende il codice asincrono più simile al codice sincrono. Una funzione async restituisce sempre una Promise, mentre await sospende l’esecuzione finché la Promise non si risolve o viene rifiutata.

function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function run() { console.log('Starting...'); await delay(1000); console.log('Waited 1 second.'); await delay(500); console.log('Waited another 0.5 seconds.'); return 'Finished!'; } run().then(console.log);

#41

Convertire una stringa in numero

Come si converte una stringa in un numero?

Spiegazione: Usa parseInt(), parseFloat() oppure l’operatore unario più (+).

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

#42

Convertire un numero in stringa

Come si converte un numero in una stringa?

Spiegazione: Usa String(), number.toString() o la concatenazione con una stringa.

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

#43

Che cos’è `JSON.stringify`?

Cosa fa JSON.stringify?

Spiegazione: Converte un oggetto o un valore JavaScript in una stringa JSON.

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

#44

Che cos’è `JSON.parse`?

Cosa fa JSON.parse?

Spiegazione: Analizza una stringa JSON e costruisce il valore o l’oggetto JavaScript descritto da quella stringa.

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

#45

Spread operator negli array

Come si usa lo spread operator con gli array?

Spiegazione: Permette di espandere un iterabile, come un array, nei punti in cui sono attesi zero o più argomenti o elementi. È utile per copiare, unire e aggiungere elementi.

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

#46

Spread operator negli oggetti

Come si usa lo spread operator con gli oggetti?

Spiegazione: Permette di copiare e unire proprietà di oggetti.

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 }

#47

Destructuring degli array

Spiega il destructuring degli array con un esempio.

Spiegazione: È una sintassi che permette di estrarre valori da array e assegnarli a variabili distinte.

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

#48

Destructuring degli oggetti

Spiega il destructuring degli oggetti con un esempio.

Spiegazione: Permette di estrarre proprietà da oggetti e assegnarle a variabili distinte.

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'

#49

Implementare `call`

Come potresti implementare una versione base di Function.prototype.call?

Spiegazione: call invoca una funzione con un valore this specifico e argomenti passati singolarmente. Puoi collegare temporaneamente la funzione al contesto this, chiamarla e poi rimuoverla.

Function.prototype.myCall = function(context, ...args) { context = context || window; const uniqueId = Symbol(); // Use a unique key 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!

#50

Implementare `apply`

Come potresti implementare una versione base di Function.prototype.apply?

Spiegazione: apply è simile a call, ma accetta gli argomenti come 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.

#51

Spiega l'Event Loop

Spiega brevemente l'Event Loop di JavaScript.

Spiegazione: JavaScript è a singolo thread, ma raggiunge la concorrenza usando un event loop. Lo stack di chiamate gestisce il codice sincrono. Le API web gestiscono operazioni asincrone (come setTimeout, fetch). Quando un'operazione asincrona termina, la sua callback va alla coda delle callback (o microtask queue per le Promise). L'event loop controlla costantemente se lo stack di chiamate è vuoto; se lo è, sposta la callback successiva dalla coda allo stack per l'esecuzione.

console.log('Start'); setTimeout(() => { console.log('Timeout Callback'); // Goes to Callback Queue }, 0); Promise.resolve().then(() => { console.log('Promise Resolved'); // Goes to Microtask Queue }); console.log('End'); // Output Order: Start, End, Promise Resolved, Timeout Callback // (Microtasks run before Macrotasks/Callbacks)

#52

Ricerca Binaria

Implementa una funzione di ricerca binaria per un array ordinato.

Spiegazione: La ricerca binaria trova efficientemente un elemento in un array ordinato dividendo ripetutamente l'intervallo di ricerca a metà. Se il valore della chiave di ricerca è inferiore all'elemento al centro dell'intervallo, si restringe l'intervallo alla metà inferiore. Altrimenti, lo si restringe alla metà superiore. Si esegue questa operazione finché il valore non viene trovato o l'intervallo è vuoto.

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; // Found } else if (sortedArray[middle] < key) { start = middle + 1; // Search right half } else { end = middle - 1; // Search left half } } return -1; // Not found } console.log(binarySearch([1, 3, 5, 7, 9, 11], 7)); // 3 console.log(binarySearch([1, 3, 5, 7, 9, 11], 2)); // -1

#53

Merge Sort

Implementa l'algoritmo Merge Sort.

Spiegazione: Merge Sort è un algoritmo divide-et-impera. Divide l'array di input in due metà, si richiama su entrambe le metà, quindi unisce le due metà ordinate. La funzione di unione è cruciale; combina due sottoarray ordinati in un unico array ordinato.

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]

#54

Quick Sort

Implementa l'algoritmo Quick Sort.

Spiegazione: Quick Sort è anch'esso un algoritmo divide-et-impera. Sceglie un elemento come pivot e partiziona l'array dato attorno al pivot scelto. Gli elementi più piccoli del pivot vanno a sinistra, e gli elementi più grandi vanno a destra. Quindi ordina ricorsivamente i sottoarray.

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]

#55

Bubble Sort

Implementa l'algoritmo Bubble Sort.

Spiegazione: Bubble Sort è un semplice algoritmo di ordinamento che scorre ripetutamente la lista, confronta gli elementi adiacenti e li scambia se sono nell'ordine sbagliato. Il passaggio attraverso la lista viene ripetuto finché la lista non è ordinata.

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]]; // Swap swapped = true; } } n--; // Optimization: last element is already in place } while (swapped); return arr; } console.log(bubbleSort([64, 34, 25, 12, 22, 11, 90])); // [11, 12, 22, 25, 34, 64, 90]

#56

Sottostringa più lunga senza caratteri ripetuti

Data una stringa, trova la lunghezza della sottostringa più lunga senza caratteri ripetuti.

Spiegazione: Utilizza la tecnica della 'finestra scorrevole'. Mantieni una finestra (sottostringa) e un set di caratteri in quella finestra. Espandi la finestra spostando il puntatore destro. Se viene trovato un carattere ripetuto, restringi la finestra da sinistra finché il carattere ripetuto non viene rimosso.

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')

#57

Implementa una Lista Collegata

Implementa una Lista Collegata Singolarmente con i metodi add e print.

Spiegazione: Una lista collegata è una struttura dati lineare dove gli elementi non sono memorizzati in posizioni di memoria contigue. Ogni elemento (nodo) punta al successivo. Hai bisogno di una classe Node e di una classe LinkedList per gestire la testa e l'aggiunta di nuovi nodi.

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

#58

Implementa un Albero di Ricerca Binario (BST)

Implementa un Albero di Ricerca Binario con i metodi insert e find.

Spiegazione: Un BST è una struttura dati ad albero binario basata su nodi che ha le seguenti proprietà: il sottoalbero sinistro di un nodo contiene solo nodi con chiavi minori della chiave del nodo. Il sottoalbero destro di un nodo contiene solo nodi con chiavi maggiori della chiave del nodo. Entrambi i sottoalberi devono essere anche alberi di ricerca binari.

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

#59

Ruota un Array

Scrivi una funzione per ruotare un array a destra di k passi.

Spiegazione: Ruotare un array significa spostare i suoi elementi. Per una rotazione a destra, l'ultimo elemento diventa il primo. Ripetere questo k volte funziona ma è inefficiente. Un modo migliore è usare la manipolazione dell'array o calcolare le nuove posizioni.

function rotateArray(nums, k) { k = k % nums.length; // Handle cases where k > length 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]

#60

Trova l'intersezione di Due Array

Dati due array, scrivi una funzione per calcolare la loro intersezione (elementi comuni ad entrambi).

Spiegazione: Un buon approccio è convertire un array in un Set per ricerche in tempo medio O(1). Quindi, iterare attraverso il secondo array e controllare se ogni elemento esiste nel set. Raccogli le corrispondenze.

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]

#61

Raggruppa Anagrammi

Dato un array di stringhe, raggruppa gli anagrammi insieme.

Spiegazione: La chiave è trovare una firma unica per ogni gruppo di anagrammi. Un modo comune è ordinare ogni parola alfabeticamente. Le parole che risultano nella stessa stringa ordinata sono anagrammi. Usa una hash map dove le chiavi sono parole ordinate e i valori sono array di parole originali.

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']]

#62

Sposta Zeri alla Fine

Dato un array nums, scrivi una funzione per spostare tutti gli 0 alla fine mantenendo l'ordine relativo degli elementi non zero.

Spiegazione: Utilizza un approccio 'a due puntatori' o 'a palla di neve'. Un puntatore tiene traccia della posizione in cui dovrebbe andare il prossimo elemento non zero. Itera attraverso l'array; se un elemento non è zero, posizionalo nella posizione del puntatore e incrementa il puntatore. Infine, riempi il resto con zeri.

function moveZeroes(nums) { let nonZeroIndex = 0; // Move all non-zero elements to the front for (let i = 0; i < nums.length; i++) { if (nums[i] !== 0) { nums[nonZeroIndex] = nums[i]; nonZeroIndex++; } } // Fill the rest with zeros 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]

#63

Sottoarray Massimo (Algoritmo di Kadane)

Dato un array di interi nums, trova il sottoarray contiguo (contenente almeno un numero) che ha la somma maggiore e restituisci la sua somma.

Spiegazione: L'algoritmo di Kadane è un modo efficiente per risolvere questo problema. Itera attraverso l'array, tenendo traccia della somma currentMax che termina nella posizione corrente e della somma globalMax trovata finora. Se currentMax diventa negativo, resettalo a 0 (o piuttosto, all'elemento corrente).

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 (from [4, -1, 2, 1])

#64

Permutazioni di una Stringa

Scrivi una funzione per generare tutte le permutazioni di una data stringa.

Spiegazione: Questo è tipicamente risolto usando la ricorsione e il backtracking. Per ogni carattere, fissalo e genera ricorsivamente le permutazioni per il resto della stringa. Caso base: quando la stringa ha un solo carattere, restituiscilo.

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)]; // Use Set to handle duplicate chars if needed } console.log(stringPermutations('abc')); // ['abc', 'acb', 'bac', 'bca', 'cab', 'cba'] console.log(stringPermutations('aab')); // ['aab', 'aba', 'baa']

#65

Massimo Comune Divisore (MCD)

Scrivi una funzione per trovare il Massimo Comune Divisore (MCD) di due numeri.

Spiegazione: L'algoritmo euclideo è un metodo efficiente. Se b è 0, a è il MCD. Altrimenti, il MCD di a e b è lo stesso del MCD di b e a % b (il resto di a diviso per 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

#66

Minimo Comune Multiplo (MCM)

Scrivi una funzione per trovare il Minimo Comune Multiplo (MCM) di due numeri.

Spiegazione: Il MCM può essere calcolato usando il MCD con la formula: MCM(a, b) = |a * b| / MCD(a, b).

function gcd(a, b) { // Helper from previous problem 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

#67

Implementa Promise.all

Implementa una funzione che si comporta come Promise.all.

Spiegazione: Promise.all prende un array di promise e restituisce una singola promise che si risolve quando tutte le promise di input si sono risolte, o si rifiuta se una qualsiasi promise di input si rifiuta. Il valore risolto è un array dei valori risolti.

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); // Reject immediately on any error }); }); } // Example usage: 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']

#68

Inverti Albero Binario

Scrivi una funzione per invertire un albero binario.

Spiegazione: Per invertire un albero binario, si scambiano i figli sinistro e destro per ogni nodo. Questo può essere fatto ricorsivamente o iterativamente (usando una coda o uno stack).

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; } // Swap the children [root.left, root.right] = [root.right, root.left]; // Recur for left and right children invertTree(root.left); invertTree(root.right); return root; } // Example: 4 -> [2, 7] -> [1, 3, 6, 9] // Becomes: 4 -> [7, 2] -> [9, 6, 3, 1]

#69

Profondità Massima dell'Albero Binario

Dato un albero binario, trova la sua profondità massima.

Spiegazione: La profondità massima è il numero di nodi lungo il percorso più lungo dal nodo radice al nodo foglia più lontano. Questo può essere trovato ricorsivamente: MaxDepth = 1 + max(MaxDepth(sinistro), MaxDepth(destro)). Il caso base è quando un nodo è nullo, la sua profondità è 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; } // Example: 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

#70

Miglior Momento per Comprare/Vendere Azioni

Ti viene dato un array prices dove prices[i] è il prezzo di una data azione nel giorno i. Vuoi massimizzare il tuo profitto scegliendo un singolo giorno per comprare un'azione e scegliendo un giorno diverso in futuro per vendere quell'azione. Restituisci il profitto massimo.

Spiegazione: Itera attraverso i prezzi, tenendo traccia del prezzo minimo incontrato finora (minPrice) e del profitto massimo trovato finora (maxProfit). Per ogni giorno, calcola il potenziale profitto se vendessi oggi (prezzo corrente - minPrice) e aggiorna maxProfit se è superiore.

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 (Buy at 1, Sell at 6) console.log(maxProfit([7, 6, 4, 3, 1])); // 0 (No profit possible)

#71

Numero Singolo

Dato un array non vuoto di interi nums, ogni elemento appare due volte tranne uno. Trova quell'unico.

Spiegazione: Un modo molto efficiente per risolvere questo problema è usare l'operatore bitwise XOR (^). Fare l'XOR di un numero con se stesso dà come risultato 0. Fare l'XOR di un numero con 0 dà come risultato il numero stesso. Se fai l'XOR di tutti i numeri nell'array, le coppie si annulleranno (diventeranno 0), lasciando solo il numero singolo.

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

#72

Elemento Maggioritario

Dato un array nums di dimensione n, restituisci l'elemento maggioritario. L'elemento maggioritario è l'elemento che appare più di ⌊n / 2⌋ volte.

Spiegazione: L'algoritmo di voto di Boyer-Moore è un modo efficiente. Inizializza un candidate e un count. Itera attraverso l'array. Se count è 0, imposta l'elemento corrente come candidate. Se l'elemento corrente corrisponde a candidate, incrementa count; altrimenti, decrementa 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

#73

Salire le Scale

Stai salendo una scala. Ci vogliono n gradini per raggiungere la cima. Ogni volta puoi salire 1 o 2 gradini. In quanti modi distinti puoi salire in cima?

Spiegazione: Questo è un classico problema di programmazione dinamica, molto simile alla sequenza di Fibonacci. Il numero di modi per raggiungere il gradino n è la somma dei modi per raggiungere il gradino n-1 (facendo 1 gradino) e dei modi per raggiungere il gradino n-2 (facendo 2 gradini). 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

#74

Prodotto dell'Array Eccetto Se Stesso

Dato un array di interi nums, restituisci un array answer tale che answer[i] sia uguale al prodotto di tutti gli elementi di nums eccetto nums[i]. Non usare l'operatore di divisione.

Spiegazione: Puoi risolvere questo problema calcolando i prodotti dei prefissi e i prodotti dei suffissi. Il risultato all'indice i è il prodotto di tutti gli elementi prima di i moltiplicato per il prodotto di tutti gli elementi dopo i.

function productExceptSelf(nums) { const n = nums.length; const answer = new Array(n).fill(1); // Calculate prefix products let prefix = 1; for (let i = 0; i < n; i++) { answer[i] = prefix; prefix *= nums[i]; } // Calculate suffix products and multiply with prefix 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]

#75

Numero di Isole

Data una griglia 2D di '1' (terra) e '0' (acqua), conta il numero di isole. Un'isola è circondata dall'acqua ed è formata collegando terre adiacenti orizzontalmente o verticalmente.

Spiegazione: Itera attraverso ogni cella della griglia. Se trovi un '1' (terra), incrementa il conteggio delle isole e avvia una Ricerca in Profondità (DFS) o una Ricerca in Ampiezza (BFS) da quella cella. Durante la ricerca, segna tutti gli '1' collegati come '0' (o visitati) per evitare di contarli di nuovo.

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'; // Mark as visited 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 (Need to run on a copy or restore grid)

#76

Cache LRU

Progetta e implementa una struttura dati per una cache Least Recently Used (LRU). Dovrebbe supportare le operazioni get e put.

Spiegazione: Una cache LRU espelle l'elemento meno recentemente usato quando la capacità è raggiunta. Un'implementazione comune utilizza una Map (o una hash map) per ricerche O(1) in tempo medio e una lista doppiamente collegata per aggiornamenti O(1) dello stato 'recentemente usato'. La Map di JavaScript mantiene l'ordine di inserimento, il che può semplificare un'implementazione di base.

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); // Remove to re-insert at the 'end' (most recent) 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); // Evicts 2 console.log(cache.get(2)); // -1

#77

Genera Parentesi

Dato n coppie di parentesi, scrivi una funzione per generare tutte le combinazioni di parentesi ben formate.

Spiegazione: Questo è un classico problema di backtracking. Mantieni i conteggi per le parentesi aperte e chiuse. Puoi aggiungere una parentesi aperta se open < n. Puoi aggiungere una parentesi chiusa se close < open. Il caso base è quando la lunghezza della stringa raggiunge 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)); // ['((()))', '(()())', '(())()', '()(())', '()()()']

#78

Contenitore con Più Acqua

Dati n interi non negativi a1, a2, ..., an, dove ciascuno rappresenta un punto con coordinata (i, ai). n linee verticali sono disegnate in modo tale che i due estremi della linea i siano in (i, ai) e (i, 0). Trova due linee che, insieme all'asse x, formano un contenitore, tale che il contenitore contenga la maggior quantità di acqua.

Spiegazione: Utilizza l'approccio dei due puntatori. Inizia con un puntatore all'inizio e uno alla fine. Calcola l'area. L'area è limitata dalla linea più corta. Per trovare potenzialmente un'area più grande, sposta il puntatore che punta alla linea più corta verso l'interno.

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

#79

3Sum

Dato un array nums di n interi, ci sono elementi a, b, c in nums tali che a + b + c = 0? Trova tutte le triplette uniche nell'array che danno come somma zero.

Spiegazione: Per prima cosa, ordina l'array. Poi, itera attraverso l'array con un puntatore i. Per ogni i, usa altri due puntatori, left (che inizia da i+1) e right (che inizia dalla fine), per trovare due numeri la cui somma sia -nums[i]. Gestisci i duplicati per assicurare triplette uniche.

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; // Skip duplicates for 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++; // Skip duplicates while (left < right && nums[right] === nums[right - 1]) right--; // Skip duplicates 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]]

#80

Cerca Posizione di Inserimento

Dato un array ordinato di interi distinti e un valore target, restituisci l'indice se il target viene trovato. Se non lo è, restituisci l'indice in cui sarebbe se fosse inserito in ordine.

Spiegazione: Questa è una variazione della ricerca binaria. Esegui la ricerca binaria, ma se l'elemento non viene trovato, il puntatore start si troverà nella posizione in cui l'elemento dovrebbe essere inserito.

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; // If not found, start is the insertion point } 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

#81

Unisci Due Liste Ordinate (Liste Collegata)

Unisci due liste collegate ordinate e restituiscile come una nuova lista ordinata. La nuova lista dovrebbe essere creata unendo i nodi delle prime due liste.

Spiegazione: Utilizza un nodo fittizio (dummy head) per semplificare il processo. Usa un puntatore per costruire la nuova lista. Confronta i nodi correnti di entrambe le liste di input e aggiungi il più piccolo alla nuova lista, avanzando il puntatore di quella lista. Ripeti finché una lista non è esaurita, quindi aggiungi il resto dell'altra lista.

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; } // Example: 1->2->4 and 1->3->4 => 1->1->2->3->4->4

#82

Albero Simmetrico

Dato un albero binario, controlla se è uno specchio di se stesso (cioè, simmetrico rispetto al suo centro).

Spiegazione: Questo può essere risolto ricorsivamente. Un albero è simmetrico se il sottoalbero sinistro è un'immagine speculare del sottoalbero destro. Crea una funzione di supporto isMirror(t1, t2) che verifica se due alberi sono speculari. Dovrebbe verificare: 1. t1.val === t2.val. 2. t1.left è uno specchio di t2.right. 3. t1.right è uno specchio di 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); } // Example: 1 -> [2, 2] -> [3, 4, 4, 3] is symmetric.

#83

Attraversamento per Livelli (BST/Albero)

Dato un albero binario, restituisci l'attraversamento per livelli dei valori dei suoi nodi. (cioè, da sinistra a destra, livello per livello).

Spiegazione: Questa è una ricerca in ampiezza (BFS). Usa una coda. Inizia aggiungendo la radice alla coda. Finché la coda non è vuota, elabora tutti i nodi al livello corrente. Per ogni nodo elaborato, aggiungi i suoi figli (se esistono) alla coda per il livello successivo.

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; } // Example: 3 -> [9, 20] -> [null, null, 15, 7] // Output: [[3], [9, 20], [15, 7]]

#84

Converti Array Ordinato in BST Bilanciato in Altezza

Dato un array i cui elementi sono ordinati in ordine crescente, convertilo in un albero di ricerca binario (BST) bilanciato in altezza.

Spiegazione: Per creare un BST bilanciato in altezza, dovresti scegliere l'elemento centrale dell'array come radice. Quindi, costruisci ricorsivamente il sottoalbero sinistro dalla metà sinistra dell'array e il sottoalbero destro dalla metà destra.

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); } // Example: [-10, -3, 0, 5, 9] // Output: A tree like [0, -3, 9, -10, null, 5, null]

#85

Implementa `bind`

Implementa la tua versione di Function.prototype.bind.

Spiegazione: bind crea una nuova funzione che, quando chiamata, ha la sua parola chiave this impostata al valore fornito, con una data sequenza di argomenti che precedono qualsiasi argomento fornito quando la nuova funzione viene chiamata. Usa apply o call all'interno di una funzione restituita, gestendo l'applicazione parziale (argomenti preimpostati).

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 is global/window) const boundGetX = unboundGetX.myBind(module); console.log(boundGetX()); // 42

#86

Trova il K-esimo Elemento Più Grande

Trova il k-esimo elemento più grande in un array non ordinato. Nota che è il k-esimo elemento più grande nell'ordine ordinato, non il k-esimo elemento distinto.

Spiegazione: Un approccio semplice è ordinare l'array e quindi scegliere l'elemento all'indice n - k. Un approccio più efficiente per array grandi implica l'uso di un min-heap o di un algoritmo di selezione come Quickselect.

function findKthLargest(nums, k) { // Simple approach: Sort nums.sort((a, b) => b - a); // Sort in descending order return nums[k - 1]; // Note: Quickselect would be more efficient in an interview // but sorting is easier to implement quickly. } 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

#87

Controlla se l'Oggetto ha una Proprietà

Come puoi controllare se un oggetto ha una proprietà specifica?

Spiegazione: Puoi usare l'operatore in (controlla le proprietà proprie ed ereditate), Object.prototype.hasOwnProperty.call(obj, prop) (controlla solo le proprietà proprie), o semplicemente obj.prop !== undefined (può essere complicato con valori undefined).

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

#88

Intero a Romano

Scrivi una funzione per convertire un intero nella sua rappresentazione in numeri romani.

Spiegazione: Crea una mappatura dei numeri romani e dei loro valori corrispondenti, ordinati dal più grande al più piccolo. Itera su questa mappa. Per ogni valore, sottrailo dal numero di input il maggior numero di volte possibile, aggiungendo il numero romano ogni volta.

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

#89

Romano a Intero

Scrivi una funzione per convertire un numero romano in un intero.

Spiegazione: Crea una mappa dei simboli romani ai valori. Itera attraverso la stringa. Se il valore del simbolo corrente è inferiore al valore del simbolo successivo (come 'IV' o 'IX'), sottrai il valore corrente; altrimenti, aggiungilo.

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

#90

Implementa un `Set`

Implementa una struttura dati Set di base (senza usare il Set predefinito) con i metodi add, has, delete e size.

Spiegazione: Puoi usare un oggetto JavaScript (hash map) come archivio sottostante. Le chiavi saranno gli elementi del set (potrebbe essere necessario gestire come memorizzare tipi diversi e garantire l'unicità come chiavi).

class MySet { constructor() { this.items = {}; this._size = 0; } add(element) { if (!this.has(element)) { this.items[element] = element; // Store the element 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

#91

Triangolo di Pascal

Dato un intero numRows, genera le prime numRows del triangolo di Pascal.

Spiegazione: Nel triangolo di Pascal, ogni numero è la somma dei due numeri direttamente sopra di esso. Inizia con [[1]]. Per ogni riga successiva, inizia e finisci con 1. Ogni elemento centrale è la somma dell'elemento allo stesso indice e dell'indice precedente dalla riga superiore.

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]]

#92

Ricerca di Parole

Data una board 2D e una parola, trova se la parola esiste nella griglia. La parola può essere costruita da lettere di celle adiacenti sequenzialmente, dove le celle 'adiacenti' sono vicine orizzontalmente o verticalmente.

Spiegazione: Questo richiede una ricerca in profondità (DFS) con backtracking. Itera attraverso ogni cella. Se una cella corrisponde alla prima lettera della parola, avvia una DFS. La funzione DFS controlla i vicini, assicurandosi che corrispondano alla lettera successiva e che non siano stati visitati nel percorso corrente. Se un percorso fallisce, torna indietro deselezionando la cella.

function exist(board, word) { const rows = board.length; const cols = board[0].length; function dfs(r, c, index) { if (index === word.length) return true; // Word found if (r < 0 || c < 0 || r >= rows || c >= cols || board[r][c] !== word[index]) { return false; } const temp = board[r][c]; board[r][c] = '#'; // Mark as visited 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

#93

Sottostringa Finestra Minima

Date due stringhe s e t, trova la finestra minima in s che conterrà tutti i caratteri in t. Se non esiste una tale finestra in s che copra tutti i caratteri in t, restituisci la stringa vuota "".

Spiegazione: Utilizza un approccio a finestra scorrevole con due puntatori (left e right) e hash map. Una mappa memorizza i conteggi dei caratteri necessari da t. Un'altra mappa memorizza i conteggi nella finestra corrente. Espandi la finestra con right. Una volta che la finestra contiene tutti i caratteri necessari, prova a restringila con left per trovare la finestra minima possibile.

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'

#94

Inverti Lista Collegata

Data la head, la testa di una lista collegata singolarmente, inverti la lista e restituisci la lista invertita.

Spiegazione: Devi iterare attraverso la lista, cambiando il puntatore next di ogni nodo in modo che punti al nodo precedente. Tieni traccia dei nodi previous, current e next durante l'iterazione.

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; // Store next node current.next = prev; // Reverse current node's pointer prev = current; // Move prev one step forward current = next; // Move current one step forward } return prev; // New head is the last 'prev' } // Example: 1->2->3->4->5 becomes 5->4->3->2->1

#95

Rileva Ciclo nella Lista Collegata

Data la head, la testa di una lista collegata, determina se la lista collegata ha un ciclo.

Spiegazione: Usa l'algoritmo 'Floyd's Tortoise and Hare'. Avere due puntatori, uno che si muove di un passo alla volta (slow) e uno che si muove di due passi alla volta (fast). Se c'è un ciclo, il puntatore fast alla fine raggiungerà il puntatore 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; // Reached end, no cycle slow = slow.next; fast = fast.next.next; } return true; // Pointers met, cycle exists } // Example: 3->2->0->-4 with -4 pointing back to 2. hasCycle returns true.

#96

Implementa `Object.create`

Implementa una funzione che imiti il comportamento di Object.create(proto).

Spiegazione: Object.create crea un nuovo oggetto, usando un oggetto esistente come prototipo dell'oggetto appena creato. Puoi ottenere questo creando una funzione costruttore temporanea, impostando il suo prototipo sull'input proto e quindi restituendo una nuova istanza di esso.

function myObjectCreate(proto) { if (typeof proto !== 'object' && typeof proto !== 'function') { if (proto !== null) { throw new TypeError('Object prototype may only be an Object or null: ' + proto); } } function F() {} F.prototype = proto; return new F(); } const person = { isHuman: false, printIntroduction: function() { console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`); } }; const me = myObjectCreate(person); me.name = 'Matthew'; me.isHuman = true; me.printIntroduction(); // My name is Matthew. Am I human? true console.log(Object.getPrototypeOf(me) === person); // true

#97

Cos'è l'Hoisting?

Spiega l'hoisting in JavaScript e fornisci un esempio.

Spiegazione: L'hoisting è il comportamento predefinito di JavaScript di spostare le dichiarazioni all'inizio dell'ambito corrente (globale o funzione) prima dell'esecuzione del codice. Le dichiarazioni di variabile (var) vengono sollevate e inizializzate con undefined. Le dichiarazioni di funzione sono completamente sollevate (sia il nome che il corpo). let e const sono sollevate ma non inizializzate, portando a una Temporal Dead Zone.

console.log(myVar); // undefined (var is hoisted and initialized with undefined) // console.log(myLet); // ReferenceError: Cannot access 'myLet' before initialization (TDZ) myFunc(); // 'Hello!' (Function declaration is fully hoisted) var myVar = 'I am a var'; let myLet = 'I am a let'; function myFunc() { console.log('Hello!'); }

#98

Spiega Event Bubbling e Capturing

Spiega Event Bubbling e Capturing nel contesto del DOM.

Spiegazione: Queste sono due fasi della propagazione degli eventi nel DOM HTML. Fase di cattura: L'evento si sposta dalla window all'elemento target. Fase di bubbling: L'evento si sposta dall'elemento target di nuovo alla window. Per impostazione predefinita, la maggior parte dei gestori di eventi sono registrati durante la fase di bubbling. Puoi usare addEventListener(type, listener, useCapture) con useCapture = true per gestire gli eventi durante la fase di cattura.

// In an HTML structure: <div><p><span>Click Me</span></p></div> // If you click the <span>: // Capturing: window -> document -> html -> body -> div -> p -> span // Bubbling: span -> p -> div -> body -> html -> document -> window /* JS Example div.addEventListener('click', () => console.log('Div clicked'), true); // Capturing p.addEventListener('click', () => console.log('P clicked'), false); // Bubbling span.addEventListener('click', () => console.log('Span clicked'), false); // Bubbling // Output would be: Div clicked, Span clicked, P clicked */

#99

Implementa `JSON.parse` manualmente (base)

Prova a implementare una versione molto basilare di JSON.parse (gestisce oggetti semplici, array, stringhe, numeri).

Spiegazione: Questo è un compito molto complesso nella sua interezza, ma per un'impostazione di codifica dal vivo, potresti essere chiesto di gestire un sottoinsieme molto semplificato. Dovresti analizzare la stringa, identificando i limiti di oggetto {} e array [], le coppie chiave-valore (key: value), e i tipi di dati di base. eval o new Function possono barare ma sono pericolosi. Un vero parser userebbe un lexer/tokenizer e un parser.

function basicJsonParse(jsonString) { // WARNING: Using new Function is insecure like eval. // This is a simplified, INSECURE example for demonstration only. // A real implementation requires a proper parser. try { return (new Function('return ' + jsonString))(); } catch (e) { throw new SyntaxError('Invalid JSON: ' + e.message); } } console.log(basicJsonParse('{"a": 1, "b": ["x", "y"]}')); // { a: 1, b: ['x', 'y'] }

#100

Appiattisci un Array Profondamente Annidato

Scrivi una funzione per appiattire un array profondamente annidato.

Spiegazione: A differenza dell'appiattimento precedente (un livello), questo deve gestire qualsiasi livello di annidamento. La ricorsione è una soluzione naturale. Itera attraverso l'array. Se un elemento è un array, chiama ricorsivamente flatten su di esso e concatena il risultato. Altrimenti, aggiungi l'elemento.

function deepFlatten(arr) { let flattened = []; arr.forEach(item => { if (Array.isArray(item)) { flattened = flattened.concat(deepFlatten(item)); } else { flattened.push(item); } }); return flattened; // ES2019+ provides a much simpler way: // return arr.flat(Infinity); } console.log(deepFlatten([1, [2, [3, 4], 5], 6])); // [1, 2, 3, 4, 5, 6]

#101

Implementa un Trie (Albero dei Prefissi)

Implementa una struttura dati Trie con i metodi insert, search e startsWith.

Spiegazione: Un Trie è una struttura dati ad albero utilizzata per memorizzare e recuperare in modo efficiente le chiavi in un set di dati di stringhe. Ogni nodo rappresenta un carattere e i percorsi dalla radice rappresentano parole o prefissi.

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

#102

Mischia un Array (Fisher-Yates)

Scrivi una funzione per mescolare un array sul posto usando l'algoritmo Fisher-Yates (o Knuth).

Spiegazione: L'algoritmo Fisher-Yates itera un array dall'ultimo elemento al primo. In ogni iterazione, scambia l'elemento corrente con un elemento scelto casualmente dall'inizio dell'array fino all'elemento corrente (incluso).

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]]; // Swap elements } return arr; } console.log(shuffleArray([1, 2, 3, 4, 5])); // e.g., [3, 5, 1, 2, 4]

#103

Componi Funzioni

Implementa una funzione compose che prende più funzioni e restituisce una nuova funzione che le applica da destra a sinistra.

Spiegazione: La composizione di funzioni (f ∘ g)(x) = f(g(x)) applica una funzione al risultato di un'altra. Una compose(f, g, h) generale significherebbe f(g(h(x))). Usa reduceRight per un'implementazione elegante.

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

#104

Pipeline di Funzioni

Implementa una funzione pipe che prende più funzioni e restituisce una nuova funzione che le applica da sinistra a destra.

Spiegazione: Simile a compose, ma l'ordine di applicazione è invertito: pipe(f, g, h) significa h(g(f(x))). Usa reduce per l'implementazione.

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

#105

Problema del Cambio Monete

Dato un array di denominazioni di monete e un importo, trova il numero minimo di monete per raggiungere quell'importo. Supponi una scorta infinita di ogni moneta.

Spiegazione: Questo è un classico problema di programmazione dinamica. Crea un array dp dove dp[i] memorizza il numero minimo di monete necessarie per l'importo i. dp[i] = min(dp[i - coin]) + 1 per tutte le monete.

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

#106

Antenato Comune Più Basso (BST)

Dato un albero di ricerca binario (BST), trova l'antenato comune più basso (LCA) di due nodi dati nel BST.

Spiegazione: L'LCA è il nodo più profondo che ha entrambi i nodi dati come discendenti. In un BST, puoi trovarlo attraversando dalla radice. Se entrambi i nodi sono più piccoli del nodo corrente, vai a sinistra. Se entrambi sono più grandi, vai a destra. Se uno è più piccolo e uno è più grande (o uno è il nodo corrente), allora il nodo corrente è l'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; // Found LCA } } return null; // Should not happen in a valid BST with p and q } // Example: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 => LCA is 6

#107

Serializza e Deserializza Albero Binario

Progetta un algoritmo per serializzare e deserializzare un albero binario.

Spiegazione: La serializzazione converte un albero in una stringa o array. La deserializzazione ricostruisce l'albero. Un modo comune è l'attraversamento in pre-ordine (DFS). Usa un marcatore speciale (ad esempio, '#') per i nodi nulli per preservare la struttura.

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(); } // Example: 1 -> [2, 3] -> [null, null, 4, 5] // Serialized: '1,2,#,#,3,4,#,#,5,#,#'

#108

Implementa `setInterval` con `setTimeout`

Implementa una funzione mySetInterval che imita setInterval ma usa setTimeout ricorsivamente.

Spiegazione: setInterval esegue una funzione ripetutamente ogni N millisecondi. Puoi ottenere questo facendo in modo che una funzione chiami se stessa con setTimeout dopo ogni esecuzione. Devi anche avere un modo per cancellarla (myClearInterval).

function mySetInterval(callback, delay, ...args) { const interval = { timerId: null }; function run() { interval.timerId = setTimeout(() => { callback.apply(this, args); run(); // Schedule the next call }, delay); } run(); return interval; // Return an object to allow clearing } function myClearInterval(interval) { clearTimeout(interval.timerId); } // Example usage: let count = 0; const intervalId = mySetInterval(() => { console.log(`Hello: ${++count}`); if (count === 3) myClearInterval(intervalId); }, 500);

#109

Attraversamento del Grafo (BFS & DFS)

Implementa la ricerca in ampiezza (BFS) e la ricerca in profondità (DFS) per un grafo dato (rappresentazione a lista di adiacenza).

Spiegazione: BFS esplora prima i vicini (usando una Coda), mentre DFS esplora il più lontano possibile lungo ogni ramo (usando uno Stack o la ricorsione). Tieni traccia dei nodi visitati per evitare cicli e lavoro ridondante.

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); // Add neighbors in reverse to process them in order later (optional) 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']

#110

Ruota Immagine (Matrice)

Ti viene data una matrice 2D n x n che rappresenta un'immagine. Ruota l'immagine di 90 gradi (in senso orario) sul posto.

Spiegazione: Un modo comune per ottenere questo è prima trasponendo la matrice (scambiando matrix[i][j] con matrix[j][i]) e quindi invertendo ogni riga.

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]]; } } // Reverse each row 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]]

#111

Attraversamento della Matrice a Spirale

Data una matrice m x n, restituisce tutti gli elementi della matrice in ordine a spirale.

Spiegazione: Usa quattro puntatori per definire i confini: top, bottom, left, right. Attraversa la riga superiore, poi la colonna destra, poi la riga inferiore, poi la colonna sinistra, riducendo i confini dopo ogni attraversamento, finché left non incrocia right o top non incrocia 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]

#112

Imposta Zeri nella Matrice

Data una matrice m x n, se un elemento è 0, imposta l'intera sua riga e colonna a 0. Fallo in-place.

Spiegazione: Un approccio ingenuo richiede uno spazio extra di O(m*n). Per farlo in spazio O(1) (o O(m+n)), puoi usare la prima riga e la prima colonna per memorizzare le informazioni su quali righe/colonne devono essere azzerate. Hai bisogno di flag separati per indicare se la prima riga/colonna stessa deve essere azzerata.

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; } // Example: [[1,1,1],[1,0,1],[1,1,1]] => [[1,0,1],[0,0,0],[1,0,1]]

#113

Implementa Promise.race

Implementa una funzione che si comporta come Promise.race.

Spiegazione: Promise.race accetta un array di promise e restituisce una singola promise. Questa promise restituita si risolve (o si rifiuta) non appena una qualsiasi delle promise di input si risolve, con il valore o la ragione di quella promise.

function myPromiseRace(promises) { return new Promise((resolve, reject) => { if (!promises || promises.length === 0) { return; // Or resolve/reject depending on spec } 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'

#114

Pattern Singleton

Implementa il pattern di progettazione Singleton in JavaScript.

Spiegazione: Il pattern Singleton garantisce che una classe abbia una sola istanza e fornisce un punto di accesso globale ad essa. Ciò può essere ottenuto utilizzando una closure per contenere l'istanza.

const Singleton = (function() { let instance; function createInstance() { // Private methods and variables const privateVar = 'I am private'; function privateMethod() { console.log('Private'); } return { // Public methods and variables publicVar: 'I am public', publicMethod: function() { console.log('Public'); }, 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()); // 'I am private'

#115

Valida Indirizzo IP

Scrivi una funzione per verificare se una data stringa è un indirizzo IPv4 o IPv6 valido.

Spiegazione: Per IPv4, controlla che ci siano 4 numeri (0-255) separati da punti, senza zeri iniziali (eccetto '0' stesso). Per IPv6, controlla che ci siano 8 gruppi di 1-4 cifre esadecimali separate da due punti (gestisci le variazioni come '::'). Si possono usare le espressioni regolari, ma l'analisi manuale è spesso più chiara per i colloqui.

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; // Simplified: No '::' 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 (Simplified) console.log(validIPAddress('256.256.256.256')); // Neither

#116

Trova Elemento Picco

Un elemento picco è un elemento strettamente maggiore dei suoi vicini. Data una matrice di input nums, trova un elemento picco e restituisci il suo indice.

Spiegazione: Poiché qualsiasi picco va bene, e nums[-1] e nums[n] sono considerati -Infinito, puoi usare una ricerca binaria modificata. Se nums[mid] è minore di nums[mid+1], un picco deve esistere a destra. Altrimenti, un picco deve esistere a sinistra (o mid stesso è un picco).

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; // Peak is to the right } else { right = mid; // Peak is mid or to the left } } return left; // 'left' will be the index of a peak } console.log(findPeakElement([1, 2, 3, 1])); // 2 (index of 3) console.log(findPeakElement([1, 2, 1, 3, 5, 6, 4])); // 5 (index of 6) or 1 (index of 2)

#117

Conteggio Bit

Dato un intero n, restituisci un array ans di lunghezza n + 1 tale che per ogni i (0 <= i <= n), ans[i] è il numero di 1 nella rappresentazione binaria di i.

Spiegazione: Puoi risolvere questo problema usando la programmazione dinamica. Nota la relazione: dp[i] = dp[i >> 1] + (i & 1). Il numero di 1 in i è il numero di 1 in i spostato a destra (cioè, i/2), più 1 se i è dispari.

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); // Or: dp[i] = dp[i & (i - 1)] + 1; (Removes last set bit) } return dp; } console.log(countBits(5)); // [0, 1, 1, 2, 1, 2]

#118

Potenza di Due

Dato un intero n, restituisci true se è una potenza di due. Altrimenti, restituisci false.

Spiegazione: Una potenza di due nella rappresentazione binaria ha esattamente un bit '1' (ad esempio, 1=1, 2=10, 4=100, 8=1000). Un trucco intelligente a livello di bit è controllare se n > 0 e (n & (n - 1)) === 0. Se n è una potenza di due, n-1 avrà tutti i bit sotto quel '1' impostati a '1'. L'operazione AND tra di essi produce 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

#119

Unisci Intervalli

Dato un array di intervalli dove intervals[i] = [starti, endi], unisci tutti gli intervalli sovrapposti e restituisci un array degli intervalli non sovrapposti.

Spiegazione: Innanzitutto, ordina gli intervalli in base ai loro orari di inizio. Quindi, itera attraverso gli intervalli ordinati. Se l'intervallo corrente si sovrappone con l'ultimo intervallo nell'elenco dei risultati, uniscili aggiornando l'orario di fine dell'ultimo intervallo. Altrimenti, aggiungi l'intervallo corrente all'elenco dei risultati.

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]) { // Overlap: merge last[1] = Math.max(last[1], current[1]); } else { // No overlap: add new interval merged.push(current); } } return merged; } console.log(mergeIntervals([[1, 3], [2, 6], [8, 10], [15, 18]])); // [[1, 6], [8, 10], [15, 18]]

#120

Interruzione di Parola

Data una stringa s e un dizionario di stringhe wordDict, restituisci true se s può essere segmentata in una sequenza separata da spazi di una o più parole del dizionario.

Spiegazione: Usa la programmazione dinamica. Crea un array booleano dp dove dp[i] è true se la sottostringa s[0...i-1] può essere segmentata. dp[i] è true se esiste un j < i tale che dp[j] è true e la sottostringa s[j...i-1] è nel dizionario.

function wordBreak(s, wordDict) { const wordSet = new Set(wordDict); const n = s.length; const dp = new Array(n + 1).fill(false); dp[0] = true; // Base case: empty string 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

#121

Implementa Array.prototype.flat

Implementa la tua versione di Array.prototype.flat, che crea un nuovo array con tutti gli elementi delle sotto-array concatenati ricorsivamente fino a una profondità specificata.

Spiegazione: Usa la ricorsione. Itera attraverso l'array. Se un elemento è un array e la profondità corrente è inferiore alla profondità specificata, chiama ricorsivamente flatten su di esso. Altrimenti, aggiungi l'elemento. Gestisci la profondità predefinita di 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]

#122

Inverti Parole in una Stringa

Data una stringa di input s, inverti l'ordine delle parole.

Spiegazione: Una 'parola' è definita come una sequenza di caratteri non-spazio. Taglia la stringa, dividila per uno o più spazi, filtra le stringhe vuote risultanti da spazi multipli, inverti l'array e uniscilo di nuovo con singoli spazi.

function reverseWords(s) { return s.trim().split(/\s+/).reverse().join(' '); } console.log(reverseWords('the sky is blue')); // 'blue is sky the' console.log(reverseWords(' hello world ')); // 'world hello' console.log(reverseWords('a good example')); // 'example good a'

#123

Parser di Stringa di Query

Scrivi una funzione per analizzare una stringa di query URL in un oggetto.

Spiegazione: Dividi la stringa per &. Per ogni parte, dividi per = per ottenere la chiave e il valore. Decodifica i componenti URI sia per la chiave che per il valore. Gestisci i casi in cui una chiave potrebbe apparire più volte (crea un array) o non ha valore.

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 }

#124

Usa Proxy per la Validazione

Dimostra come usare un oggetto Proxy per la validazione delle proprietà degli oggetti.

Spiegazione: Un Proxy ti permette di intercettare e personalizzare le operazioni eseguite su un oggetto. Usa il trap set per validare i valori prima di assegnarli a una proprietà. Lancia un errore se la validazione fallisce.

const validator = { set: function(obj, prop, value) { if (prop === 'age') { if (!Number.isInteger(value)) { throw new TypeError('The age is not an integer'); } if (value < 0 || value > 150) { throw new RangeError('The age seems invalid'); } } // Default behavior: Set the property obj[prop] = value; return true; } }; const person = {}; const personProxy = new Proxy(person, validator); personProxy.age = 30; // OK console.log(personProxy.age); // 30 // personProxy.age = 'thirty'; // Throws TypeError // personProxy.age = 200; // Throws RangeError

#125

Sale Riunioni

Dato un array di intervalli di tempo di riunioni [[start1, end1], [start2, end2], ...], determina se una persona potrebbe partecipare a tutte le riunioni.

Spiegazione: Se una persona può partecipare a tutte le riunioni, significa che nessuna delle due riunioni si sovrappone. Per verificarlo, ordina gli intervalli in base ai loro orari di inizio. Quindi, scorri gli intervalli ordinati e controlla se l'orario di inizio della riunione corrente è precedente all'orario di fine della riunione precedente. In tal caso, c'è una sovrapposizione e la persona non può partecipare a tutte le riunioni.

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; // Overlap detected } } return true; } console.log(canAttendMeetings([[0, 30], [5, 10], [15, 20]])); // false (5<30) console.log(canAttendMeetings([[7, 10], [2, 4]])); // true

#126

Implementa Promise.any

Implementa una funzione che si comporta come Promise.any.

Spiegazione: Promise.any accetta un array di promise e restituisce una singola promise. Questa promise si risolve non appena una qualsiasi delle promise di input si risolve. Se tutte le promise di input si rifiutano, si rifiuta con un AggregateError contenente tutte le ragioni di rifiuto.

function myPromiseAny(promises) { return new Promise((resolve, reject) => { const numPromises = promises.length; if (numPromises === 0) { reject(new AggregateError([], 'All promises were rejected')); return; } let rejectionCount = 0; const errors = []; promises.forEach((promise, index) => { Promise.resolve(promise) .then(resolve) // Resolve as soon as one fulfills .catch(error => { errors[index] = error; rejectionCount++; if (rejectionCount === numPromises) { reject(new AggregateError(errors, 'All promises were rejected')); } }); }); }); } // Example: myPromiseAny([Promise.reject('err1'), Promise.resolve('ok')]) -> 'ok' // Example: myPromiseAny([Promise.reject('err1'), Promise.reject('err2')]) -> AggregateError

#127

Pattern Osservatore

Implementa il pattern di progettazione Observer (Pub/Sub) in JavaScript.

Spiegazione: Il pattern Observer definisce una dipendenza uno-a-molti tra oggetti. Quando un oggetto (Subject) cambia stato, tutti i suoi dipendenti (Observers) vengono notificati e aggiornati automaticamente. Il Subject mantiene un elenco di osservatori e fornisce metodi per aggiungerli, rimuoverli e notificarli.

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} received: ${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('Something happened!'); // Observer 1 received: Something happened! // Observer 2 received: Something happened!

#128

Trova Numero Duplicato

Dato un array di interi nums contenente n + 1 interi dove ogni intero è nell'intervallo [1, n] inclusivo, trova il numero ripetuto.

Spiegazione: Poiché i numeri sono in [1, n] e la dimensione dell'array è n+1, garantisce almeno un duplicato. Questo può essere mappato a un problema di 'Trova Ciclo in Lista Collegata' (Floyd's Tortoise and Hare). Tratta i valori dell'array come puntatori: index -> nums[index].

function findDuplicate(nums) { let tortoise = nums[0]; let hare = nums[0]; // Phase 1: Find the intersection point do { tortoise = nums[tortoise]; hare = nums[nums[hare]]; } while (tortoise !== hare); // Phase 2: Find the entrance to the cycle 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

#129

Sanitizzatore HTML Base

Scrivi una funzione base per sanitizzare una stringa HTML, rimuovendo tag potenzialmente dannosi (come script) mantenendo tag sicuri (come p, b).

Spiegazione: Questo è un argomento complesso e i sanitizzatori reali sono sofisticati. Per un colloquio, un approccio di base potrebbe comportare l'uso di espressioni regolari per rimuovere tag specifici o consentire solo una whitelist di tag. Questo non è sicuro per la produzione.

function basicSanitize(html) { // WARNING: This is a very basic and insecure example. // Do NOT use this in production. Use a library like DOMPurify. // Remove script tags let sanitized = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, ''); // Example: Remove onclick attributes (very basic) sanitized = sanitized.replace(/\s(on\w+)=['"]?[^'"]*['"]?/gi, ''); return sanitized; } console.log(basicSanitize('<p>Hello <script>alert("XSS")</script> <b onclick="danger()">World</b></p>')); // <p>Hello <b >World</b></p>

#130

Distanza di Modifica

Date due stringhe word1 e word2, restituisci il numero minimo di operazioni (inserimento, eliminazione o sostituzione) necessarie per convertire word1 in word2.

Spiegazione: Questo è un classico problema di programmazione dinamica (distanza di Levenshtein). Crea un array 2D dp dove dp[i][j] è la distanza di modifica tra i primi i caratteri di word1 e i primi j caratteri di word2. La relazione di ricorrenza comporta la considerazione del costo di inserimento, eliminazione e sostituzione.

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, // Deletion dp[i][j - 1] + 1, // Insertion dp[i - 1][j - 1] + cost // Substitution/Match ); } } return dp[m][n]; } console.log(minDistance('horse', 'ros')); // 3 console.log(minDistance('intention', 'execution')); // 5

#131

Sottosequenza Crescente Più Lunga (LIS)

Dato un array di interi nums, restituisci la lunghezza della sottosequenza strettamente crescente più lunga.

Spiegazione: Usa la programmazione dinamica. Sia dp[i] la lunghezza della LIS che termina all'indice i. Per calcolare dp[i], trova tutti i j < i tali che nums[j] < nums[i], e prendi dp[i] = 1 + max(dp[j]). La risposta finale è il valore massimo nell'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)

#132

Problema delle N-Regine

Il puzzle delle N-Regine è il problema di posizionare N regine di scacchi su una scacchiera N×N in modo che nessuna delle due regine si minacci a vicenda. Dato N, restituisci una soluzione valida (o tutte).

Spiegazione: Usa il backtracking. Posiziona le regine riga per riga. Per ogni riga, prova a posizionare una regina in ogni colonna. Prima di posizionare, controlla se la posizione proposta è sicura (non attaccata da regine nelle righe precedenti). Se sicura, posizionala e ricorsivamente chiama per la riga successiva. Se la ricorsione fallisce, torna indietro (rimuovi la regina) e prova la colonna successiva.

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; // Check col for (let i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) if (board[i][j] === 'Q') return false; // Check diag up-left for (let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) if (board[i][j] === 'Q') return false; // Check diag up-right 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] = '.'; // Backtrack } } } backtrack(0); return results; } console.log(solveNQueens(4)); // [ [ '.Q..', '...Q', 'Q...', '..Q.' ], [ '..Q.', 'Q...', '...Q', '.Q..' ] ]

#133

Usa WeakMap per Dati Privati

Dimostra come usare WeakMap per memorizzare dati privati per le istanze di classe.

Spiegazione: WeakMap ti permette di associare dati a un oggetto in un modo che non impedisce la garbage collection se l'oggetto non è più referenziato. Questo è utile per creare membri 'privati' nelle classi JavaScript prima che i campi privati nativi fossero ampiamente disponibili o per casi d'uso specifici.

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' isn't a public property // console.log(privateData.get(person)); // Accessible, but not directly via 'person.'

#134

Implementa Promise.allSettled

Implementa una funzione che si comporta come Promise.allSettled.

Spiegazione: Promise.allSettled accetta un array di promise e restituisce una singola promise. Questa promise si risolve quando tutte le promise di input si sono stabilite (o risolte o rifiutate). Il valore di risoluzione è un array di oggetti, ognuno dei quali descrive l'esito di una promise ({status: 'fulfilled', value: ...} o {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); } }); }); }); } // Example: myPromiseAllSettled([Promise.resolve(1), Promise.reject('err')]) // -> [{status: 'fulfilled', value: 1}, {status: 'rejected', reason: 'err'}]

#135

Trova la Mediana di Due Array Ordinati

Dati due array ordinati nums1 e nums2 di dimensione m e n rispettivamente, restituisci la mediana dei due array ordinati.

Spiegazione: Questo è un problema difficile spesso risolto con un approccio di ricerca binaria sull'array più piccolo. L'obiettivo è partizionare entrambi gli array in modo che tutti gli elementi sul lato sinistro siano minori o uguali a tutti gli elementi sul lato destro, e il numero di elementi sul lato sinistro sia uguale (o uno in più) a quello sul lato destro. La mediana può quindi essere calcolata dagli elementi di confine.

function findMedianSortedArrays(nums1, nums2) { if (nums1.length > nums2.length) [nums1, nums2] = [nums2, nums1]; // Ensure nums1 is smaller 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('Input arrays are not sorted'); } console.log(findMedianSortedArrays([1, 3], [2])); // 2.0 console.log(findMedianSortedArrays([1, 2], [3, 4])); // 2.5

#136

Risolutore di Sudoku

Scrivi un programma per risolvere un puzzle di Sudoku riempiendo le celle vuote.

Spiegazione: Usa il backtracking. Trova una cella vuota. Prova a riempirla con numeri da 1 a 9. Per ogni numero, controlla se è valido (non viola le regole del Sudoku). Se valido, chiama ricorsivamente il risolutore. Se la ricorsione restituisce true, è stata trovata una soluzione. In caso contrario, torna indietro (reimposta la cella) e prova il numero successivo.

function solveSudoku(board) { function isValid(row, col, numStr) { for (let i = 0; i < 9; i++) { if (board[row][i] === numStr) return false; // Check row if (board[i][col] === numStr) return false; // Check col 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; // Check box } 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] = '.'; // Backtrack } } return false; // No valid number found } } } return true; // Board solved } backtrack(); return board; } // Example requires a 9x9 board with '.' for empty cells.

#137

Implementa un Pattern Middleware di Base

Implementa un semplice pattern middleware spesso visto nei framework web.

Spiegazione: Le funzioni middleware elaborano una richiesta prima che raggiunga il gestore finale. Ogni middleware può modificare la richiesta/risposta o passare il controllo al middleware next. Crea una classe o un oggetto che gestisce un elenco di funzioni middleware e le esegue in sequenza.

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: Logging...'); req.logged = true; next(); }); app.use((req, next) => { console.log('2: Authenticating...'); req.authed = true; next(); }); app.use((req, next) => { console.log('3: Final Handler:', req); }); app.handle({ data: 'some request' }); // 1: Logging... // 2: Authenticating... // 3: Final Handler: { data: 'some request', logged: true, authed: true }

#138

Rileva Ciclo nel Grafo Orientato

Dato un grafo orientato, scrivi una funzione per determinare se contiene un ciclo.

Spiegazione: Usa la ricerca in profondità (DFS). Mantieni due insiemi: visiting (nodi attualmente nello stack di ricorsione) e visited (nodi che sono stati completamente esplorati). Se incontri un nodo nell'insieme visiting durante la DFS, hai trovato un arco di ritorno, il che significa che c'è un ciclo.

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; // Cycle detected 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

#139

Implementa il Comportamento di Object.freeze

Spiega Object.freeze e implementa una versione (shallow).

Spiegazione: Object.freeze impedisce l'aggiunta di nuove proprietà, la rimozione di proprietà esistenti e la modifica di proprietà esistenti o della loro enumerabilità, configurabilità o scrivibilità. Rende un oggetto immutabile (in modo shallow). Puoi implementarlo usando Object.preventExtensions e Object.defineProperty per rendere le proprietà esistenti non scrivibili e non configurabili.

function myFreeze(obj) { Object.preventExtensions(obj); // Prevent adding new properties 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; // Fails silently (or throws in strict mode) // delete obj.a; // Fails silently (or throws in strict mode) // obj.a = 10; // Fails silently (or throws in strict mode) console.log(obj); // { a: 1, b: 2 }

#140

Usa requestAnimationFrame per Animazioni Fluide

Spiega requestAnimationFrame e mostra come usarlo per un semplice ciclo di animazione.

Spiegazione: requestAnimationFrame (rAF) dice al browser che desideri eseguire un'animazione e richiede che il browser chiami una funzione specificata per aggiornare un'animazione prima del prossimo repaint. È più efficiente e fluido dell'uso di setTimeout o setInterval per le animazioni, poiché si sincronizza con la frequenza di aggiornamento del browser e si mette in pausa quando la scheda non è visibile.

// 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; // Move 100px in 2 seconds (50px/sec) position = (elapsedTime / 2000) * 100; if (position < 200) { // Keep animating until it reaches 200px box.style.left = position + 'px'; requestAnimationFrame(animate); } else { box.style.left = '200px'; } } // Start the animation // requestAnimationFrame(animate); // Uncomment to run in browser console.log('Use requestAnimationFrame for browser animations.');

#141

Implementa Array.prototype.some

Implementa la tua versione di Array.prototype.some.

Spiegazione: some verifica se almeno un elemento nell'array supera il test implementato dalla funzione fornita. Restituisce true se trova un elemento per cui il callback restituisce true; altrimenti, restituisce false.

function mySome(arr, callback, thisArg) { for (let i = 0; i < arr.length; i++) { if (i in arr) { // Handle sparse arrays 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

#142

Implementa Array.prototype.every

Implementa la tua versione di Array.prototype.every.

Spiegazione: every verifica se tutti gli elementi nell'array superano il test implementato dalla funzione fornita. Restituisce true se tutti gli elementi superano (o se l'array è vuoto), e false se un qualsiasi elemento fallisce.

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

#143

Implementa Array.prototype.findIndex

Implementa la tua versione di Array.prototype.findIndex.

Spiegazione: findIndex restituisce l'indice del primo elemento nell'array che soddisfa la funzione di test fornita. Altrimenti, restituisce -1, indicando che nessun elemento ha superato il test.

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

#144

Cosa sono i Web Workers?

Spiega cosa sono i Web Workers e il loro caso d'uso principale.

Spiegazione: I Web Workers sono un mezzo per il contenuto web per eseguire script in thread in background. Il caso d'uso principale è quello di scaricare attività a lungo termine o computazionalmente intensive dal thread principale (che gestisce gli aggiornamenti dell'interfaccia utente e le interazioni con l'utente), prevenendo così che l'interfaccia utente diventi non responsiva o 'si blocchi'. I Worker comunicano con il thread principale tramite passaggio di messaggi (postMessage e onmessage).

// main.js /* if (window.Worker) { const myWorker = new Worker('worker.js'); myWorker.postMessage([5, 3]); // Send data to worker myWorker.onmessage = function(e) { console.log('Result from worker:', e.data); } } else { console.log('Your browser does not support Web Workers.'); } */ // worker.js /* self.onmessage = function(e) { console.log('Message received from main script'); const result = e.data[0] * e.data[1]; console.log('Posting result back to main script'); self.postMessage(result); } */ console.log('Web Workers run scripts in background threads.');

#145

Decodifica Modi

Un messaggio contenente lettere dalla A-Z può essere codificato in numeri usando la mappatura 'A' -> 1, 'B' -> 2, ..., 'Z' -> 26. Data una stringa s contenente solo cifre, restituisci il numero di modi per decodificarla.

Spiegazione: Usa la programmazione dinamica. Sia dp[i] il numero di modi per decodificare s[0...i-1]. dp[i] dipende da dp[i-1] (se s[i-1] è un codice valido di 1 cifra) e dp[i-2] (se s[i-2...i-1] è un codice valido di 2 cifre).

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' or 'L') console.log(numDecodings('226')); // 3 ('BBF', 'BZ', 'VF') console.log(numDecodings('06')); // 0

#146

Addizione Bitwise (senza +)

Scrivi una funzione per sommare due interi senza usare l'operatore +.

Spiegazione: Usa gli operatori bitwise. La somma può essere calcolata come a ^ b (somma senza riporto), e il riporto può essere calcolato come (a & b) << 1. Ripeti questo processo finché il riporto diventa 0.

function getSum(a, b) { while (b !== 0) { const carry = (a & b) << 1; // Calculate carry a = a ^ b; // Calculate sum without carry b = carry; // Carry becomes the new 'b' } return a; } console.log(getSum(3, 5)); // 8 console.log(getSum(-2, 3)); // 1

#147

Verifica Palindromo (Numero)

Dato un intero x, restituisci true se x è un palindromo, e false altrimenti.

Spiegazione: Un numero negativo non è un palindromo. Converti il numero in una stringa e controlla se la stringa è un palindromo (si legge allo stesso modo in avanti e all'indietro). In alternativa, inverti il numero matematicamente e confronta.

function isPalindromeNumber(x) { if (x < 0) return false; // String approach // return String(x) === String(x).split('').reverse().join(''); // Mathematical approach 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

#148

Pattern Factory

Implementa il pattern di progettazione Factory in JavaScript.

Spiegazione: Il pattern Factory fornisce un'interfaccia per la creazione di oggetti in una superclasse, ma consente alle sottoclassi di alterare il tipo di oggetti che verranno creati. È utile per creare oggetti senza specificare la classe esatta.

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('Unknown vehicle type'); } } } 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' }

#149

Spiega Symbol e un Caso d'Uso

Spiega cos'è Symbol in JavaScript e fornisce un caso d'uso, come la creazione di proprietà 'private' o chiavi di oggetti uniche.

Spiegazione: Symbol è un tipo di dato primitivo introdotto in ES6. Le sue istanze sono uniche e immutabili. Sono spesso usate come chiavi per le proprietà degli oggetti per evitare collisioni di nomi tra diverse librerie o per creare proprietà pseudo-private (anche se non veramente private, non sono accessibili tramite l'iterazione tipica o Object.keys).

const _privateName = Symbol('name'); const _privateMethod = Symbol('greet'); class Person { constructor(name) { this[_privateName] = name; } [_privateMethod]() { console.log(`Hello, my name is ${this[_privateName]}`); } introduce() { this[_privateMethod](); } } const p = new Person('Bob'); p.introduce(); // Hello, my name is Bob console.log(Object.keys(p)); // [] - Symbols are not included console.log(p[_privateName]); // Bob (Accessible if you have the Symbol)

#150

Converti arguments in un Vero Array

Come puoi convertire l'oggetto arguments (disponibile nelle funzioni non freccia) in un vero array JavaScript?

Spiegazione: L'oggetto arguments sembra un array ma non lo è; gli mancano metodi come map, filter, ecc. Puoi convertirlo usando:

  1. Array.from(arguments) (ES6)
  2. [...arguments] (Sintassi Spread ES6)
  3. Array.prototype.slice.call(arguments) (Modo più vecchio)
function sumAll() { // 1. Array.from const args1 = Array.from(arguments); console.log('Using Array.from:', args1.reduce((a, b) => a + b, 0)); // 2. Spread Syntax const args2 = [...arguments]; console.log('Using Spread:', args2.reduce((a, b) => a + b, 0)); // 3. Slice const args3 = Array.prototype.slice.call(arguments); console.log('Using Slice:', args3.reduce((a, b) => a + b, 0)); } sumAll(1, 2, 3, 4); // Using Array.from: 10 // Using Spread: 10 // Using Slice: 10