Manipuler des chaînes
Inverser une chaîne, tester un palindrome, compter des caractères.
Objectifs
À la fin de cette leçon, vous saurez :
- parcourir une chaîne caractère par caractère, comme un tableau ;
- construire une chaîne par accumulation — et comprendre pourquoi c'est le seul moyen ;
- tester si un mot est un palindrome, par deux algorithmes différents ;
- normaliser (casse, espaces) avant toute comparaison littérale.
🔗 Pour vous rafraîchir la mémoire : parcourir, sommer, compter
Une chaîne, une suite de caractères
Une chaîne se comporte comme une suite ordonnée de caractères, indexée dès zéro exactement comme un tableau :
const mot = "cours";
console.log(mot[0]); // "c"
console.log(mot.length); // 5
console.log(mot[mot.length - 1]); // "s" : dernier caractère
Et elle se parcourt avec for...of comme un tableau :
for (const lettre of mot) {
console.log(lettre);
}
// c
// o
// u
// r
// s
Mais souvenez-vous du chapitre 4 : les chaînes sont immuables. mot[0] = "k" n'a aucun effet (ou plante en mode strict). Conséquence méthodologique pour tout ce chapitre : on ne modifie jamais une chaîne en place — on en construit de nouvelles. Tous les algorithmes qui suivent découlent de cette seule contrainte.
Construire une chaîne par accumulation
L'accumulateur du chapitre précédent fonctionne aussi pour les chaînes ; la valeur neutre devient "" :
function inverser(texte) {
let resultat = "";
for (const lettre of texte) {
resultat = lettre + resultat; // chaque lettre se place DEVANT l'accumulation
}
return resultat;
}
console.log(inverser("cours")); // "sruoc"
Le geste clé est lettre + resultat : la nouvelle lettre passe devant. Tracez-le sur "abc" — l'exercice qui rend l'algorithme évident :
départ : ""
après 'a' : "" + "a" = "a"
après 'b' : "b" + "a" = "ba"
après 'c' : "c" + "ba" = "cba"
résultat : "cba" ← l'inverse, mécaniquement
Inversez l'ordre (resultat + lettre) et vous obtenez... la chaîne d'origine. Une position dans une expression, un algorithme entièrement différent : voilà pourquoi la trace à la main reste votre meilleur outil.
Remarque honnête sur le coût : à chaque tour, une nouvelle chaîne est fabriquée depuis zéro (immutabilité oblige). Sur des textes courts, invisible. Sur des millions de caractères, cela devient un vrai coût — Node fournit des outils adaptés aux gros volumes, vus plus loin dans le cursus. Pour aujourd'hui : la clarté d'abord.
Palindrome, version 1 : comparer avec l'inverse
Un palindrome se lit identiquement dans les deux sens : « kayak », « radar », « ressasser ». La définition elle-même donne l'algorithme : comparer la chaîne à son inverse.
function estPalindrome(texte) {
return texte === inverser(texte);
}
console.log(estPalindrome("kayak")); // true
console.log(estPalindrome("cours")); // false
Deux lignes, grâce à la composition : on réutilise inverser au lieu de réinventer. C'est exactement la décomposition enseignée au début du chapitre.
Palindrome, version 2 : deux curseurs vers le centre
Il existe une seconde approche, plus fine : comparer les extrémités deux à deux en avançant vers le centre.
function estPalindromeBis(texte) {
let gauche = 0; // curseur au début
let droite = texte.length - 1; // curseur à la fin
while (gauche < droite) { // tant que les curseurs ne se croisent pas
if (texte[gauche] !== texte[droite]) {
return false; // divergence → inutile d'aller plus loin
}
gauche += 1; // on avance des deux bouts
droite -= 1;
}
return true; // toutes les paires concordent
}
Tracez-le sur "kayak" (5 lettres, indices 0..4) :
gauche=0 ('k'), droite=4 ('k') : égaux → continuer
gauche=1 ('a'), droite=3 ('a') : égaux → continuer
gauche=2, droite=2 : gauche < droite est FAUX → boucle finie
résultat : true (le caractère central ne peut pas diverger de lui-même)
Comparaison des deux versions :
| Critère | Version inverse | Version deux curseurs |
|---|---|---|
| Lisibilité | excellente | bonne |
| Travail | reconstruit toute la chaîne | s'arrête à la première divergence |
| Cas « presque palindrome » | travaille quand même tout | sort immédiatement |
Le motif des deux curseurs reviendra souvent en algorithmique : retenez-le comme un outil à part entière.
Normaliser avant de comparer
« Kayak » avec majuscule, ou des espaces internes (« Engage le jeu que je le gagne »), cassent la comparaison naïve. La pratique universelle : normaliser d'abord, comparer ensuite.
function normaliser(texte) {
return texte.toLowerCase().replaceAll(" ", "");
}
estPalindrome(normaliser("Kay ak")); // true
La normalisation est une décision de contrat : que fait-on de la casse ? des espaces ? des accents (qui demandent plus qu'un toLowerCase — hors périmètre ici) ? Des entrées différentes peuvent exiger des contrats différents — l'important est de décider explicitement, pas de laisser le hasard trancher.
Compter des caractères selon un critère
L'accumulateur compteur, appliqué aux caractères :
function compterVoyelles(texte) {
const voyelles = "aeiouy"; // notre critère, sous forme de chaîne
let compteur = 0;
for (const lettre of texte.toLowerCase()) {
if (voyelles.includes(lettre)) { // appartenance au critère
compteur += 1;
}
}
return compteur;
}
console.log(compterVoyelles("algorithmique")); // 6
Notez l'usage élégant de includes sur une chaîne : elle teste la présence d'un caractère dans notre liste de voyelles. Le critère devient une donnée — facile à étendre ("aeiouyàâéè...").
Exercice
Testez chaque fonction au minimum sur : "", "a", "algorithmique" et "Esope reste ici et se repose".
inverser(texte)— refaites-la sans regarder.compterConsonnes(texte)— consonnes = lettres alphabétiques non voyelles ; décidez du traitement des espaces et chiffres, documentez-le.premiereVoyelle(texte)— renvoie la première voyelle rencontrée, ounull.contientMot(phrase, mot)— vrai simotapparaît dansphrase(une ligne suffit).estPalindromeNormalise(texte)— palindrome insensible à la casse et aux espaces.- Trace à la main : déroulez
estPalindromeBis("radar")tour par tour (valeurs de gauche/droite à chaque itération).
Résumé
- Chaîne = suite indexée de caractères, parcourable par
for...of, mais immuable. - Accumuler une chaîne : partir de
""et reconstruire ; l'ordre de concaténation EST l'algorithme. - Palindrome : par comparaison à l'inverse (simple) ou par deux curseurs (efficace, motif réutilisable).
- Normaliser avant de comparer ; chaque règle de normalisation est une décision de contrat.
Correction disponibleCherchez d’abord par vous-même.Voir la correction
Correction
Solutions commentées
function inverser(texte) {
let resultat = "";
for (const lettre of texte) {
resultat = lettre + resultat;
}
return resultat;
}
function compterConsonnes(texte) {
const voyelles = "aeiouy";
let compteur = 0;
for (const lettre of texte.toLowerCase()) {
// contrat choisi : on ne compte que a-z ; espaces, chiffres, ponctuation ignorés
if (lettre >= "a" && lettre <= "z" && !voyelles.includes(lettre)) {
compteur += 1;
}
}
return compteur;
}
function premiereVoyelle(texte) {
const voyelles = "aeiouy";
for (const lettre of texte.toLowerCase()) {
if (voyelles.includes(lettre)) {
return lettre; // sortie anticipée : inutile de chercher plus loin
}
}
return null; // aucune voyelle trouvée (ou texte vide)
}
function contientMot(phrase, mot) {
return phrase.includes(mot);
}
// Attention au contrat : includes teste la sous-chaîne, pas le MOT.
// contientMot("les os", "os") répond true... parce que « os » est dans « les ».
// Un vrai découpage en mots existe (.split), vu au chapitre suivant.
function estPalindromeNormalise(texte) {
const propre = texte.toLowerCase().replaceAll(" ", "");
return propre === inverser(propre);
}
Vérification sur les cas limites demandés
- Chaîne vide
"":inverser("")→""; palindrome →true(convention acceptable : le vide se lit pareil dans les deux sens) ;premiereVoyelle→null; compteurs →0. Chaque réponse doit être décidée, jamais subie — si votre fonction renvoie autre chose, relisez vos initialisations. - Un seul caractère
"a": palindrome par définition (un curseur gauche rejoint droite aussitôt) ; voyelle retournée immédiatement. - « Esope reste ici et se repose » : échoue en version stricte (casse + espaces), passe en version normalisée (hors accents : le « É » initial, abaissé en « é », diffère du « e » final — limite assumée de notre normalisation simple).
Question 6 — trace attendue sur "radar" (indices 0..4) :
tour 1 : gauche=0 ('r'), droite=4 ('r') → égaux → gauche=1, droite=3
tour 2 : gauche=1 ('a'), droite=3 ('a') → égaux → gauche=2, droite=2
tour 3 : gauche(2) < droite(2) est faux → sortie de boucle
résultat : true
Trois tours pour cinq caractères : la moitié des positions, précisément. Si votre trace diffère, rejouez-la ligne par ligne face au code — c'est le meilleur exercice de lecture d'algorithme de ce chapitre.