Ben on a l'âge de ses artères, mon lapin. Et si ça t'es pas jouasse, marche à l'ombre.
:ptdr::ptdr::ptdr:
Version imprimable
Voila la doc sur mon code JS précédent : (https://www.developpez.net/forums/d2.../ )
Quelques explications…
Tout d’abord, ce code utilise « intensivement » la technique de chainage présente dans JS
exemple :
ce qui est équivalent àCode:
1
2
3
4
5
6 const leTexte = document.querySelector('#mon-texte') const decoup = leTexte .textContent .toLowerCase() .match(/[a-zàèìòùáéíóúýâêîôûãñõäëïöüÿçߨøåæ]+/g)
au passage j’ai changé le regex duCode:
1
2
3
4
5 const leTexte = document.querySelector('#mon-texte') let x1 = leTexte.textContent let x2 = x1.toLowerCase() const decoup = x2.match(/[a-zàèìòùáéíóúýâêîôûãñõäëïöüÿçߨøåæ]+/g)
split(/[^a-zA-ZàèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜçÇߨøÅ寿]/).
par : .match(/[a-zàèìòùáéíóúýâêîôûãñõäëïöüÿçߨøåæ]+/g).
puisqu’il est précédé par une méthode qui transforme toutes les lettres en minuscules ; donc inutile de tester avec des lettres majuscules dans le regex,
+ c’est aussi une optimisation.
+ remplacement du split par un match ( merci CosmoKnacki ;) )
À la suite j’ai ajouté d’autres méthodes :
1 – .filter(s=>(s!==undefined && s.length > 3) ).
celle ci réalise un filtrage sur l’array généré précédement , c.a.d. qu’elle génère un nouvel array ne contenant que les "vrais" mots ayant plus de 3 lettres.
La doc sur cette fonction => https://developer.mozilla.org/fr/doc...x/Array/filter
pour mémo, j’ai utilisé ici une fonction fléchée équivalente à function(s) { return ( s!==undefined && s.length > 3) } ..
2 – .sort((a, b) => a.localeCompare(b)).
un grand classique pour trier du texte, la comparaison est réalisée par la méthode localeCompare()=> https://developer.mozilla.org/fr/doc.../localeCompare
utile pour toutes langues utilisant des signes diacritiques sur ses caractères (les accents sur les lettres )
3 – .reduce((a,c,i,t)=> { ... }, []).
La fameuse méthode reduce applicable aux array JS => https://developer.mozilla.org/fr/doc...x/Array/reduce
ici avec tous les arguments possibles (a,c,i,t)et même l’option d’instanciation : , [].
cette méthode permet de parcourir l’Array, chaque occurrence étant représenté ici par l’élément c.
(désigne la currentValue, donc on utilise cette lettre « c » par convention), le i représente l’index courant sur le tableau, ici désigné par l’argument t .
il faut bien voir qu’ici, le tableau sur lequel cette méthode s’exécute n’existe que dans la mémoire du système, et dans le cas présent il sera utile de pouvoir s’y référer.
Quant au paramètre a c’est un accumulateur.
Cet accumulateur représente la valeur de retour de la méthode reduce.
Cet élément central récupère au fur et à mesure de l’itération sur la table les données que l’on souhaite retrouver en sortie.
Ici cet accumulateur commence avec la valeur [] : un array vide (sinon il commence avec le premier item de l’array en entrée.
Ici l’idée c’est de créer un tableau ressemblant à ça :
alors qu’initialement on a : [ 'envoler', 'évada', 'évadé', ... 'évadé', 'évader', ... 'évader' ].Code:
1
2
3
4
5 [ { m: 'envoler', n: 1 } , { m: 'évada', n: 1 } , { m: 'évadé', n: 11 } , { m: 'évader', n: 3 } ]
d’où la fonction fléchée
si le currentItem est différent de l’item précédent (ou si c’est le premier) on ajoute dans l’accumulateur un nouvel objet {m:c, n:1} exemple { m: 'envoler', n: 1 }.Code:
1
2
3 if (i===0 || t[i-1]!==c ) a.push ({m:c, n:1}) else a[a.length-1].n++ return a
sinon, ben, on augmente d’un point le dernier objet inséré dans l’accumulateur a[a.length-1].n++.
et a la fin on renvoie l’accumulateur pour la boucle suivante ou la fin de la partie.
Je profite ensuite de l’affichage de ce résultat pour renseigner une liste des nombres d’ocurences trouvée, via un Set JS => https://developer.mozilla.org/fr/doc...ts_globaux/Set
let occurences = new Set().
que je remplis au fur et à mesure d’une boucle classique
ce qui donne le set suivant pour cet exemple occurences : 1 / 2 / 9 / 11 / 3 / 7 / 5 / 8sinon la boucle est aussi utilisée pour remplir la table <table id="result"></table>via ce code:Citation:
for (let mot of decoup)
{
occurences.add(mot.n)
Code:
1
2
3
4 let nRow = leResult.insertRow(-1) // nouveau <tr></tr> en fin de table , rCell = 0 // indice du premier <td></td> nRow.insertCell(rCell++).textContent = mot.m // crée et rempli le premier <td> avec le mot.m ( ex: 'envoler') nRow.insertCell(rCell++).textContent = mot.n // crée et rempli le premier <td> avec le mot.n ( son nombre d'occurence )
la boucle suivante sert à afficher la liste des termes correspondant à chaque occurrence.
Petit problème: cette liste est un objet Set, sur lequel on ne peut effectuer un tri, utile pour présenter les occurrences par ordre croissant,
donc faut le transformer en Array: [...occurences] qui est en fait de la déclaration d’un Array [ ] que l’on remplit à l’aide du spread operator (les trois petits points ... que l’on peux appliquer sur le Set (parce qu’il est itérable).
La doc : https://developer.mozilla.org/fr/doc...%A9composition
donc on a un Array qu’on peut trier .sort((a, b)=>a-b)…
et que l’on peut parcourir via un .forEach(o=> j’ai pris la lettre o pour argument, vu qu’il s’agit d’un item dans le Set occurrences
après c’est pareil pour remplir le tableau sauf que cette fois-là il faut retrouver tous les termes correspondants sur une occurrence
Code:
1
2
3
4 decoup // sur cet Array .filter(d=>d.n===o) // ne garde que ceux ayant la même valeur que l'occurence courante .map(e=>e.m) // crée un tableau avec uniquement les termes ( [ ..., { m: 'évader', n: 3 }, ...] devient [ ..., 'évader', ...] ) .join(', ') // transfo de cet array en String séparé par des virgules...
Mieux vaut un bon match qu'un mauvais split (ce qui évite les chaînes vides):
Code:
1
2 .toLowerCase() .match(/[a-zàèìòùáéíóúýâêîôûãñõäëïöüÿçߨøåæ]+/g)
Faut mieux ne pas trop jouer avec les non-déclarations de variables, et ça donne tout de suite une mauvaise image sur le codeur.
éviter les variables globale aussi, car après on risque de se retrouver avec des effets de bord
C'est l'un de mes pires cauchemars sur la maintenance de code :
obligé de décortiquer des centaines de pages de code pour savoir si une variable globale était volontairement changée par une fonction 10 pages plus loin, ou si ce devait être une autre variable qui ne devrait pas porter le même nom,
et c'était loin d'être un cas unique...
donc autant que possible ce limiter le nombre de variables globales au strict minimum , et encapsuler toutes les variables, voire les mettre dans des closures pour qu'elles soient inaccessibles par 'l'extérieur"
Merci, c'est une explication détaillée !
Ça va j'avais compris la majorité...
Merci.
Ben c'est clair qu'il vaut mieux éviter, je ne défend pas le contraire... J'ai juste fait remarqué que puisqu’elles n'étaient pas déclarées (avec var ou let) elles étaient du coup globales ce qui est vrai (même si il y a une différence subtile).
Comme j'avais dit en ce moment j'ai la tête dans Python et on ne déclare pas les variables dans ce langage... Or le code JS que j'ai posté je l'ai traduit d'un code Python que j'avais écrit juste avant et j'avais oublié d'ajouter les déclarations des variables...
Et j'étais surtout concentré sur l'algorithme...
Oui effectivement, je le dit aussi parfois...
Oui les closures et aussi les modules...
Visiblement on n’a pas tous la même vision de « Peut-on améliorer le code ? »
Pour moi, améliorer le code c’est, entre autre, améliorer la lisibilité et donc la maintenance de celui-ci, la concision parfois incompatible avec la lisibilité, permettre les tests, séparer les tâches à effectuer afin de simplifier la complexité de celles-ci ... et pourquoi pas optimiser.
Indépendamment des bonnes manières à respecter, déclaration des variables …, et ce quelque soit le langage, une des améliorations passerait par la création d’une fonction réutilisable par tâche à accomplir.
On définie la fonction avec
- Ce qu’elle attend en entrée
- Le traitement a effectué
- ce qu’elle l’on retourne pour exploitation dans le reste du code.
On pourrait donc imaginer la définition de la fonction, son prototype, comme suit
@psychadelic :Code:
1
2
3
4
5
6
7
8
9
10
11 /** * @param {String} texte Texte à traiter * @param {Number} minlength Longueur minimale des mots à prendre en compte * @returns {Array of Array} result - Tableau des données sous forme [[mot1, nbr1], [mot1, nbr2], [ ] */ function getOccurrenceTexte(text, minlength) { // déclaration des variables // préparation des données // traitement des données return result; }
•••
Il manque à minima un test sur la donnée de départ, que se passe t-il si le texte est null ?
•••
Pour moi dans bien des cas le chaînage entraîne une perte de lisibilité et est accessoirement plus délicat à déboguer.Citation:
Tout d’abord, ce code utilise « intensivement » la technique de chainage présente dans JS
•••
Pourquoi ne pas le faire dans reduce, on réduit ainsi le nombre d’itérations nécessaires pour obtenir le même résultat !?!Citation:
.filter(s=>(s!==undefined && s.length > 3) )
•••
L’amélioration du code passe aussi par le choix judicieux du nom des variables.Citation:
.reduce((a,c,i,t)=> // crée un tableau d'objet
Pourquoi utiliser reduce, => réduire, alors que ce n’est pas vraiment, parmi toutes les méthodes disponibles, la mieux adaptée et ce au moins sémantiquement parlant ne serait ce que par rapport à forEach.
Il est à noter que, si l’on parle d’optimisation/performance, les méthodes forEach et reduce seront moins performante qu’une bonne boucle for du fait de l'appel à une fonction callback, mais ceci ne doit pas forcément être un critère !
•••
Là totale incompréhension de ma part, déjà ici on aborde une autre partie, la sortie affichage, et cela devrait donc se trouver dans une fonction indépendante en utilisant les données reçues.Citation:
let occurences = new Set()
De plus je ne vois pas l’intérêt de passer par un new Set quand les données sont disponibles, pour l'affichage suivant. Tu as déjà « en sortie »
Il en est de même pour l’affichage sous forme de <table> et l’utilisation deCode:
1
2
3
4
5
6
7
8
9 decoup = [ { "m": "abri", "n": 1 }, { "m": "affaler", "n": 1 },
Mais là pour le coup, dans ton code, je pense que cela limite pas mal la gestion des lignes.Code:
1
2
3
4 decoup .filter(d => d.n === o) .map(e => e.m) .join(', ')
•••
Donc oui tes explications sont très MDNesque mais à mon avis tu t’es plus fait plaisir qu’amélioré le code de départ.
En gros comme le dit Beginner.
Donc pas franchement une amélioration mais une autre façon de faire.Citation:
Et oui celui de psychadelic nécessite un peu plus de temps pour être compris et le débogueur aide bien lol...
Un dernier point, omettre le ; (point virgule) en fin d’instruction peut rendre le code inutilisable en cas de minification et même parfois dans le code non minifié.
@Beginner. :
peut importe mais surement une sur base forEach, pour la sémantique ou for pour l'optimisation éventuelle.Citation:
Tu aurais utiliser une autre boucle ?
Cela pourrait donner, en tenant compte de certaines remarques
L'appel à celle-ci se faisant simplement par exempleCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 /**---------------------------------------------------------------------- * Fonction de récupération du nombre doccurrence des mots dans un texte *----------------------------------------------------------------------- * @param {String} texte Texte à traiter * @param {Number} minlength Longueur minimale des mots à prendre en compte * @returns {Array of Array} result - Tableau des données sous forme [[mot1, nbr1], [mot1, nbr2], [ ] */ function getOccurrenceTexte(text = "", minlength = 3) { const objResult = {}; const CosmoKnackiReg = /[a-zàèìòùáéíóúýâêîôûãñõäëïöüÿçߨøåæ]+/g; // préparation et récupération des données const tabMots = text.toLowerCase().match(CosmoKnackiReg); if (tabMots) { // récupération de toutes les occurrences tabMots.forEach( (el) => { if( el.length >= minlength){ if (!objResult[el]) objResult[el] = 0; objResult[el] += 1 } }); } // convert en array d'array const tabResult = Object.entries(objResult); // on peut trier dans l'ordre alpha par défaut tabResult.sort((a, b) => a[0].localeCompare(b[0])); return tabResult; }
Le contenu de la boucle pouvant varier, ce ne sont pas les choix qui manquentCode:
1
2
3 const texte = document.querySelector('#mon-texte').textContent; const result = getOccurrenceTexte(texte); // les données sont ici disponible pour en faire l'affichage par exemple.
ou encoreCode:
1
2
3
4
5 tabMots.forEach( (el) => { if( el.length >= minlength){ objResult[el] ? objResult[el] += 1: objResult[el] = 1; } });
et il y en a d'autres.Code:
1
2
3
4
5
6
7
8
9
10 tabMots.forEach( (el) => { if( el.length >= minlength){ if (objResult[el]) { objResult[el] += 1; } else { objResult[el] = 1; } } });
[EDIT] Il s'est permis, j'ai corrigé, je manque d'air sur ce coup !
Si je peux me permettre...
On écrit "Occurrence", avec 2 "c" et 2 "r". :aie:
tu peux te permettre ;)Citation:
Si je peux me permettre...
Un mot:
Un objet global contenant toutes les références à une des fonctionnalités de la page ne me semble pas une aberration du point de vue lisibilité et maintenance. Un autre objet définira les variables liées à une autre fonctionnalité et puis voilà. Question performance, je n'y connais pas grand chose mais bon: essayez de créer une page web nécessitant JS sans déclarer à un moment ou à un autre un objet... ça revient à se passer de JS.Citation:
Envoyé par "NoSmoking
Je retiens les mêmes valeurs que NoSmoking. On peut étaler sa science à l'infini, ça ne fait pas progresser d'un millimètre l'utilisateur de l'appli qui demande essentiellement de l'ergonomie. Et je ne suis pas allé vérifier la définition sur le CNRTL, mon dico préféré!
Il y a bien une différence quand même et certains disent que pour que ce soient des variables il faut les déclarer sinon ce ne sont pas des variables à proprement parlé... Bon après dans la pratique, dans la plupart des cas la différence ne se voit pas...
* Exemple About variables :
Bon après il y a quand même des références qui disent (tout en expliquant ensuite les différences) :Citation:
Often various articles and even books on JavaScript claim that: “it is possible to declare global variables using var keyword (in the global context) and without using var keyword (in any place)”. It is not so. Remember:
variables are declared only with using var keyword.
...
Suite : https://developer.mozilla.org/fr/doc...structions/varCitation:
Si on affecte une valeur à une variable qui n'a pas été déclarée (le mot-clé var n'a pas été utilisé), cela devient une variable globale (une propriété de l'objet global) lorsque l'affectation est exécutée. Les différences entre les variables déclarées et les variables non-déclarées sont :
Les variables déclarées sont contraintes dans le contexte d'exécution dans lequel elles sont déclarées. Les variables non-déclarées sont toujours globales.
...
une propriété de l'objet global ---> dans un navigateur c'est l'objet window et j'ai testé et vérifié ce point sur Chrome
Mais bon dans tous les cas c'est déconseillé, c'est à éviter, nous sommes d’accord...