Cours:Technologies avancées du eLearning 2/exercices/Élèves
De GBLL, TAL, ALAO, etc.
Énoncé
On a un document HTML, qui contient une liste d’étudiant avec les notes obtenues lors du semestre, sous la forme suivante :
<h2>Facile</h2>
<ul id="fastoche">
<li>Maegan : 4, 8, 10, 12</li>
<li>Ajay : 15, 16, 12, 16</li>
<li>Sheri : 9, 10, 11, 15</li>
<li>Marianne : 10, 10, 10, 10</li>
</ul>
<h2>Moins facile</h2>
<ul id = "moyen">
<li>Gus : 4, 5.5, 9, 9.5, 10</li>
<li>Sammy : 3, 17</li>
<li>Belinda : 8.5, 11, 20</li>
</ul>
<h2>Très difficile</h2>
<ul id="chaud">
<li>Brayden: 12, 14 , 8.5 ; 12 ;</li>
<li>Kassidy : 13, 3.5 ;</li>
<li>Jonas : 18, 19, 16.5, 10.</li>
</ul>
On veut créer du code qui permette automatiquement de :
- Mettre en italiques le nom de l’élève ;
- Mettre en gras les notes supérieures à la moyenne ;
- Calculer et souligner la moyenne.
Informations utiles
DOMelt.children→ renvoie un tableau avec la liste des enfants d’un élément ;mot.charAt(i)→ (i+1)e caractère d’une chaine ;chaineOuTableau.length→ nombre d’éléments ;Number("25")→25(en tant queNumber).
Méthode 1 : une analyse descendante
On va procéder progressivement et créer un ensemble de fonctions que nous regrouperons ensuite :
function getEleves(id)idest une chaine de caractères contenant l'identifiant d'un élément du DOM ;- renvoie la liste des éléments contenus dans l'élément d'identifiant du DOM ;
function getPrenom(ligne)ligneest une chaine de caractères (telle que le contenu des<li>ci-dessus ;- renvoie une chaine de caractères ne contenant que le prénom de l'élève ;
function getNotes(ligne)ligneest une chaine de caractères (telle que le contenu des<li>ci-dessus ;- renvoie un tableau de
Numberavec toutes les notes ; - cette fonction nécessite une autre fonction :
function estNombre(caractere)caractereest un caractère (ou unestringde longueur 1 ;- renvoie un booléen :
truesi le caractère peut être contenu dans l'écriture d'un nombre ;falsesinon ;
function moy(tabNotes)tabNotesest un tableau de notes (Number) ;- renvoie un
Numberreprésentant la moyenne des nombres dans le tableau ;
function bilan(ligne)ligneest une chaine de caractères (telle que le contenu des<li>ci-dessus ;- renvoie une chaine de caractère répondant aux contraintes de l'énoncé ;
function traite(id)idest un identifiant d'élément du DOM qui contient une liste d'élève ;- remplace le contenu de l'élément par les informations mises en forme.
Correction
Voir une correction
//pour savoir si un caractère est un nombre (ou peut en faire partie)
function estNombre(char){
var resultat;
switch(char){
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
case "6":
case "7":
case "8":
case "9":
case ".":
resultat = true;
break;
default:
resultat = false;
}
return resultat;
}
//Pour récupérer toutes les informations nécessaires
function traiterLigne(ligne){
var i=0;
var resultat = {"prenom":"",
"notes":[ ],
"moyenne":false};
while (i < ligne.length
&& ligne.charAt(i) != " "
&& ligne.charAt(i) != ":"){
resultat.prenom += ligne.charAt(i);
i++;
}
if(i==ligne.length){
resultat=false;
}
else{
let nbCourant = "",
somme = 0;
while(i <= ligne.length){
if (i < ligne.length
&& estNombre(ligne.charAt(i))){
nbCourant += ligne.charAt(i); //chaine de caractère
}
else if(nbCourant!==""){
let laNote = Number(nbCourant);
somme += laNote ;
resultat.notes.push(laNote);
nbCourant = "";
}
i++;
}
if(resultat.notes.length>0){
resultat.moyenne = somme/resultat.notes.length
}
}
return resultat;
}
function ecrireLigne(semestre){
var resultat = "<i>" + semestre.prenom + "</i> : ";
for(let i=0; i<semestre.notes.length;i++){
if(i > 0){//pour les séparateurs
resultat = resultat +", "
}
if(semestre.notes[i]>=10){
resultat = resultat + "<b>" + semestre.notes[i] + "</b>";
}
else{
resultat = resultat + semestre.notes[i];
}
}
resultat = resultat + " → <u>" + semestre.moyenne + "</u>";
return resultat;
}
//prend un tableau du DOM et stocke les données extraites
//dans un tableau d'objets composites
function traiterClasse(id){
var elt = document.getElementById(id),
tableauDOM = elt.children;
for(let i=0;i<tableauDOM.length;i++){
tableauDOM[i].innerHTML = ecrireLigne(traiterLigne(tableauDOM[i].innerHTML));
}
}
//Programme principal
traiterClasse("fastoche");
traiterClasse("moyen");
traiterClasse("chaud");
Méthode 2 : création d'objets pour limiter le nombre de tours de boucles
Dans cette méthode, le code sera moins lisible car, en substance, les fonctions getPrenom(), getNotes(), moy() seront traitées en une seule fonction.
function estNombre(caractere)caractereest un caractère (ou unestringde longueur 1 ;- renvoie un booléen :
truesi le caractère peut être contenu dans l'écriture d'un nombre ;falsesinon ;
function traiterLigne(ligne)est la fonction qui regroupe les traitements degetPrenom(),getNotes()etmoy().- cette fonction prendra en entrée une chaine de caractères contenant les informations non mises en forme ;
- elle renverra un objet composite muni de 3 attributs :
prenom, le prénom de l'élève (string) ;notes, un tableau deNumberavec les notes de l'élève ;moyenne, unNumber, sa moyenne ;
function ecrireLigne(semestre)- prend un objet tel que celui renvoyé par
traiterLigne; - et génère en conséquences une
stringmise en forme en HTML ;
- prend un objet tel que celui renvoyé par
- enfin
function traiterClasse(id):- prend
id: une chaine de caractères contenant l'identifiant d'un élément du DOM ; - et modifie en conséquence cet élément du DOM.
- prend
Correction
https://codepen.io/lzbk/pen/OOBYJq
Voir une correction
//pour savoir si un caractère est un nombre (ou peut en faire partie)
function estNombre(char){
var resultat;
switch(char){
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
case "6":
case "7":
case "8":
case "9":
case ".":
resultat = true;
break;
default:
resultat = false;
}
return resultat;
}
//Pour récupérer toutes les informations nécessaires
function traiterLigne(ligne){
var i=0;
var resultat = {"prenom":"",
"notes":[ ],
"moyenne":false};
while (i < ligne.length
&& ligne.charAt(i) != " "
&& ligne.charAt(i) != ":"){
resultat.prenom += ligne.charAt(i);
i++;
}
if(i==ligne.length){
resultat=false;
}
else{
let nbCourant = "",
somme = 0;
while(i <= ligne.length){
if (i < ligne.length
&& estNombre(ligne.charAt(i))){
nbCourant += ligne.charAt(i); //chaine de caractère
}
else if(nbCourant!==""){
let laNote = Number(nbCourant);
somme += laNote ;
resultat.notes.push(laNote);
nbCourant = "";
}
i++;
}
if(resultat.notes.length>0){
resultat.moyenne = somme/resultat.notes.length
}
}
return resultat;
}
function ecrireLigne(semestre){
var resultat = "<i>" + semestre.prenom + "</i> : ";
for(let i=0; i<semestre.notes.length;i++){
if(i > 0){//pour les séparateurs
resultat = resultat +", "
}
if(semestre.notes[i]>=10){
resultat = resultat + "<b>" + semestre.notes[i] + "</b>";
}
else{
resultat = resultat + semestre.notes[i];
}
}
resultat = resultat + " → <u>" + semestre.moyenne + "</u>";
return resultat;
}
//prend un tableau du DOM et stocke les données extraites
//dans un tableau d'objets composites
function traiterClasse(id){
var elt = document.getElementById(id),
tableauDOM = elt.children;
for(let i=0;i<tableauDOM.length;i++){
tableauDOM[i].innerHTML = ecrireLigne(traiterLigne(tableauDOM[i].innerHTML));
}
}
//Programme principal
traiterClasse("fastoche");
traiterClasse("moyen");
traiterClasse("chaud");
Méthode 3
Pour gagner en modularité et donc en maintenabilité, nous allons créer une classe d'objets Eleve, qui encapsulera données et traitements.
NB : contrairement à ce qui a été dit en cours, il n'y a plus besoin du "use strict";, pour utiliser les classes dans les navigateurs modernes.
La class Eleve :
- devra faire figurer les trois attributs des objets de la méthode 2, qui seront initialisés dans son constructeur (
constructor(prenom)) ; - elle possèdera un setter :
setNom(prenom): prend unestringen entrée et la stocke dans l'attribut correspondant ;
- la méthode
ajouterNote(note)prend un nombre et l'ajoute à la liste de notes, tout en mettant à jour la moyenne de l'élève en conséquence ; - et une liste de getters :
getNom(): renvoie le nom de l'objetEleveconcerné ;getMoy(): renvoie la moyenne de l'objetEleveconcerné ;getTabNotes(): renvoie un tableau des notes de l'objetEleveconcerné ;getNbNotes(): renvoie le nombre de notes obtenues par l'élève ;
- enfin la méthode
toString(): transforme l'objet en chaine de caractère au format défini ci-dessus.
Pour tester la classe vous pourrez utiliser le code suivant :
//test du code
var meg = new Eleve("Megan");
console.log("¿Megan? → ",meg.getNom());
console.log("¿notes et moyenne quand il n'y en a pas encore? →",meg.getNotes(), meg.getNbNotes(), meg.getMoy());
meg.ajouterNote(12);
console.log("¿[12], 1, 12? →",meg.getNotes(), meg.getNbNotes(), meg.getMoy());
meg.ajouterNote(15.5);
console.log("¿[12,15.5], 2, 13.75? →",meg.getNotes(), meg.getNbNotes(), meg.getMoy());
meg.ajouterNote(13);
console.log("¿[12,15.5,13], 3, 13.5? →",meg.getNotes(), meg.getNbNotes(), meg.getMoy());
console.log("<li>"+meg+"</li>");
Quand cela sera fait vous pourrez modifier le constructeur pour qu'il puisse prendre une ligne ou un prénom :
constructor(prenomOuLigne, isLigne) traite une ligne quand isLigne est vrai, traite un prénom sinon.
Correction
Voir une correction
var msg = "Nooooooon, enfin… ",
msgs = ["C'est un peu tôt pour ",
"vous creuser les méninges. ",
"Mais on va y arriver. ",
"Là, il faut encore ",
"que je mette une correction. "
];
for(var i=0; i<=msgs.length/2-1;i++){
if(i%2==0){
msg += msgs[i]+msgs[msgs.length-1-i];
}
else{
msg += msgs[msgs.length-1-i]+msgs[i];
}
}
if(i !== msgs.length/2){
msg = msg+msgs[i];
}
console.log(msg);
//pour savoir si un caractère est un nombre (ou peut en faire partie)
function estNombre(char){
/* cf. précédemment */
}
//La classe élève
class Eleve{
//prend une ligne et crée un élève en conséquence…
constructor(ligneOrPrenom, isLigne){
this.notes = [] ;
this.moy = false;
this.prenom = "";
if(typeof isLigne == "undefined" || isLigne === false){
//constructeur de base quand isLigne est indéfini ou faux
this.setNom(ligneOrPrenom) ;
}
else{//sinon cf. traiterLigne
var i=0;
while (i < ligneOrPrenom.length
&& ligneOrPrenom.charAt(i) != " "
&& ligneOrPrenom.charAt(i) != ":"){
this.prenom += ligneOrPrenom.charAt(i);
i++;
}
if(i<ligneOrPrenom.length){
let nbCourant = "";
while(i <= ligneOrPrenom.length){
if (i < ligneOrPrenom.length
&& estNombre(ligneOrPrenom.charAt(i))){
nbCourant += ligneOrPrenom.charAt(i); //chaine de caractère
}
else if(nbCourant!==""){
this.ajouterNote(Number(nbCourant));
nbCourant="";
}
i++;
}
}
}
}
setNom(prenom){
this.prenom = prenom;
}
getNom(){
return this.prenom;
}
getNbNotes(){
return this.notes.length ;
}
getNotes(){
return this.notes;
}
getMoy(){
return this.moy ;
}
ajouterNote(note){
if(this.getNbNotes() > 0){
this.moy = (this.moy * this.getNbNotes() + note)/(this.getNbNotes()+1);
}
else{
this.moy = note ;
}
this.notes.push(note);
}
toString(){
var resultat = "<i>" + this.getNom() + "</i> : ";
for(let i=0; i<this.getNbNotes();i++){
if(i > 0){//pour les séparateurs
resultat = resultat +", "
}
if(this.notes[i]>=10){
resultat = resultat + "<b>" + this.notes[i] + "</b>";
}
else{
resultat = resultat + this.notes[i];
}
}
resultat = resultat + " → <u>" + this.getMoy() + "</u>";
return resultat;
}
}
//Le programme principal adapté
function traiterClasse(id){
var tableauDOM = document.getElementById(id).children;
for(let i=0;i<tableauDOM.length;i++){
tableauDOM[i].innerHTML = "" + new Eleve(tableauDOM[i].innerHTML, true);
//transcodé en string avec toString() automatiquement
}
}
//test de la classe
var meg = new Eleve("Megan");
console.log("¿Megan? → ",meg.getNom());
console.log("¿notes et moyenne quand il n'y en a pas encore? →",meg.getNotes(), meg.getNbNotes(), meg.getMoy());
meg.ajouterNote(12);
console.log("¿[12], 1, 12? →",meg.getNotes(), meg.getNbNotes(), meg.getMoy());
meg.ajouterNote(15.5);
console.log("¿[12,15.5], 2, 13.75? →",meg.getNotes(), meg.getNbNotes(), meg.getMoy());
meg.ajouterNote(13);
console.log("¿[12,15.5,13], 3, 13.5? →",meg.getNotes(), meg.getNbNotes(), meg.getMoy());
console.log("<li>"+meg+"</li>");
//Programme principal
traiterClasse("fastoche");
traiterClasse("moyen");
traiterClasse("chaud");