Live Coding

Vérifier si un objet est vide

Comment vérifier si un objet JavaScript est vide ?

Explication : Un objet est vide s'il n'a pas de propriétés énumérables propres. Nous pouvons utiliser Object.keys(obj) qui renvoie un tableau des noms de propriétés énumérables propres d'un objet donné. Si la longueur de ce tableau est 0, l'objet est vide.

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

Inverser une chaîne de caractères

Écrire une fonction pour inverser une chaîne de caractères donnée.

Explication : Le moyen le plus simple est de convertir la chaîne en un tableau de caractères, d'utiliser la méthode reverse() intégrée pour les tableaux, puis de joindre les caractères pour reformer une chaîne.

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

Vérification de Palindrome

Écrire une fonction qui vérifie si une chaîne de caractères donnée est un palindrome.

Explication : Un palindrome est un mot ou une phrase qui se lit de la même manière dans les deux sens. Nous pouvons le vérifier en inversant la chaîne (en ignorant la casse et les caractères non alphanumériques pour une vérification plus robuste, mais ici nous faisons une vérification simple) et en la comparant à l'originale.

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

Trouver le nombre maximum dans un tableau

Écrire une fonction pour trouver le plus grand nombre dans un tableau de nombres.

Explication : Vous pouvez parcourir le tableau en gardant une trace du plus grand nombre trouvé jusqu'à présent. Alternativement, vous pouvez utiliser la fonction Math.max() avec l'opérateur de décomposition (...) pour passer les éléments du tableau comme arguments.

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

FizzBuzz

Écrire un programme qui imprime les nombres de 1 à n. Mais pour les multiples de trois, imprimer 'Fizz' au lieu du nombre et pour les multiples de cinq, imprimer 'Buzz'. Pour les nombres qui sont des multiples de trois et de cinq, imprimer 'FizzBuzz'.

Explication : Ce problème classique teste la logique de base des boucles et des conditions. Vous devez itérer de 1 à n et utiliser l'opérateur modulo (%) pour vérifier la divisibilité par 3 et 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);

Supprimer les doublons d'un tableau

Écrire une fonction qui supprime les éléments en double d'un tableau.

Explication : Une façon moderne et concise d'y parvenir est d'utiliser un Set. Les ensembles ne stockent que des valeurs uniques. Vous pouvez convertir le tableau en un ensemble, puis le reconvertir en un tableau.

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

Vérification d'anagramme

Écrire une fonction pour vérifier si deux chaînes de caractères sont des anagrammes l'une de l'autre.

Explication : Les anagrammes sont des mots formés en réorganisant les lettres d'un autre mot. Pour vérifier, nous pouvons nettoyer, trier et comparer les chaînes. Le nettoyage implique la suppression des caractères non alphanumériques et la conversion dans une casse cohérente.

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

Calculer la factorielle

Écrire une fonction pour calculer la factorielle d'un entier non négatif.

Explication : La factorielle (n !) d'un nombre est le produit de tous les entiers positifs inférieurs ou égaux à n. Nous pouvons le calculer de manière itérative en multipliant les nombres de 1 à n.

function factorial(n) { if (n < 0) return undefined; // La factorielle n'est pas définie pour les nombres négatifs if (n === 0) return 1; let result = 1; for (let i = 1; i <= n; i++) { result *= i; } return result; } console.log(factorial(5)); // 120

Sommer tous les nombres d'un tableau

Écrire une fonction qui renvoie la somme de tous les nombres d'un tableau.

Explication : Vous pouvez utiliser la méthode reduce, qui est idéale pour accumuler une seule valeur à partir d'un tableau. Elle prend une fonction de rappel et une valeur initiale (0 pour la somme).

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

Aplatir un tableau imbriqué

Écrire une fonction pour aplatir un tableau imbriqué. Pour simplifier, n'assurez qu'un seul niveau d'imbrication.

Explication : Pour un seul niveau d'imbrication, vous pouvez utiliser Array.prototype.concat() avec l'opérateur de décomposition ou la méthode flat() (ES2019).

function flattenArray(arr) { // Utilisation de flat() (plus simple si ES2019+ est acceptable) // return arr.flat(); // Utilisation de reduce et concat return arr.reduce((acc, val) => acc.concat(val), []); } console.log(flattenArray([1, [2, 3], 4, [5]])); // [1, 2, 3, 4, 5]

Compter les voyelles dans une chaîne

Écrire une fonction qui compte le nombre de voyelles (a, e, i, o, u) dans une chaîne donnée.

Explication : Parcourir la chaîne (sans tenir compte de la casse) et vérifier si chaque caractère est une voyelle. Maintenir un compteur.

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

Mettre une phrase en casse de titre

Écrire une fonction qui convertit une phrase en casse de titre (la première lettre de chaque mot est en majuscule).

Explication : Diviser la phrase en mots, puis parcourir chaque mot, en mettant la première lettre en majuscule et le reste en minuscule. Enfin, joindre les mots.

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'

Problème des deux sommes

Étant donné un tableau d'entiers nums et un entier target, renvoyer les indices des deux nombres dont la somme est égale à target.

Explication : Une approche courante consiste à utiliser une table de hachage (ou un objet JavaScript) pour stocker les nombres et leurs indices au fur et à mesure que vous itérez. Pour chaque nombre, vérifiez si target - current_number existe dans la table.

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 []; // Ou null, ou lancer une erreur si pas de solution } console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1]

Implémenter un compteur avec des fermetures

Créer une fonction qui renvoie une fonction de compteur. Chaque fois que la fonction renvoyée est appelée, elle doit incrémenter et renvoyer un compte.

Explication : Ceci démontre les fermetures (closures). La fonction interne a accès à la variable count de la portée de la fonction externe, même après que la fonction externe a terminé son exécution.

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

Trouver le premier caractère non répété

Écrire une fonction qui trouve le premier caractère non répété dans une chaîne.

Explication : Vous pouvez utiliser une table de hachage pour compter les fréquences des caractères. Tout d'abord, parcourez la chaîne pour construire la table de fréquences. Ensuite, parcourez à nouveau la chaîne et renvoyez le premier caractère avec un compte de 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; // Ou un indicateur si tous les caractères se répètent } console.log(firstNonRepeatingChar('aabbcdeeff')); // 'c' console.log(firstNonRepeatingChar('swiss')); // 'w'

Découpage de tableau (Array Chunking)

Écrire une fonction qui divise un tableau en groupes d'une taille spécifiée.

Explication : Parcourir le tableau, en prenant des tranches de la taille spécifiée et en les poussant dans un nouveau tableau. Gérer le cas où le dernier morceau pourrait être plus petit.

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

Vérifier si un nombre est premier

Écrire une fonction pour déterminer si un nombre donné est un nombre premier.

Explication : Un nombre premier est un nombre naturel supérieur à 1 qui n'a pas d'autres diviseurs positifs que 1 et lui-même. Itérer de 2 jusqu'à la racine carrée du nombre, en vérifiant la divisibilité. Gérer les cas limites comme 1 et 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

Trouver le mot le plus long dans une chaîne

Écrire une fonction qui trouve le mot le plus long dans une phrase.

Explication : Diviser la chaîne en un tableau de mots. Ensuite, parcourir le tableau, en gardant une trace du mot le plus long trouvé jusqu'à présent (ou de sa longueur).

function findLongestWord(str) { const words = str.split(' '); let longestWord = ''; for (const word of words) { // Nettoyer les mots si nécessaire (par exemple, supprimer la ponctuation) if (word.length > longestWord.length) { longestWord = word; } } return longestWord; } console.log(findLongestWord('The quick brown fox jumped over the lazy dog')); // 'jumped'

Implémenter `Array.prototype.map`

Implémenter votre propre version de la fonction Array.prototype.map.

Explication : La fonction map prend une fonction de rappel et crée un nouveau tableau en appliquant la fonction de rappel à chaque élément du tableau original. Votre fonction doit parcourir le tableau et construire le nouveau tableau.

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]

Implémenter `Array.prototype.filter`

Implémenter votre propre version de la fonction Array.prototype.filter.

Explication : La fonction filter prend une fonction de rappel et crée un nouveau tableau avec tous les éléments qui réussissent le test implémenté par la fonction de rappel fournie.

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

Implémenter `Array.prototype.reduce`

Implémenter votre propre version de la fonction Array.prototype.reduce.

Explication : La fonction reduce exécute une fonction de rappel 'reducer' fournie par l'utilisateur sur chaque élément du tableau, en transmettant la valeur de retour du calcul sur l'élément précédent. Le résultat final de l'exécution du reducer sur tous les éléments du tableau est une seule valeur. Gérer l'argument de la valeur initiale.

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

Mémoïsation - Suite de Fibonacci

Implémenter une fonction Fibonacci en utilisant la mémoïsation pour optimiser les performances.

Explication : La suite de Fibonacci implique des calculs répétés. La mémoïsation stocke les résultats des appels de fonction coûteux et renvoie le résultat mis en cache lorsque les mêmes entrées se produisent à nouveau.

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 (beaucoup plus rapide que sans mémoïsation)

Vérifier les crochets équilibrés

Écrire une fonction pour vérifier si une chaîne contenant des crochets {}[]() est équilibrée.

Explication : Utiliser une pile. Lorsque vous rencontrez un crochet ouvrant, poussez-le sur la pile. Lorsque vous rencontrez un crochet fermant, vérifiez si la pile est vide ou si l'élément supérieur est le crochet ouvrant correspondant. S'il correspond, retirez-le de la pile. Sinon, ou si la pile est vide, elle est déséquilibrée. À la fin, une pile vide signifie qu'elle est équilibrée.

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

Implémenter une file d'attente simple

Implémenter une structure de données File d'attente avec les méthodes enqueue (ajouter à la fin) et dequeue (retirer du début).

Explication : Une file d'attente suit le principe du Premier entré, premier sorti (FIFO). Un tableau peut être utilisé, avec push pour enqueue et shift pour 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

Implémenter une pile simple

Implémenter une structure de données Pile avec les méthodes push (ajouter au sommet) et pop (retirer du sommet).

Explication : Une pile suit le principe du Dernier entré, premier sorti (LIFO). Un tableau peut être utilisé, avec les méthodes push et 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

Trouver le nombre manquant dans une séquence

Étant donné un tableau contenant n nombres distincts pris de 0, 1, 2, ..., n, trouver celui qui manque dans le tableau.

Explication : La somme des nombres de 0 à n peut être calculée à l'aide de la formule n*(n+1)/2. La somme réelle des éléments du tableau peut être calculée. La différence entre ces deux sommes sera le nombre manquant.

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

Fonction de démultiplication (Debounce)

Implémenter une fonction de démultiplication. La démultiplication garantit qu'une fonction n'est pas rappelée avant qu'un certain temps ne se soit écoulé sans qu'elle ne soit appelée.

Explication : Utiliser setTimeout et clearTimeout. Chaque fois que la fonction démultipliée est appelée, effacer le délai d'attente précédent et en définir un nouveau. L'appel de fonction réel ne se produit que lorsque le délai d'attente est terminé.

function debounce(func, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => { func.apply(this, args); }, delay); }; } // Exemple d'utilisation : const sayHello = () => console.log('Hello!'); const debouncedHello = debounce(sayHello, 1000); debouncedHello(); // Appelé après 1 sec (si non appelé à nouveau) debouncedHello(); // Réinitialise le timer debouncedHello(); // Réinitialise le timer, s'exécutera réellement 1 sec après cet appel.

Fonction de limitation (Throttle)

Implémenter une fonction de limitation. La limitation garantit qu'une fonction est appelée au plus une fois dans un intervalle de temps spécifié.

Explication : Utiliser un drapeau pour indiquer si un appel est autorisé. Lorsqu'elle est appelée, si elle est autorisée, exécuter la fonction, mettre le drapeau à faux et utiliser setTimeout pour réinitialiser le drapeau après l'intervalle.

function throttle(func, limit) { let inThrottle = false; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } // Exemple d'utilisation : const logScroll = () => console.log('Scrolling...'); const throttledScroll = throttle(logScroll, 1000); // window.addEventListener('scroll', throttledScroll); // Se connectera au plus une fois par seconde.

Fonction de Currying

Écrire une fonction qui prend une fonction avec deux arguments et renvoie une version curried de celle-ci.

Explication : Le currying transforme une fonction avec plusieurs arguments en une séquence de fonctions, chacune prenant un seul argument. f(a, b) devient 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

Clonage profond d'un objet

Écrire une fonction pour effectuer un clonage profond d'un objet JavaScript.

Explication : Un clonage superficiel ne copie que les propriétés de niveau supérieur. Un clonage profond copie récursivement toutes les propriétés, y compris les objets et les tableaux imbriqués. Un moyen simple (avec des limitations, par exemple, ne gère pas bien les fonctions, les dates, les expressions régulières) est d'utiliser JSON.parse(JSON.stringify(obj)). Une approche récursive est plus robuste.

function deepClone(obj) { // Version simple (avec des limitations) try { return JSON.parse(JSON.stringify(obj)); } catch (e) { console.error("Le clonage a échoué :", e); return null; } // Récursive plus robuste (exemple de base) : /* 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 (prouve que c'est un clone profond) console.log(cloned.b.c); // 3

Obtenir les clés d'un objet

Comment obtenir un tableau de clés à partir d'un objet ?

Explication : Utiliser Object.keys(obj).

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

Obtenir les valeurs d'un objet

Comment obtenir un tableau de valeurs à partir d'un objet ?

Explication : Utiliser Object.values(obj).

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

Vérifier si un tableau inclut une valeur

Comment vérifier si un tableau contient une valeur spécifique ?

Explication : Utiliser Array.prototype.includes(value).

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

Fusionner deux tableaux

Comment fusionner deux tableaux en un seul ?

Explication : Utiliser l'opérateur de décomposition (...) ou Array.prototype.concat().

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

Expliquer 'this' dans la portée globale

À quoi fait référence this dans la portée globale dans un navigateur ?

Explication : Dans le contexte d'exécution global (en dehors de toute fonction), this fait référence à l'objet global, qui est window dans les navigateurs web.

console.log(this === window); // true (dans un environnement de navigateur)

Expliquer 'this' dans une méthode d'objet

À quoi fait référence this lorsqu'il est utilisé à l'intérieur d'une méthode d'objet ?

Explication : Lorsqu'une fonction est appelée comme méthode d'un objet, this fait référence à l'objet sur lequel la méthode est appelée.

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

Expliquer 'this' avec les fonctions fléchées

Comment les fonctions fléchées gèrent-elles this ?

Explication : Les fonctions fléchées n'ont pas leur propre contexte this. Au lieu de cela, elles héritent de this de la portée englobante (lexicale) où elles sont définies.

function MyClass() { this.value = 42; setTimeout(() => { console.log(this.value); // Affiche 42 car 'this' est hérité }, 100); } new MyClass();

Différence entre `let`, `const` et `var`

Quelles sont les principales différences entre let, const et var ?

Explication : var a une portée fonctionnelle et est remonté (hoisted). let et const ont une portée de bloc et sont remontés mais se trouvent dans une 'zone morte temporelle' jusqu'à la déclaration. const doit être initialisé et ne peut pas être réaffecté.

function scopeTest() { var a = 1; let b = 2; const c = 3; if (true) { var a = 10; // Redéclare et affecte le 'a' extérieur let b = 20; // Nouveau 'b' dans le bloc // const c = 30; // Serait un nouveau 'c' console.log(a, b, c); // 10, 20, 3 } console.log(a, b, c); // 10, 2, 3 } scopeTest();

Qu'est-ce qu'une promesse ?

Expliquer ce qu'est une Promesse en JavaScript.

Explication : Une Promesse est un objet représentant l'achèvement éventuel (ou l'échec) d'une opération asynchrone et sa valeur résultante. Elle peut être dans l'un des trois états : en attente (pending), résolue (fulfilled) ou rejetée (rejected).

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

Utilisation de `async/await`

Comment async et await simplifient-ils le travail avec les Promesses ?

Explication : async/await fournit un sucre syntaxique sur les Promesses, rendant le code asynchrone plus proche du code synchrone. Une fonction async renvoie toujours une Promesse, et await interrompt l'exécution jusqu'à ce qu'une Promesse soit réglée.

function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function run() { console.log('Démarrage...'); await delay(1000); console.log('Attendu 1 seconde.'); await delay(500); console.log('Attendu 0,5 seconde de plus.'); return 'Terminé !'; } run().then(console.log);

Convertir une chaîne en nombre

Comment convertir une chaîne de caractères en un nombre ?

Explication : Utiliser parseInt(), parseFloat() ou l'opérateur unaire plus (+).

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

Convertir un nombre en chaîne

Comment convertir un nombre en chaîne de caractères ?

Explication : Utiliser String(), number.toString() ou la concaténation de chaînes.

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

Qu'est-ce que `JSON.stringify` ?

Que fait JSON.stringify ?

Explication : Il convertit un objet ou une valeur JavaScript en une chaîne JSON.

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

Qu'est-ce que `JSON.parse` ?

Que fait JSON.parse ?

Explication : Il analyse une chaîne JSON, construisant la valeur ou l'objet JavaScript décrit par la chaîne.

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

Opérateur de décomposition dans les tableaux

Comment l'opérateur de décomposition est-il utilisé avec les tableaux ?

Explication : Il permet d'étendre un itérable (comme un tableau) dans des endroits où zéro ou plusieurs arguments ou éléments sont attendus. Utile pour copier, fusionner et ajouter des éléments.

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

Opérateur de décomposition dans les objets

Comment l'opérateur de décomposition est-il utilisé avec les objets ?

Explication : Il permet de copier et de fusionner des propriétés d'objet.

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 }

Déstructuration de tableaux

Expliquer la déstructuration de tableaux avec un exemple.

Explication : C'est une syntaxe qui permet de déballer des valeurs de tableaux dans des variables distinctes.

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

Déstructuration d'objets

Expliquer la déstructuration d'objets avec un exemple.

Explication : Elle permet de déballer des propriétés d'objets dans des variables distinctes.

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'

Implémenter `call`

Comment pourriez-vous implémenter une version de base de Function.prototype.call ?

Explication : call invoque une fonction avec une valeur this spécifiée et des arguments fournis individuellement. Vous pouvez attacher la fonction au contexte this, l'appeler, puis la supprimer.

Function.prototype.myCall = function(context, ...args) { context = context || window; const uniqueId = Symbol(); // Utiliser une clé unique 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', '!'); // Salut, Charlie !

Implémenter `apply`

Comment pourriez-vous implémenter une version de base de Function.prototype.apply ?

Explication : apply est similaire à call, mais il accepte les arguments sous forme de tableau.

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', '.']); // Bonjour, David.

Expliquer la boucle d'événements (Event Loop)

Expliquez brièvement la boucle d'événements JavaScript.

Explication : JavaScript est à thread unique, mais il réalise la concurrence en utilisant une boucle d'événements. La pile d'appels gère le code synchrone. Les API Web gèrent les opérations asynchrones (comme setTimeout, fetch). Lorsqu'une opération asynchrone est terminée, son rappel est placé dans la file d'attente des rappels (ou file d'attente des microtâches pour les Promesses). La boucle d'événements vérifie constamment si la pile d'appels est vide ; si c'est le cas, elle déplace le rappel suivant de la file d'attente vers la pile pour exécution.

console.log('Start'); setTimeout(() => { console.log('Timeout Callback'); // Va à la file d'attente des rappels }, 0); Promise.resolve().then(() => { console.log('Promise Resolved'); // Va à la file d'attente des microtâches }); console.log('End'); // Ordre de sortie : Start, End, Promise Resolved, Timeout Callback // (Les microtâches s'exécutent avant les macrotâches/rappels)

Recherche binaire

Implémenter une fonction de recherche binaire pour un tableau trié.

Explication : La recherche binaire trouve efficacement un élément dans un tableau trié en divisant à plusieurs reprises l'intervalle de recherche en deux. Si la valeur de la clé de recherche est inférieure à l'élément au milieu de l'intervalle, réduisez l'intervalle à la moitié inférieure. Sinon, réduisez-le à la moitié supérieure. Vous faites cela jusqu'à ce que la valeur soit trouvée ou que l'intervalle soit vide.

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; // Trouvé } else if (sortedArray[middle] < key) { start = middle + 1; // Rechercher la moitié droite } else { end = middle - 1; // Rechercher la moitié gauche } } return -1; // Non trouvé } console.log(binarySearch([1, 3, 5, 7, 9, 11], 7)); // 3 console.log(binarySearch([1, 3, 5, 7, 9, 11], 2)); // -1

Tri fusion (Merge Sort)

Implémenter l'algorithme de tri fusion.

Explication : Le tri fusion est un algorithme diviser pour régner. Il divise le tableau d'entrée en deux moitiés, s'appelle lui-même pour les deux moitiés, puis fusionne les deux moitiés triées. La fonction de fusion est cruciale ; elle combine deux sous-tableaux triés en un seul tableau trié.

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]

Tri rapide (Quick Sort)

Implémenter l'algorithme de tri rapide.

Explication : Le tri rapide est également un algorithme diviser pour régner. Il choisit un élément comme pivot et partitionne le tableau donné autour du pivot choisi. Les éléments plus petits que le pivot vont à gauche, et les éléments plus grands vont à droite. Il trie ensuite récursivement les sous-tableaux.

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]

Tri à bulles (Bubble Sort)

Implémenter l'algorithme de tri à bulles.

Explication : Le tri à bulles est un algorithme de tri simple qui parcourt à plusieurs reprises la liste, compare les éléments adjacents et les échange s'ils sont dans le mauvais ordre. Le passage dans la liste est répété jusqu'à ce que la liste soit triée.

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]]; // Échange swapped = true; } } n--; // Optimisation : le dernier élément est déjà en place } while (swapped); return arr; } console.log(bubbleSort([64, 34, 25, 12, 22, 11, 90])); // [11, 12, 22, 25, 34, 64, 90]

Plus longue sous-chaîne sans caractères répétés

Étant donné une chaîne de caractères, trouver la longueur de la plus longue sous-chaîne sans caractères répétés.

Explication : Utiliser la technique de la 'fenêtre glissante'. Maintenir une fenêtre (sous-chaîne) et un ensemble de caractères dans cette fenêtre. Étendre la fenêtre en déplaçant le pointeur droit. Si un caractère répété est trouvé, réduire la fenêtre par la gauche jusqu'à ce que le caractère répété soit supprimé.

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

Implémenter une liste chaînée

Implémenter une liste chaînée simple avec les méthodes add et print.

Explication : Une liste chaînée est une structure de données linéaire où les éléments ne sont pas stockés à des emplacements mémoire contigus. Chaque élément (nœud) pointe vers le suivant. Vous avez besoin d'une classe Node et d'une classe LinkedList pour gérer la tête et l'ajout de nouveaux nœuds.

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

Implémenter un arbre binaire de recherche (ABR)

Implémenter un arbre binaire de recherche avec les méthodes insert et find.

Explication : Un ABR est une structure de données d'arbre binaire basée sur les nœuds qui possède les propriétés suivantes : le sous-arbre gauche d'un nœud ne contient que des nœuds avec des clés inférieures à la clé du nœud. Le sous-arbre droit d'un nœud ne contient que des nœuds avec des clés supérieures à la clé du nœud. Les deux sous-arbres doivent également être des arbres binaires de recherche.

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

Faire pivoter un tableau

Écrire une fonction pour faire pivoter un tableau vers la droite de k étapes.

Explication : Faire pivoter un tableau signifie déplacer ses éléments. Pour une rotation à droite, le dernier élément devient le premier. Répéter cela k fois fonctionne mais est inefficace. Une meilleure façon est d'utiliser la manipulation de tableau ou de calculer les nouvelles positions.

function rotateArray(nums, k) { k = k % nums.length; // Gérer les cas où k > longueur 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]

Trouver l'intersection de deux tableaux

Étant donné deux tableaux, écrire une fonction pour calculer leur intersection (éléments communs aux deux).

Explication : Une bonne approche consiste à convertir un tableau en un Set pour des recherches en temps moyen de O(1). Ensuite, itérer sur le deuxième tableau et vérifier si chaque élément existe dans l'ensemble. Collecter les correspondances.

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]

Regrouper les anagrammes

Étant donné un tableau de chaînes de caractères, regrouper les anagrammes ensemble.

Explication : La clé est de trouver une signature unique pour chaque groupe d'anagrammes. Une façon courante est de trier chaque mot alphabétiquement. Les mots qui donnent la même chaîne triée sont des anagrammes. Utiliser une table de hachage où les clés sont des mots triés et les valeurs sont des tableaux de mots originaux.

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'])); // Sortie : [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]

Déplacer les zéros à la fin

Étant donné un tableau nums, écrire une fonction pour déplacer tous les 0 à la fin de celui-ci tout en maintenant l'ordre relatif des éléments non nuls.

Explication : Utiliser une approche à 'deux pointeurs' ou 'boule de neige'. Un pointeur garde la trace de la position où le prochain élément non nul doit aller. Parcourir le tableau ; si un élément est non nul, placez-le à la position du pointeur et incrémentez le pointeur. Enfin, remplissez le reste avec des zéros.

function moveZeroes(nums) { let nonZeroIndex = 0; // Déplacer tous les éléments non nuls vers l'avant for (let i = 0; i < nums.length; i++) { if (nums[i] !== 0) { nums[nonZeroIndex] = nums[i]; nonZeroIndex++; } } // Remplir le reste avec des zéros 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]

Sous-tableau maximal (Algorithme de Kadane)

Étant donné un tableau d'entiers nums, trouver le sous-tableau contigu (contenant au moins un nombre) qui a la plus grande somme et renvoyer sa somme.

Explication : L'algorithme de Kadane est un moyen efficace de résoudre ce problème. Parcourir le tableau en gardant la trace de la somme currentMax se terminant à la position actuelle et de la somme globalMax trouvée jusqu'à présent. Si currentMax devient négatif, réinitialisez-le à 0 (ou plutôt, à l'élément actuel).

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

Permutations d'une chaîne

Écrire une fonction pour générer toutes les permutations d'une chaîne donnée.

Explication : Ceci est généralement résolu en utilisant la récursion et le retour sur trace (backtracking). Pour chaque caractère, fixez-le et générez récursivement les permutations pour le reste de la chaîne. Cas de base : lorsque la chaîne n'a qu'un seul caractère, renvoyez-le.

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)]; // Utiliser Set pour gérer les caractères en double si nécessaire } console.log(stringPermutations('abc')); // ['abc', 'acb', 'bac', 'bca', 'cab', 'cba'] console.log(stringPermutations('aab')); // ['aab', 'aba', 'baa']

Plus grand commun diviseur (PGCD)

Écrire une fonction pour trouver le Plus Grand Commun Diviseur (PGCD) de deux nombres.

Explication : L'algorithme d'Euclide est une méthode efficace. Si b est 0, a est le PGCD. Sinon, le PGCD de a et b est le même que le PGCD de b et a % b (le reste de a divisé par 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

Plus petit commun multiple (PPCM)

Écrire une fonction pour trouver le Plus Petit Commun Multiple (PPCM) de deux nombres.

Explication : Le PPCM peut être calculé en utilisant le PGCD avec la formule : PPCM(a, b) = |a * b| / PGCD(a, b).

function gcd(a, b) { // Aide du problème précédent 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

Implémenter Promise.all

Implémenter une fonction qui se comporte comme Promise.all.

Explication : Promise.all prend un tableau de promesses et renvoie une seule promesse qui se résout lorsque toutes les promesses d'entrée ont été résolues, ou se rejette si l'une des promesses d'entrée se rejette. La valeur résolue est un tableau des valeurs résolues.

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); // Rejeter immédiatement en cas d'erreur }); }); } // Exemple d'utilisation : 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']

Inverser un arbre binaire

Écrire une fonction pour inverser un arbre binaire.

Explication : Pour inverser un arbre binaire, vous échangez les enfants gauche et droit pour chaque nœud. Cela peut être fait récursivement ou itérativement (en utilisant une file d'attente ou une pile).

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; } // Échanger les enfants [root.left, root.right] = [root.right, root.left]; // Récurrence pour les enfants gauche et droit invertTree(root.left); invertTree(root.right); return root; } // Exemple : 4 -> [2, 7] -> [1, 3, 6, 9] // Devient : 4 -> [7, 2] -> [9, 6, 3, 1]

Profondeur maximale d'un arbre binaire

Étant donné un arbre binaire, trouver sa profondeur maximale.

Explication : La profondeur maximale est le nombre de nœuds le long du chemin le plus long du nœud racine jusqu'au nœud feuille le plus éloigné. Cela peut être trouvé récursivement : ProfondeurMax = 1 + max(ProfondeurMax(gauche), ProfondeurMax(droite)). Le cas de base est lorsqu'un nœud est nul, sa profondeur est 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; } // Exemple : 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

Meilleur moment pour acheter/vendre des actions

Vous recevez un tableau pricesprices[i] est le prix d'une action donnée le i-ème jour. Vous voulez maximiser votre profit en choisissant un seul jour pour acheter une action et en choisissant un jour différent dans le futur pour vendre cette action. Renvoyer le profit maximum.

Explication : Parcourir les prix, en gardant une trace du prix minimum rencontré jusqu'à présent (minPrice) et du profit maximum trouvé jusqu'à présent (maxProfit). Pour chaque jour, calculer le profit potentiel si vous vendiez aujourd'hui (prix actuel - minPrice) et mettre à jour maxProfit s'il est plus élevé.

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 (Acheter à 1, Vendre à 6) console.log(maxProfit([7, 6, 4, 3, 1])); // 0 (Aucun profit possible)

Nombre unique

Étant donné un tableau non vide d'entiers nums, chaque élément apparaît deux fois sauf un. Trouver ce seul élément.

Explication : Un moyen très efficace de résoudre ce problème est d'utiliser l'opérateur de bits XOR (^). XORer un nombre avec lui-même donne 0. XORer un nombre avec 0 donne le nombre lui-même. Si vous XORez tous les nombres du tableau, les paires s'annuleront (deviendront 0), ne laissant que le nombre unique.

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

Élément majoritaire

Étant donné un tableau nums de taille n, renvoyer l'élément majoritaire. L'élément majoritaire est l'élément qui apparaît plus de ⌊n / 2⌋ fois.

Explication : L'algorithme de vote de Boyer-Moore est un moyen efficace. Initialiser un candidat et un compteur. Parcourir le tableau. Si le compteur est 0, définir l'élément actuel comme candidat. Si l'élément actuel correspond au candidat, incrémenter le compteur ; sinon, décrémenter le compteur.

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

Escalader les escaliers

Vous montez un escalier. Il faut n marches pour atteindre le sommet. Chaque fois, vous pouvez monter 1 ou 2 marches. De combien de manières différentes pouvez-vous atteindre le sommet ?

Explication : Il s'agit d'un problème classique de programmation dynamique, très similaire à la suite de Fibonacci. Le nombre de façons d'atteindre la marche n est la somme des façons d'atteindre la marche n-1 (en prenant 1 marche) et des façons d'atteindre la marche n-2 (en prenant 2 marches). 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

Produit du tableau sauf lui-même

Étant donné un tableau d'entiers nums, renvoyer un tableau answer tel que answer[i] soit égal au produit de tous les éléments de nums sauf nums[i]. Ne pas utiliser l'opérateur de division.

Explication : Vous pouvez résoudre ce problème en calculant les produits de préfixe et les produits de suffixe. Le résultat à l'index i est le produit de tous les éléments avant i multiplié par le produit de tous les éléments après i.

function productExceptSelf(nums) { const n = nums.length; const answer = new Array(n).fill(1); // Calculer les produits de préfixe let prefix = 1; for (let i = 0; i < n; i++) { answer[i] = prefix; prefix *= nums[i]; } // Calculer les produits de suffixe et multiplier avec le préfixe 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]

Nombre d'îles

Étant donné une carte de grille 2D de '1' (terre) et '0' (eau), compter le nombre d'îles. Une île est entourée d'eau et est formée en connectant des terres adjacentes horizontalement ou verticalement.

Explication : Parcourir chaque cellule de la grille. Si vous trouvez un '1' (terre), incrémentez le compteur d'îles et démarrez une recherche en profondeur (DFS) ou une recherche en largeur (BFS) à partir de cette cellule. Pendant la recherche, marquez tous les '1' connectés comme '0' (ou visités) pour éviter de les compter à nouveau.

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'; // Marquer comme visité 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 (Nécessite d'être exécuté sur une copie ou de restaurer la grille)

Cache LRU (Least Recently Used)

Concevoir et implémenter une structure de données pour un cache LRU (Least Recently Used). Elle doit prendre en charge les opérations get et put.

Explication : Un cache LRU expulse l'élément le moins récemment utilisé lorsque la capacité est atteinte. Une implémentation courante utilise une Map (ou une table de hachage) pour des recherches en O(1) en moyenne et une liste doublement chaînée pour des mises à jour en O(1) du statut 'récemment utilisé'. La Map de JavaScript maintient l'ordre d'insertion, ce qui peut simplifier une implémentation de 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); // Supprimer pour réinsérer à la 'fin' (le plus récent) 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); // Expulse 2 console.log(cache.get(2)); // -1

Générer des parenthèses

Étant donné n paires de parenthèses, écrire une fonction pour générer toutes les combinaisons de parenthèses bien formées.

Explication : Il s'agit d'un problème classique de retour sur trace (backtracking). Maintenir des comptes pour les parenthèses ouvrantes et fermantes. Vous pouvez ajouter une parenthèse ouvrante si open < n. Vous pouvez ajouter une parenthèse fermante si close < open. Le cas de base est lorsque la longueur de la chaîne atteint 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)); // ['((()))', '(()())', '(())()', '()(())', '()()()']

Conteneur avec le plus d'eau

Étant donné n entiers non négatifs a1, a2, ..., an, où chacun représente un point à la coordonnée (i, ai). n lignes verticales sont tracées de telle sorte que les deux extrémités de la ligne i sont à (i, ai) et (i, 0). Trouver deux lignes qui, avec l'axe des x, forment un conteneur, de sorte que le conteneur contienne le plus d'eau.

Explication : Utiliser l'approche à deux pointeurs. Commencer avec un pointeur au début et un à la fin. Calculer la surface. La surface est limitée par la ligne la plus courte. Pour potentiellement trouver une surface plus grande, déplacer le pointeur qui pointe vers la ligne la plus courte vers l'intérieur.

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

3Somme

Étant donné un tableau nums de n entiers, existe-t-il des éléments a, b, c dans nums tels que a + b + c = 0 ? Trouver toutes les triplets uniques dans le tableau qui donnent la somme de zéro.

Explication : Tout d'abord, trier le tableau. Ensuite, parcourir le tableau avec un pointeur i. Pour chaque i, utiliser deux autres pointeurs, left (commençant à i+1) et right (commençant à la fin), pour trouver deux nombres dont la somme est égale à -nums[i]. Gérer les doublons pour garantir des triplets uniques.

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; // Ignorer les doublons pour 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++; // Ignorer les doublons while (left < right && nums[right] === nums[right - 1]) right--; // Ignorer les doublons 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]]

Rechercher une position d'insertion

Étant donné un tableau trié d'entiers distincts et une valeur cible, renvoyer l'index si la cible est trouvée. Sinon, renvoyer l'index où elle serait si elle était insérée dans l'ordre.

Explication : Il s'agit d'une variation de la recherche binaire. Effectuer une recherche binaire, mais si l'élément n'est pas trouvé, le pointeur start se retrouvera à la position où l'élément devrait être inséré.

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; // Si non trouvé, start est le point d'insertion } 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

Fusionner deux listes triées (listes chaînées)

Fusionner deux listes chaînées triées et les renvoyer sous forme de nouvelle liste triée. La nouvelle liste doit être formée en épissant les nœuds des deux premières listes.

Explication : Utiliser un nœud de tête factice pour simplifier le processus. Utiliser un pointeur pour construire la nouvelle liste. Comparer les nœuds actuels des deux listes d'entrée et ajouter le plus petit à la nouvelle liste, en faisant avancer le pointeur de cette liste. Répéter jusqu'à ce qu'une liste soit épuisée, puis ajouter le reste de l'autre liste.

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

Arbre Symétrique

Étant donné un arbre binaire, vérifiez s'il est un miroir de lui-même (c'est-à-dire, symétrique par rapport à son centre).

Explication : Cela peut être résolu récursivement. Un arbre est symétrique si le sous-arbre gauche est une image miroir du sous-arbre droit. Créez une fonction d'assistance isMirror(t1, t2) qui vérifie si deux arbres sont des miroirs. Elle doit vérifier : 1. t1.val === t2.val. 2. t1.left est un miroir de t2.right. 3. t1.right est un miroir de 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); } // Exemple : 1 -> [2, 2] -> [3, 4, 4, 3] est symétrique.

Parcours en Largeur (BFS) (Arbre Binaire de Recherche/Arbre)

Étant donné un arbre binaire, renvoyez le parcours en largeur de ses valeurs de nœuds (c'est-à-dire, de gauche à droite, niveau par niveau).

Explication : Il s'agit d'une recherche en largeur (BFS - Breadth-First Search). Utilisez une file d'attente. Commencez par ajouter la racine à la file d'attente. Tant que la file d'attente n'est pas vide, traitez tous les nœuds du niveau actuel. Pour chaque nœud traité, ajoutez ses enfants (s'ils existent) à la file d'attente pour le niveau suivant.

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

Convertir un Tableau Trié en Arbre Binaire de Recherche (BST) Équilibré en Hauteur

Étant donné un tableau dont les éléments sont triés par ordre croissant, convertissez-le en un arbre binaire de recherche (BST) équilibré en hauteur.

Explication : Pour créer un BST équilibré en hauteur, vous devez choisir l'élément du milieu du tableau comme racine. Ensuite, construisez récursivement le sous-arbre gauche à partir de la moitié gauche du tableau et le sous-arbre droit à partir de la moitié droite.

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); } // Exemple : [-10, -3, 0, 5, 9] // Sortie : Un arbre comme [0, -3, 9, -10, null, 5, null]

Implémenter `bind`

Implémentez votre propre version de Function.prototype.bind.

Explication : bind crée une nouvelle fonction qui, lorsqu'elle est appelée, a son mot-clé this défini sur la valeur fournie, avec une séquence d'arguments donnée précédant ceux fournis lors de l'appel de la nouvelle fonction. Utilisez apply ou call à l'intérieur d'une fonction renvoyée, en gérant l'application partielle (arguments prédéfinis).

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

Trouver le K-ième Plus Grand Élément

Trouvez le k-ième plus grand élément dans un tableau non trié. Notez qu'il s'agit du k-ième plus grand élément dans l'ordre trié, et non du k-ième élément distinct.

Explication : Une approche simple consiste à trier le tableau puis à prendre l'élément à l'index n - k. Une approche plus efficace pour les grands tableaux implique l'utilisation d'un tas min ou d'un algorithme de sélection comme Quickselect.

function findKthLargest(nums, k) { // Approche simple : Tri nums.sort((a, b) => b - a); // Tri par ordre décroissant return nums[k - 1]; // Note : Quickselect serait plus efficace lors d'un entretien // mais le tri est plus facile à implémenter rapidement. } 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

Vérifier si un Objet a une Propriété

Comment vérifier si un objet a une propriété spécifique ?

Explication : Vous pouvez utiliser l'opérateur in (vérifie les propriétés propres et héritées), Object.prototype.hasOwnProperty.call(obj, prop) (vérifie uniquement les propriétés propres), ou simplement obj.prop !== undefined (peut être délicat avec des valeurs 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 est hérité)

Entier en Romain

Écrivez une fonction pour convertir un entier en sa représentation en chiffre romain.

Explication : Créez un mappage des chiffres romains et de leurs valeurs correspondantes, classés du plus grand au plus petit. Itérez sur cette carte. Pour chaque valeur, soustrayez-la du nombre d'entrée autant de fois que possible, en ajoutant le chiffre romain à chaque fois.

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

Romain en Entier

Écrivez une fonction pour convertir un chiffre romain en un entier.

Explication : Créez une carte des symboles romains aux valeurs. Itérez sur la chaîne. Si la valeur du symbole actuel est inférieure à la valeur du symbole suivant (comme 'IV' ou 'IX'), soustrayez la valeur actuelle ; sinon, ajoutez-la.

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

Implémenter un `Set`

Implémentez une structure de données Set de base (sans utiliser le Set intégré) avec les méthodes add, has, delete et size.

Explication : Vous pouvez utiliser un objet JavaScript (carte de hachage) comme stockage sous-jacent. Les clés seront les éléments de l'ensemble (vous devrez peut-être gérer la manière de stocker différents types et d'assurer l'unicité des clés).

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

Triangle de Pascal

Étant donné un entier numRows, générez les numRows premières lignes du triangle de Pascal.

Explication : Dans le triangle de Pascal, chaque nombre est la somme des deux nombres directement au-dessus. Commencez avec [[1]]. Pour chaque ligne suivante, commencez et finissez par 1. Chaque élément du milieu est la somme de l'élément au même index et de l'index précédent de la ligne supérieure.

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

Recherche de Mot

Étant donné un tableau 2D (plateau) et un mot, trouvez si le mot existe dans la grille. Le mot peut être construit à partir de lettres de cellules adjacentes séquentiellement, où les cellules 'adjacentes' sont voisines horizontalement ou verticalement.

Explication : Cela nécessite une recherche en profondeur (DFS) avec retour sur trace. Itérez sur chaque cellule. Si une cellule correspond à la première lettre du mot, démarrez un DFS. La fonction DFS vérifie les voisins, en s'assurant qu'ils correspondent à la lettre suivante et qu'ils n'ont pas été visités dans le chemin actuel. Si un chemin échoue, revenez en arrière en démarquant la cellule.

function exist(board, word) { const rows = board.length; const cols = board[0].length; function dfs(r, c, index) { if (index === word.length) return true; // Mot trouvé if (r < 0 || c < 0 || r >= rows || c >= cols || board[r][c] !== word[index]) { return false; } const temp = board[r][c]; board[r][c] = '#'; // Marque comme visité 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; // Retour sur trace 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

Sous-chaîne de Fenêtre Minimale

Étant donné deux chaînes s et t, trouvez la fenêtre minimale dans s qui contiendra tous les caractères de t. S'il n'y a pas une telle fenêtre dans s qui couvre tous les caractères de t, renvoyez la chaîne vide "".

Explication : Utilisez une approche de fenêtre glissante avec deux pointeurs (left et right) et des cartes de hachage. Une carte stocke les comptes de caractères nécessaires à partir de t. Une autre carte stocke les comptes dans la fenêtre actuelle. Développez la fenêtre avec right. Une fois que la fenêtre contient tous les caractères nécessaires, essayez de la réduire avec left pour trouver la fenêtre minimale possible.

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'

Inverser une Liste Chaînée

Étant donné la head d'une liste chaînée simple, inversez la liste et renvoyez la liste inversée.

Explication : Vous devez itérer sur la liste, en changeant le pointeur next de chaque nœud pour qu'il pointe vers le nœud précédent. Gardez une trace des nœuds previous, current et next pendant l'itération.

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; // Stocke le nœud suivant current.next = prev; // Inverse le pointeur du nœud actuel prev = current; // Avance prev d'un pas current = next; // Avance current d'un pas } return prev; // La nouvelle tête est le dernier 'prev' } // Exemple : 1->2->3->4->5 devient 5->4->3->2->1

Détecter un Cycle dans une Liste Chaînée

Étant donné head, la tête d'une liste chaînée, déterminez si la liste chaînée contient un cycle.

Explication : Utilisez l'algorithme de la 'Tortue et du Lièvre de Floyd'. Ayez deux pointeurs, l'un avançant d'un pas à la fois (slow) et l'autre avançant de deux pas à la fois (fast). S'il y a un cycle, le pointeur fast finira par rattraper le pointeur 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; // Atteint la fin, pas de cycle slow = slow.next; fast = fast.next.next; } return true; // Les pointeurs se sont rencontrés, un cycle existe } // Exemple : 3->2->0->-4 avec -4 pointant vers 2. hasCycle renvoie true.

Implémenter `Object.create`

Implémentez une fonction qui imite le comportement de Object.create(proto).

Explication : Object.create crée un nouvel objet, en utilisant un objet existant comme prototype de l'objet nouvellement créé. Vous pouvez y parvenir en créant une fonction de constructeur temporaire, en définissant son prototype sur le proto d'entrée, puis en renvoyant une nouvelle instance de celui-ci.

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

Qu'est-ce que le Hoisting ?

Expliquez le hoisting en JavaScript et fournissez un exemple.

Explication : Le hoisting est le comportement par défaut de JavaScript qui consiste à déplacer les déclarations au début de la portée actuelle (globale ou de fonction) avant l'exécution du code. Les déclarations de variables (var) sont hissées et initialisées avec undefined. Les déclarations de fonctions sont entièrement hissées (à la fois le nom et le corps). let et const sont hissés mais non initialisés, ce qui entraîne une zone morte temporelle.

console.log(myVar); // undefined (var est hissé et initialisé avec undefined) // console.log(myLet); // ReferenceError: Impossible d'accéder à 'myLet' avant l'initialisation (TDZ) myFunc(); // 'Hello!' (La déclaration de fonction est entièrement hissée) var myVar = 'I am a var'; let myLet = 'I am a let'; function myFunc() { console.log('Hello!'); }

Expliquer le Bullage et la Capture d'Événements

Expliquez le bullage et la capture d'événements dans le contexte du DOM.

Explication : Ce sont deux phases de propagation d'événements dans le DOM HTML. Phase de capture : L'événement descend de la window vers l'élément cible. Phase de bullage : L'événement remonte de l'élément cible vers la window. Par défaut, la plupart des gestionnaires d'événements sont enregistrés pendant la phase de bullage. Vous pouvez utiliser addEventListener(type, listener, useCapture) avec useCapture = true pour gérer les événements pendant la phase de capture.

// Dans une structure HTML : <div><p><span>Cliquez-moi</span></p></div> // Si vous cliquez sur le <span> : // Capture : window -> document -> html -> body -> div -> p -> span // Bullage : span -> p -> div -> body -> html -> document -> window /* Exemple JS div.addEventListener('click', () => console.log('Div cliquée'), true); // Capture p.addEventListener('click', () => console.log('P cliqué'), false); // Bullage span.addEventListener('click', () => console.log('Span cliqué'), false); // Bullage // La sortie serait : Div cliquée, Span cliqué, P cliqué */

Implémenter `JSON.parse` manuellement (basique)

Essayez d'implémenter une version très basique de JSON.parse (gérer les objets simples, les tableaux, les chaînes, les nombres).

Explication : C'est une tâche très complexe dans son intégralité, mais dans un contexte de codage en direct, il pourrait vous être demandé de gérer un sous-ensemble très simplifié. Vous auriez besoin de parser la chaîne, d'identifier les limites des objets {} et des tableaux [], les paires clé-valeur ("clé": valeur), et les types de données de base. eval ou new Function peuvent tricher mais sont dangereux. Un vrai parseur utiliserait un lexer/tokenizer et un parseur.

function basicJsonParse(jsonString) { // AVERTISSEMENT : L'utilisation de new Function est aussi peu sécurisée que eval. // Ceci est un exemple simplifié et INSÉCURISÉ à des fins de démonstration uniquement. // Une implémentation réelle nécessite un parseur approprié. try { return (new Function('return ' + jsonString))(); } catch (e) { throw new SyntaxError('JSON invalide : ' + e.message); } } console.log(basicJsonParse('{"a": 1, "b": ["x", "y"]}')); // { a: 1, b: ['x', 'y'] }

Aplatir un Tableau Profondément Imbriqué

Écrivez une fonction pour aplatir un tableau profondément imbriqué.

Explication : Contrairement à l'aplatissement précédent (un niveau), cela doit gérer n'importe quel niveau d'imbrication. La récursivité est un ajustement naturel. Itérez sur le tableau. Si un élément est un tableau, appelez récursivement flatten dessus et concaténez le résultat. Sinon, ajoutez l'élément.

function deepFlatten(arr) { let flattened = []; arr.forEach(item => { if (Array.isArray(item)) { flattened = flattened.concat(deepFlatten(item)); } else { flattened.push(item); } }); return flattened; // ES2019+ offre un moyen beaucoup plus simple : // return arr.flat(Infinity); } console.log(deepFlatten([1, [2, [3, 4], 5], 6])); // [1, 2, 3, 4, 5, 6]

Implémenter un Trie (Arbre de Préfixes)

Implémentez une structure de données Trie avec les méthodes insert, search et startsWith.

Explication : Un Trie est une structure de données arborescente utilisée pour stocker et récupérer efficacement des clés dans un ensemble de chaînes. Chaque nœud représente un caractère, et les chemins à partir de la racine représentent des mots ou des préfixes.

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

Mélanger un Tableau (Fisher-Yates)

Écrivez une fonction pour mélanger un tableau sur place en utilisant l'algorithme de Fisher-Yates (ou Knuth).

Explication : L'algorithme de Fisher-Yates itère sur un tableau du dernier élément au premier. À chaque itération, il échange l'élément actuel avec un élément choisi aléatoirement du début du tableau jusqu'à l'élément actuel (inclus).

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]]; // Échange les éléments } return arr; } console.log(shuffleArray([1, 2, 3, 4, 5])); // ex: [3, 5, 1, 2, 4]

Composer des Fonctions

Implémentez une fonction compose qui prend plusieurs fonctions et renvoie une nouvelle fonction qui les applique de droite à gauche.

Explication : La composition de fonctions (f ∘ g)(x) = f(g(x)) applique une fonction au résultat d'une autre. Une compose(f, g, h) générale signifierait f(g(h(x))). Utilisez reduceRight pour une implémentation élégante.

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

Enchaîner des Fonctions (Pipe)

Implémentez une fonction pipe qui prend plusieurs fonctions et renvoie une nouvelle fonction qui les applique de gauche à droite.

Explication : Semblable à compose, mais l'ordre d'application est inversé : pipe(f, g, h) signifie h(g(f(x))). Utilisez reduce pour l'implémentation.

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

Problème du Rendu de Monnaie

Étant donné un tableau de dénominations de pièces et un montant, trouvez le nombre minimum de pièces pour atteindre ce montant. Supposons une offre infinie de chaque pièce.

Explication : C'est un problème classique de programmation dynamique. Créez un tableau dpdp[i] stocke le nombre minimum de pièces nécessaires pour le montant i. dp[i] = min(dp[i - pièce]) + 1 pour toutes les pièces.

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

Plus Bas Ancêtre Commun (BST)

Étant donné un arbre binaire de recherche (BST), trouvez le plus bas ancêtre commun (LCA) de deux nœuds donnés dans le BST.

Explication : Le LCA est le nœud le plus profond qui a les deux nœuds donnés comme descendants. Dans un BST, vous pouvez le trouver en traversant à partir de la racine. Si les deux nœuds sont plus petits que le nœud actuel, allez à gauche. Si les deux sont plus grands, allez à droite. Si l'un est plus petit et l'autre plus grand (ou si l'un est le nœud actuel), alors le nœud actuel est le LCA.

class Node { constructor(val) { this.val = val; this.left = this.right = null; } } function lowestCommonAncestor(root, p, q) { let current = root; while (current) { if (p.val > current.val && q.val > current.val) { current = current.right; } else if (p.val < current.val && q.val < current.val) { current = current.left; } else { return current; // LCA trouvé } } return null; // Ne devrait pas arriver dans un BST valide avec p et q } // Exemple : root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 => LCA est 6

Sérialiser et Désérialiser un Arbre Binaire

Concevez un algorithme pour sérialiser et désérialiser un arbre binaire.

Explication : La sérialisation convertit un arbre en une chaîne ou un tableau. La désérialisation reconstitue l'arbre. Une méthode courante est le parcours pré-ordre (DFS). Utilisez un marqueur spécial (par exemple, '#') pour les nœuds nuls afin de préserver la structure.

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

Implémenter `setInterval` avec `setTimeout`

Implémentez une fonction mySetInterval qui imite setInterval mais utilise setTimeout de manière récursive.

Explication : setInterval exécute une fonction de manière répétée toutes les N millisecondes. Vous pouvez y parvenir en faisant en sorte qu'une fonction s'appelle elle-même avec setTimeout après chaque exécution. Vous avez également besoin d'un moyen de l'effacer (myClearInterval).

function mySetInterval(callback, delay, ...args) { const interval = { timerId: null }; function run() { interval.timerId = setTimeout(() => { callback.apply(this, args); run(); // Planifie le prochain appel }, delay); } run(); return interval; // Renvoie un objet pour permettre l'effacement } function myClearInterval(interval) { clearTimeout(interval.timerId); } // Exemple d'utilisation : let count = 0; const intervalId = mySetInterval(() => { console.log(`Bonjour : ${++count}`); if (count === 3) myClearInterval(intervalId); }, 500);

Parcours de Graphe (BFS & DFS)

Implémentez la recherche en largeur (BFS) et la recherche en profondeur (DFS) pour un graphe donné (représentation par liste d'adjacence).

Explication : BFS explore d'abord les voisins (en utilisant une file d'attente), tandis que DFS explore aussi loin que possible le long de chaque branche (en utilisant une pile ou la récursion). Gardez une trace des nœuds visités pour éviter les cycles et les travaux redondants.

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); // Ajoutez les voisins en sens inverse pour les traiter dans l'ordre plus tard (facultatif) 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']

Rotation d'Image (Matrice)

Étant donné une matrice 2D n x n représentant une image. Faites pivoter l'image de 90 degrés (dans le sens des aiguilles d'une montre) sur place.

Explication : Une façon courante d'y parvenir est de d'abord transposer la matrice (échanger matrix[i][j] avec matrix[j][i]) puis d'inverser chaque ligne.

function rotate(matrix) { const n = matrix.length; // Transposition 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]]; } } // Inverser chaque ligne 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]]

Parcours de Matrice en Spirale

Étant donné une matrice m x n, renvoyez tous les éléments de la matrice en ordre spiralé.

Explication : Utilisez quatre pointeurs pour définir les limites : top, bottom, left, right. Traversez la ligne supérieure, puis la colonne de droite, puis la ligne du bas, puis la colonne de gauche, en réduisant les limites après chaque traversée, jusqu'à ce que left traverse right ou top traverse 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]

Mettre à zéro les lignes et colonnes d'une matrice

Étant donné une matrice m x n, si un élément est 0, mettez à zéro toute sa ligne et sa colonne. Faites-le sur place.

Explication : Une approche naïve nécessite un espace supplémentaire de O(m*n). Pour le faire en espace O(1) (ou O(m+n)), vous pouvez utiliser la première ligne et la première colonne pour stocker des informations sur les lignes/colonnes qui doivent être mises à zéro. Vous avez besoin de drapeaux séparés pour savoir si la première ligne/colonne elle-même doit être mise à zéro.

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

Implémenter `Promise.race`

Implémentez une fonction qui se comporte comme Promise.race.

Explication : Promise.race prend un tableau de promesses et renvoie une seule promesse. Cette promesse se résout (se résout ou est rejetée) dès qu'une des promesses d'entrée se résout, avec la valeur ou la raison de cette promesse.

function myPromiseRace(promises) { return new Promise((resolve, reject) => { if (!promises || promises.length === 0) { return; // Ou resolve/reject selon la spécification } 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'

Motif Singleton

Implémentez le motif de conception Singleton en JavaScript.

Explication : Le motif Singleton garantit qu'une classe n'a qu'une seule instance et fournit un point d'accès global à celle-ci. Cela peut être réalisé en utilisant une fermeture pour contenir l'instance.

const Singleton = (function() { let instance; function createInstance() { // Méthodes et variables privées const privateVar = 'Je suis privé'; function privateMethod() { console.log('Privé'); } return { // Méthodes et variables publiques publicVar: 'Je suis 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()); // 'Je suis privé'

Valider l'adresse IP

Écrivez une fonction pour vérifier si une chaîne donnée est une adresse IPv4 ou IPv6 valide.

Explication : Pour IPv4, vérifiez s'il y a 4 nombres (0-255) séparés par des points, sans zéros non significatifs (sauf pour '0' lui-même). Pour IPv6, vérifiez s'il y a 8 groupes de 1 à 4 chiffres hexadécimaux séparés par des deux-points (gérer les variations comme '::'). Les expressions régulières peuvent être utilisées, mais l'analyse manuelle est souvent plus claire pour les entretiens.

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; // Simplifié : Pas de '::' 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 (Simplifié) console.log(validIPAddress('256.256.256.256')); // Neither

Trouver un élément de pic

Un élément de pic est un élément qui est strictement supérieur à ses voisins. Étant donné un tableau d'entrée nums, trouvez un élément de pic et renvoyez son index.

Explication : Puisque n'importe quel pic fera l'affaire, et que nums[-1] et nums[n] sont considérés comme -Infinity, vous pouvez utiliser une recherche binaire modifiée. Si nums[mid] est inférieur à nums[mid+1], un pic doit exister à droite. Sinon, un pic doit exister à gauche (ou mid lui-même est un pic).

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; // Le pic est à droite } else { right = mid; // Le pic est mid ou à gauche } } return left; // 'left' sera l'index d'un pic } console.log(findPeakElement([1, 2, 3, 1])); // 2 (index de 3) console.log(findPeakElement([1, 2, 1, 3, 5, 6, 4])); // 5 (index de 6) ou 1 (index de 2)

Compter les bits

Étant donné un entier n, renvoyez un tableau ans de longueur n + 1 tel que pour chaque i (0 <= i <= n), ans[i] est le nombre de 1 dans la représentation binaire de i.

Explication : Vous pouvez résoudre ce problème en utilisant la programmation dynamique. Remarquez la relation : dp[i] = dp[i >> 1] + (i & 1). Le nombre de 1 dans i est le nombre de 1 dans i décalé vers la droite (c'est-à-dire i/2), plus 1 si i est impair.

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); // Ou : dp[i] = dp[i & (i - 1)] + 1; (Supprime le dernier bit défini) } return dp; } console.log(countBits(5)); // [0, 1, 1, 2, 1, 2]

Puissance de deux

Étant donné un entier n, renvoyez true s'il s'agit d'une puissance de deux. Sinon, renvoyez false.

Explication : Une puissance de deux en représentation binaire a exactement un bit '1' (par exemple, 1=1, 2=10, 4=100, 8=1000). Une astuce astucieuse au niveau des bits consiste à vérifier si n > 0 et (n & (n - 1)) === 0. Si n est une puissance de deux, n-1 aura tous les bits inférieurs à ce '1' mis à '1'. Leur AND logique donne 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

Fusionner les intervalles

Étant donné un tableau d'intervalsintervals[i] = [starti, endi], fusionnez tous les intervalles qui se chevauchent et renvoyez un tableau des intervalles non chevauchants.

Explication : Tout d'abord, triez les intervalles en fonction de leurs heures de début. Ensuite, itérez à travers les intervalles triés. Si l'intervalle actuel chevauche le dernier intervalle de la liste de résultats, fusionnez-les en mettant à jour l'heure de fin du dernier intervalle. Sinon, ajoutez l'intervalle actuel à la liste de résultats.

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]) { // Chevauchement : fusionner last[1] = Math.max(last[1], current[1]); } else { // Pas de chevauchement : ajouter un nouvel intervalle merged.push(current); } } return merged; } console.log(mergeIntervals([[1, 3], [2, 6], [8, 10], [15, 18]])); // [[1, 6], [8, 10], [15, 18]]

Coupure de mot

Étant donné une chaîne s et un dictionnaire de chaînes wordDict, renvoyez true si s peut être segmenté en une séquence de mots de dictionnaire séparés par des espaces, composée d'un ou plusieurs mots.

Explication : Utilisez la programmation dynamique. Créez un tableau booléen dpdp[i] est vrai si la sous-chaîne s[0...i-1] peut être segmentée. dp[i] est vrai s'il existe un j < i tel que dp[j] est vrai et que la sous-chaîne s[j...i-1] est dans le dictionnaire.

function wordBreak(s, wordDict) { const wordSet = new Set(wordDict); const n = s.length; const dp = new Array(n + 1).fill(false); dp[0] = true; // Cas de base : chaîne vide 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

Implémenter `Array.prototype.flat`

Implémentez votre propre version de Array.prototype.flat, qui crée un nouveau tableau avec tous les éléments de sous-tableau concaténés récursivement jusqu'à une profondeur spécifiée.

Explication : Utilisez la récursion. Itérez à travers le tableau. Si un élément est un tableau et que la profondeur actuelle est inférieure à la profondeur spécifiée, appelez récursivement flatten dessus. Sinon, poussez l'élément. Gérez la profondeur par défaut de 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]

Inverser les mots dans une chaîne

Étant donné une chaîne d'entrée s, inversez l'ordre des mots.

Explication : Un 'mot' est défini comme une séquence de caractères sans espace. Supprimez les espaces en début et fin de chaîne, divisez-la par un ou plusieurs espaces, filtrez les chaînes vides résultant de plusieurs espaces, inversez le tableau et rejoignez-le avec des espaces uniques.

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'

Analyseur de chaîne de requête

Écrivez une fonction pour analyser une chaîne de requête URL dans un objet.

Explication : Divisez la chaîne par &. Pour chaque partie, divisez par = pour obtenir la clé et la valeur. Décodez les composants URI pour la clé et la valeur. Gérez les cas où une clé peut apparaître plusieurs fois (créer un tableau) ou n'a pas de valeur.

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 }

Utiliser `Proxy` pour la validation

Démontrez comment utiliser un objet Proxy pour la validation des propriétés d'objet.

Explication : Un Proxy vous permet d'intercepter et de personnaliser les opérations effectuées sur un objet. Utilisez le piège set pour valider les valeurs avant de les affecter à une propriété. Levez une erreur si la validation échoue.

const validator = { set: function(obj, prop, value) { if (prop === 'age') { if (!Number.isInteger(value)) { throw new TypeError('L\'âge n\'est pas un entier'); } if (value < 0 || value > 150) { throw new RangeError('L\'âge semble invalide'); } } // Comportement par défaut : Définir la propriété obj[prop] = value; return true; } }; const person = {}; const personProxy = new Proxy(person, validator); personProxy.age = 30; // OK console.log(personProxy.age); // 30 // personProxy.age = 'trente'; // Lève une TypeError // personProxy.age = 200; // Lève une RangeError

Salles de réunion

Étant donné un tableau d'intervalles de temps de réunion [[start1, end1], [start2, end2], ...], déterminez si une personne pourrait assister à toutes les réunions.

Explication : Si une personne peut assister à toutes les réunions, cela signifie qu'aucune réunion ne se chevauche. Pour vérifier cela, triez les intervalles par leurs heures de début. Ensuite, itérez à travers les intervalles triés et vérifiez si l'heure de début de la réunion actuelle est antérieure à l'heure de fin de la réunion précédente. Si c'est le cas, il y a un chevauchement et la personne ne peut pas assister à toutes les réunions.

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

Implémenter `Promise.any`

Implémentez une fonction qui se comporte comme Promise.any.

Explication : Promise.any prend un tableau de promesses et renvoie une seule promesse. Cette promesse est tenue dès qu'une des promesses d'entrée est tenue. Si toutes les promesses d'entrée sont rejetées, elle est rejetée avec un AggregateError contenant toutes les raisons de rejet.

function myPromiseAny(promises) { return new Promise((resolve, reject) => { const numPromises = promises.length; if (numPromises === 0) { reject(new AggregateError([], 'Toutes les promesses ont été rejetées')); return; } let rejectionCount = 0; const errors = []; promises.forEach((promise, index) => { Promise.resolve(promise) .then(resolve) // Résoudre dès qu'une est tenue .catch(error => { errors[index] = error; rejectionCount++; if (rejectionCount === numPromises) { reject(new AggregateError(errors, 'Toutes les promesses ont été rejetées')); } }); }); }); } // Exemple : myPromiseAny([Promise.reject('err1'), Promise.resolve('ok')]) -> 'ok' // Exemple : myPromiseAny([Promise.reject('err1'), Promise.reject('err2')]) -> AggregateError

Motif Observateur

Implémentez le motif de conception Observateur (Pub/Sub) en JavaScript.

Explication : Le motif Observateur définit une dépendance un-à-plusieurs entre les objets. Lorsqu'un objet (Sujet) change d'état, tous ses dépendants (Observateurs) sont notifiés et mis à jour automatiquement. Le Sujet maintient une liste d'observateurs et fournit des méthodes pour les ajouter, les supprimer et les notifier.

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} a reçu : ${data}`); } } const subject = new Subject(); const obs1 = new Observer('Observateur 1'); const obs2 = new Observer('Observateur 2'); subject.subscribe(obs1); subject.subscribe(obs2); subject.notify('Quelque chose s\'est passé !'); // Observateur 1 a reçu : Quelque chose s'est passé ! // Observateur 2 a reçu : Quelque chose s'est passé !

Trouver un nombre en double

Étant donné un tableau d'entiers nums contenant n + 1 entiers où chaque entier est dans la plage [1, n] inclusivement, trouvez le nombre répété.

Explication : Puisque les nombres sont dans [1, n] et que la taille du tableau est n+1, cela garantit au moins un doublon. Cela peut être mappé à un problème de 'Recherche de cycle dans une liste chaînée' (Tortue et lièvre de Floyd). Traitez les valeurs du tableau comme des pointeurs : index -> nums[index].

function findDuplicate(nums) { let tortoise = nums[0]; let hare = nums[0]; // Phase 1 : Trouver le point d'intersection do { tortoise = nums[tortoise]; hare = nums[nums[hare]]; } while (tortoise !== hare); // Phase 2 : Trouver l'entrée du 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

Nettoyeur HTML basique

Écrivez une fonction de base pour nettoyer une chaîne HTML, en supprimant les balises potentiellement dangereuses (comme <script>) tout en conservant les balises sûres (comme <p>, <b>).

Explication : Il s'agit d'un sujet complexe, et les nettoyeurs du monde réel sont sophistiqués. Pour un entretien, une approche de base pourrait impliquer l'utilisation de Regex pour supprimer des balises spécifiques ou n'autoriser qu'une liste blanche de balises. Ceci n'est pas sécurisé pour la production.

function basicSanitize(html) { // AVERTISSEMENT : Ceci est un exemple très basique et non sécurisé. // NE PAS utiliser cela en production. Utilisez une bibliothèque comme DOMPurify. // Supprimer les balises script let sanitized = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, ''); // Exemple : Supprimer les attributs onclick (très basique) 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>

Distance d'édition

Étant données deux chaînes word1 et word2, renvoyez le nombre minimum d'opérations (insertion, suppression ou substitution) nécessaires pour convertir word1 en word2.

Explication : Il s'agit d'un problème classique de programmation dynamique (distance de Levenshtein). Créez un tableau 2D dpdp[i][j] est la distance d'édition entre les i premiers caractères de word1 et les j premiers caractères de word2. La relation de récurrence implique de considérer le coût de l'insertion, de la suppression et de la substitution.

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

Plus longue sous-séquence croissante (LIS)

Étant donné un tableau d'entiers nums, renvoyez la longueur de la plus longue sous-séquence strictement croissante.

Explication : Utilisez la programmation dynamique. Soit dp[i] la longueur de la LIS se terminant à l'indice i. Pour calculer dp[i], trouvez tous les j < i tels que nums[j] < nums[i], et prenez dp[i] = 1 + max(dp[j]). La réponse finale est la valeur maximale dans le tableau 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)

Problème des N-Dames

Le problème des N-Dames consiste à placer N dames d'échecs sur un échiquier N×N de telle sorte qu'aucune des dames ne se menace mutuellement. Étant donné N, renvoyez une solution valide (ou toutes).

Explication : Utilisez le backtracking. Placez les dames ligne par ligne. Pour chaque ligne, essayez de placer une dame dans chaque colonne. Avant de placer, vérifiez si la position proposée est sûre (non attaquée par les dames des lignes précédentes). Si elle est sûre, placez-la et récursez pour la ligne suivante. Si la récursion échoue, faites du backtracking (retirez la dame) et essayez la colonne suivante.

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; // Vérifier la colonne for (let i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) if (board[i][j] === 'Q') return false; // Vérifier la diagonale haut-gauche for (let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) if (board[i][j] === 'Q') return false; // Vérifier la diagonale haut-droite 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..' ] ]

Utiliser `WeakMap` pour les données privées

Démontrez comment utiliser WeakMap pour stocker des données privées pour les instances de classe.

Explication : WeakMap vous permet d'associer des données à un objet d'une manière qui n'empêche pas le ramasse-miettes si l'objet n'est plus référencé. C'est utile pour créer des membres 'privés' dans les classes JavaScript avant que les champs privés natifs ne soient largement disponibles ou pour des cas d'utilisation spécifiques.

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' n'est pas une propriété publique // console.log(privateData.get(person)); // Accessible, mais pas directement via 'person.'

Implémenter `Promise.allSettled`

Implémentez une fonction qui se comporte comme Promise.allSettled.

Explication : Promise.allSettled prend un tableau de promesses et renvoie une seule promesse. Cette promesse est tenue lorsque toutes les promesses d'entrée ont été réglées (soit tenues, soit rejetées). La valeur d'accomplissement est un tableau d'objets, chacun décrivant le résultat d'une promesse ({status: 'fulfilled', value: ...} ou {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); } }); }); }); } // Exemple : myPromiseAllSettled([Promise.resolve(1), Promise.reject('err')]) // -> [{status: 'fulfilled', value: 1}, {status: 'rejected', reason: 'err'}]

Trouver la médiane de deux tableaux triés

Étant donnés deux tableaux triés nums1 et nums2 de taille m et n respectivement, renvoyez la médiane des deux tableaux triés.

Explication : Il s'agit d'un problème difficile souvent résolu avec une approche de recherche binaire sur le plus petit tableau. L'objectif est de partitionner les deux tableaux de manière à ce que tous les éléments du côté gauche soient inférieurs ou égaux à tous les éléments du côté droit, et que le nombre d'éléments du côté gauche soit égal (ou un de plus) à celui du côté droit. La médiane peut alors être calculée à partir des éléments limites.

function findMedianSortedArrays(nums1, nums2) { if (nums1.length > nums2.length) [nums1, nums2] = [nums2, nums1]; // S'assurer que nums1 est plus petit 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('Les tableaux d\'entrée ne sont pas triés'); } console.log(findMedianSortedArrays([1, 3], [2])); // 2.0 console.log(findMedianSortedArrays([1, 2], [3, 4])); // 2.5

Résolveur de Sudoku

Écrivez un programme pour résoudre un puzzle Sudoku en remplissant les cellules vides.

Explication : Utilisez le backtracking. Trouvez une cellule vide. Essayez de la remplir avec les nombres de 1 à 9. Pour chaque nombre, vérifiez s'il est valide (ne viole pas les règles du Sudoku). Si valide, appelez récursivement le solveur. Si la récursion renvoie vrai, une solution est trouvée. Sinon, faites du backtracking (réinitialisez la cellule) et essayez le nombre suivant.

function solveSudoku(board) { function isValid(row, col, numStr) { for (let i = 0; i < 9; i++) { if (board[row][i] === numStr) return false; // Vérifier la ligne if (board[i][col] === numStr) return false; // Vérifier la colonne 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; // Vérifier la boîte } 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; // Aucun nombre valide trouvé } } } return true; // Tableau résolu } backtrack(); return board; } // L'exemple nécessite un tableau 9x9 avec '.' pour les cellules vides.

Implémenter un modèle de middleware de base

Implémentez un modèle de middleware simple souvent vu dans les frameworks web.

Explication : Les fonctions middleware traitent une requête avant qu'elle n'atteigne le gestionnaire final. Chaque middleware peut modifier la requête/réponse ou passer le contrôle au middleware next. Créez une classe ou un objet qui gère une liste de fonctions middleware et les exécute en séquence.

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: Journalisation...'); req.logged = true; next(); }); app.use((req, next) => { console.log('2: Authentification...'); req.authed = true; next(); }); app.use((req, next) => { console.log('3: Gestionnaire final :', req); }); app.handle({ data: 'une requête' }); // 1: Journalisation... // 2: Authentification... // 3: Gestionnaire final : { data: 'une requête', logged: true, authed: true }

Détecter un cycle dans un graphe orienté

Étant donné un graphe orienté, écrivez une fonction pour déterminer s'il contient un cycle.

Explication : Utilisez la recherche en profondeur (DFS). Maintenez deux ensembles : visiting (nœuds actuellement dans la pile de récursion) et visited (nœuds qui ont été entièrement explorés). Si vous rencontrez un nœud dans l'ensemble visiting pendant la DFS, vous avez trouvé une arête arrière, ce qui signifie qu'il y a un cycle.

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 détecté 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

Implémenter le comportement de `Object.freeze`

Expliquez Object.freeze et implémentez une version (superficielle).

Explication : Object.freeze empêche l'ajout de nouvelles propriétés, la suppression de propriétés existantes et la modification des propriétés existantes ou de leur énumérabilité, configurabilité ou inscriptibilité. Il rend un objet immuable (superficiellement). Vous pouvez l'implémenter en utilisant Object.preventExtensions et Object.defineProperty pour rendre les propriétés existantes non inscriptibles et non configurables.

function myFreeze(obj) { Object.preventExtensions(obj); // Empêche l'ajout de nouvelles propriétés 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; // Échoue silencieusement (ou lève une exception en mode strict) // delete obj.a; // Échoue silencieusement (ou lève une exception en mode strict) // obj.a = 10; // Échoue silencieusement (ou lève une exception en mode strict) console.log(obj); // { a: 1, b: 2 }

Utiliser `requestAnimationFrame` pour une animation fluide

Expliquez requestAnimationFrame et montrez comment l'utiliser pour une boucle d'animation simple.

Explication : requestAnimationFrame (rAF) indique au navigateur que vous souhaitez effectuer une animation et demande au navigateur d'appeler une fonction spécifiée pour mettre à jour une animation avant le prochain rafraîchissement. C'est plus efficace et plus fluide que d'utiliser setTimeout ou setInterval pour les animations, car il se synchronise avec le taux de rafraîchissement du navigateur et se met en pause lorsque l'onglet n'est pas visible.

// 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; // Déplacer de 100px en 2 secondes (50px/sec) position = (elapsedTime / 2000) * 100; if (position < 200) { // Continuer l'animation jusqu'à ce qu'elle atteigne 200px box.style.left = position + 'px'; requestAnimationFrame(animate); } else { box.style.left = '200px'; } } // Démarrer l'animation // requestAnimationFrame(animate); // Décommenter pour exécuter dans le navigateur console.log('Utilisez requestAnimationFrame pour les animations du navigateur.');

Implémenter `Array.prototype.some`

Implémentez votre propre version de Array.prototype.some.

Explication : some teste si au moins un élément du tableau passe le test implémenté par la fonction fournie. Il renvoie true s'il trouve un élément pour lequel le rappel renvoie true ; sinon, il renvoie false.

function mySome(arr, callback, thisArg) { for (let i = 0; i < arr.length; i++) { if (i in arr) { // Gérer les tableaux creux 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

Implémenter `Array.prototype.every`

Implémentez votre propre version de Array.prototype.every.

Explication : every teste si tous les éléments du tableau passent le test implémenté par la fonction fournie. Il renvoie true si tous les éléments passent (ou si le tableau est vide), et false si un élément échoue.

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

Implémenter `Array.prototype.findIndex`

Implémentez votre propre version de Array.prototype.findIndex.

Explication : findIndex renvoie l'index du premier élément du tableau qui satisfait la fonction de test fournie. Sinon, il renvoie -1, indiquant qu'aucun élément n'a passé le 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

Que sont les Web Workers ?

Expliquez ce que sont les Web Workers et leur principal cas d'utilisation.

Explication : Les Web Workers sont un moyen pour le contenu web d'exécuter des scripts dans des threads d'arrière-plan. Le principal cas d'utilisation est de décharger les tâches longues ou gourmandes en calcul du thread principal (qui gère les mises à jour de l'interface utilisateur et les interactions de l'utilisateur), empêchant ainsi l'interface utilisateur de devenir non réactive ou de se 'figer'. Les workers communiquent avec le thread principal via le passage de messages (postMessage et onmessage).

// main.js /* if (window.Worker) { const myWorker = new Worker('worker.js'); myWorker.postMessage([5, 3]); // Envoyer des données au worker myWorker.onmessage = function(e) { console.log('Résultat du worker :', e.data); } } else { console.log('Votre navigateur ne prend pas en charge les Web Workers.'); } */ // worker.js /* self.onmessage = function(e) { console.log('Message reçu du script principal'); const result = e.data[0] * e.data[1]; console.log('Affichage du résultat dans le script principal'); self.postMessage(result); } */ console.log('Les Web Workers exécutent des scripts dans des threads d\'arrière-plan.');

Décoder les façons

Un message contenant des lettres de A à Z peut être encodé en nombres en utilisant le mappage 'A' -> 1, 'B' -> 2, ..., 'Z' -> 26. Étant donné une chaîne s ne contenant que des chiffres, renvoyez le nombre de façons de la décoder.

Explication : Utilisez la programmation dynamique. Soit dp[i] le nombre de façons de décoder s[0...i-1]. dp[i] dépend de dp[i-1] (si s[i-1] est un code à 1 chiffre valide) et de dp[i-2] (si s[i-2...i-1] est un code à 2 chiffres valide).

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

Addition bit à bit (sans `+`)

Écrivez une fonction pour additionner deux entiers sans utiliser l'opérateur +.

Explication : Utilisez les opérateurs binaires. La somme peut être calculée comme a ^ b (somme sans retenue), et la retenue peut être calculée comme (a & b) << 1. Répétez ce processus jusqu'à ce que la retenue devienne 0.

function getSum(a, b) { while (b !== 0) { const carry = (a & b) << 1; // Calculer la retenue a = a ^ b; // Calculer la somme sans retenue b = carry; // La retenue devient le nouveau 'b' } return a; } console.log(getSum(3, 5)); // 8 console.log(getSum(-2, 3)); // 1

Vérifier si un nombre est un palindrome

Étant donné un entier x, renvoyez true si x est un palindrome, et false sinon.

Explication : Un nombre négatif n'est pas un palindrome. Convertissez le nombre en chaîne de caractères et vérifiez si la chaîne est un palindrome (se lit de la même manière dans les deux sens). Alternativement, inversez le nombre mathématiquement et comparez.

function isPalindromeNumber(x) { if (x < 0) return false; // Approche par chaîne de caractères // return String(x) === String(x).split('').reverse().join(''); // Approche mathématique 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

Motif Fabrique

Implémentez le motif de conception Fabrique en JavaScript.

Explication : Le motif Fabrique fournit une interface pour créer des objets dans une superclasse, mais permet aux sous-classes de modifier le type d'objets qui seront créés. Il est utile pour créer des objets sans spécifier la classe exacte.

class Car { constructor(options) { this.type = 'Voiture'; this.doors = options.doors || 4; } } class Truck { constructor(options) { this.type = 'Camion'; 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('Type de véhicule inconnu'); } } } 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: 'Voiture', doors: 2 } console.log(truck); // Truck { type: 'Camion', bedSize: 'short' }

Expliquer `Symbol` et un cas d'utilisation

Expliquez ce qu'est Symbol en JavaScript et fournissez un cas d'utilisation, tel que la création de propriétés 'privées' ou de clés d'objet uniques.

Explication : Symbol est un type de données primitif introduit dans ES6. Ses instances sont uniques et immuables. Elles sont souvent utilisées comme clés pour les propriétés d'objet afin d'éviter les collisions de noms entre différentes bibliothèques ou pour créer des propriétés pseudo-privées (bien que non vraiment privées, elles ne sont pas accessibles via l'itération typique ou Object.keys).

const _privateName = Symbol('name'); const _privateMethod = Symbol('greet'); class Person { constructor(name) { this[_privateName] = name; } [_privateMethod]() { console.log(`Bonjour, je m'appelle ${this[_privateName]}`); } introduce() { this[_privateMethod](); } } const p = new Person('Bob'); p.introduce(); // Bonjour, je m'appelle Bob console.log(Object.keys(p)); // [] - Les symboles ne sont pas inclus console.log(p[_privateName]); // Bob (Accessible si vous avez le Symbole)

Convertir `arguments` en un vrai tableau

Comment pouvez-vous convertir l'objet arguments (disponible dans les fonctions non fléchées) en un vrai tableau JavaScript ?

Explication : L'objet arguments ressemble à un tableau mais n'en est pas un ; il lui manque des méthodes comme map, filter, etc. Vous pouvez le convertir en utilisant :

  1. Array.from(arguments) (ES6)
  2. [...arguments] (Syntaxe de propagation ES6)
  3. Array.prototype.slice.call(arguments) (Ancienne méthode)
function sumAll() { // 1. Array.from const args1 = Array.from(arguments); console.log('Utilisation de Array.from :', args1.reduce((a, b) => a + b, 0)); // 2. Syntaxe de propagation const args2 = [...arguments]; console.log('Utilisation de Spread :', args2.reduce((a, b) => a + b, 0)); // 3. Slice const args3 = Array.prototype.slice.call(arguments); console.log('Utilisation de Slice :', args3.reduce((a, b) => a + b, 0)); } sumAll(1, 2, 3, 4); // Utilisation de Array.from: 10 // Utilisation de Spread: 10 // Utilisation de Slice: 10