Une moyenne pondérée
Dans les 3 versions du code, le nom des fonctions à utiliser sont en anglais :
- SUM() pour SOMME()
- IF() pour SI()
- ISNUMBER(() pour ESTNUM()
- OFFSET() pour DECALER
Pour MS Excel et LO Calc, le langage de programmation est VBA, pour G Sheet, c'est Javascript.
Les objets utilisés, les propriétés et méthodes qui leurs sont appliquées, sont décrites dans les billets respectifs :
1. Google Sheet
Aucune difficulté n'est à signaler dans ce code.
La 1ère boucle calcule le nombre de devoirs NbDevoirs (pour la suite je suppose que ce calcul rend la valeur 4).
Les 2 variables de type "chaînes de caractères (string) "lesCoef" et "lesNotes" sont utilisées pour simplifier l'écriture et éviter les fautes de frappe lors de la saisie des variables de type chaîne "leNum" (le numérateur) et "leDeno" (le dénominateur) de la formule finale (variable "laFormule").
lesCoef qui a pour valeur "OFFSET(D3;0;0;1;4)" lorsque NbDevoirs vaut 4 ;
lesNotes qui est calculée à chaque tour de la 2ème boucle et qui vaudra par exemple "OFFSET(D6;0;0;1;4)" au 3ème tour.
A chaque tour t de la 2ème boucle, la variable chaîne "laFormule" est calculée puis est passée en paramètre de la méthode formulaArray de la cellule At pour produire l'affichage cherché.
2. Microsoft Excel (v2010)
Remarquons l'utilisation des méthodes COUNTA() et COUNT() sur l'objet racine WorksheetFunction pour calculer respectivement :
nbEleves : le nombre de tour de boucles à effectuer ;
nbDevoirs : la longueur des plages de cellules.
L'objet racine WorksheetFunction ne dispose pas de la méthode OFFSET. Le calcul du nombre de devoir (1ère boucle du code) qui est le résultat de :
COUNT(OFFSET($" + CStr(i) + ":$" + CStr(i) + ";0;3;1;COUNTA($" + CStr(i) + ":$" + CStr(i) + ")))"
nécessite donc d'adapter le calcul de la plage sur laquelle porte l'opération : OFFSET().
Par exemple, pour obtenir la plage "C3:C6" correspondant à OFFSET(C3;0;0;1;4), il faut appliquer successivement les méthodes Offset() et Resize() sur l'objet de type Range "C3" (la cellule C3) par :
var laPlage
set laPlage = ActiveSheet.Range("C3").Offset(0, 0).Resize(1,4)
Le 2nd paramètre de resize() se calcule par la méthode COUNTA() de l'objet système WorksheetFunction.
Cela explique le code de la 1ère boucle :
Set laPlage = ActiveSheet.Range("$" + CStr(i) + ":$" + CStr(i))
Set laPlage = ActiveSheet.Range("C" + CStr(i)).Offset(0, 0).Resize(1, WorksheetFunction.CountA(laPlage))
Il ne reste plus qu'à compter le nombre de nombres de la plage résultat en appliquant la méthode COUNT() de l'objet système WorksheetFunction sur l'objet "laPlage" obtenu.
nbNoteI = WorksheetFunction.Count(laPlage)
A l'intérieur de la boucle 2 qui calcule la moyenne pondérée apparait une difficulté d'un autre ordre.
En effet, en supposant qu'il y a 4 devoirs à compter, le code suivant qui calcule la moyenne pondérée produit l'erreur d'exécution 1004 due dans mon cas à une chaîne de caractère dont la taille dépasse les 200 caractères. Ce programme (expliqué ICI) fonctionne cependant parfaitement sous Google Sheet (onglet 1 ci-dessous) et sous LO Calc (onglet 3 ci-dessous).
lesCoef = "OFFSET(D2,0,0,1,4)"
lesNotes = "OFFSET(D3,0,0,1,4)"
'la moyenne
leNum = "(SUM(IF(ISNUMBER(" + lesCoef + "),IF(ISNUMBER(" + lesNotes + ")," + lesNotes + "*" + lesCoef + ",0)," + lesNotes + ")))"
leDeno = "(SUM(IF(ISNUMBER(" + lesNotes + "),IF(ISNUMBER(" + lesCoef + ")," + lesCoef + ",1),0)))"
MsgBox Len(leNum) + Len(leDeno)
'Cette ligne fonctionne
ActiveSheet.Cells(20, 1).FormulaArray = "=" + leNum
'Cette ligne fonctionne
ActiveSheet.Cells(20, 1).FormulaArray = "=" + leDeno
'Cette ligne produit l'erreur d'exécution
ActiveSheet.Cells(20, 1).FormulaArray = "=" + leNum + "/ " + leDeno
Pour outrepasser cette erreur, j'ai modifié le code pour raccourcir les chaînes "leNum" et "leDeno" en calculant le décalage en dehors de la formule grâce aux méthodes OFFSET et RESIZE utilisées comme indiqué au début de ce chapitre pour obtenir les plages "lesCoef" et "lesNotes". Attention, maintenant les variables "lesCoef" et "lesNotes" sont des objets "range" et non des chaînes de caractères.
Enfin, grâce à la propriété address de ces objets ("lesCoef.address et "lesNotes.address), nous construisons les chaînes "leNum" et "leDeno".
Remarquez enfin :
les 3 arguments de la fonction IF qui sont séparés par des virgules selon la syntaxe imposée ;
l'utilisation de la méthode formulaArray sur la cellule qui affichera la moyenne.
3. Libre Office Calc (v7.14)
Remarquons l'utilisation de la méthode "callFunction" d'une instance du service "com.sun.star.sheet.FunctionAccess" pour utiliser les fonctions de calcul interne COUNTA et COUNT pour calculer respectivement :
- nbEleves : le nombre de tour de boucles à effectuer ;
- nbDevoirs : la longueur des plages de cellules
Aucune difficulté n'est à signaler dans ce code.
La 1ère boucle calcule le nombre de devoirs NbDevoirs (pour la suite je suppose que ce calcul rend la valeur 4).
Les 2 variables de type "chaînes de caractères (string) " lesCoef" et "lesNotes" sont utilisées pour simplifier l'écriture et éviter les fautes de frappe lors de la saisie des variables de type chaîne "leNum" (le numérateur) et "leDeno" (le dénominateur) de la formule finale (variable "laFormule").
- 1esCoef qui a pour valeur "OFFSET(D3;0;0;1;4)" lorsque NbDevoirs vaut 4
- lesNotes qui est calculée à chaque tour de la 2ème boucle et qui vaudra par exemple "OFFSET(D6;0;0;1;4)" au 3ème tour
A chaque tour t de la 2ème boucle la variable chaîne "laFormule" est calculée puis est passé en paramètre de la méthode formulaArray de la cellule At pour produire l'affichage cherché.
4. Le code dans les 3 versions
function calculerMoyenne(){
var laCelluleBcle
var laZone
var laFeuille = SpreadsheetApp.getActiveSheet();
var laCellule = laFeuille.getRange("A2");
laCellule.setValue ="Moyenne pond.";
//la cellule A3 contiendra le nb de ligne contenant des noms d'élèves donc le nombre d'eleves
//=NBVAL($C:$C)
LaCellule = laFeuille.getRange("A3");
laFormule = "=COUNTA($C:$C)";
laCellule.setFormula(laFormule);
nbEleves = laCellule.getValue();
//Calcul du nb de devoirs donnés
//Bcler car peut être absence à un devoir
var nbDevoirs = 0;
LaCellule = laFeuille.getRange("A"+ (nbEleves + 2));
for ( i = 4; i < (nbEleves +3) ; i++) {
laFormule = "=COUNT(OFFSET($"+i+":$"+ i+";0;3;1;COUNTA($"+i+ ":$"+i+")))";
laCellule.setFormula(laFormule);
var nbNoteI=laCellule.getValue()
if (nbDevoirs < nbNoteI) {
nbDevoirs = nbNoteI
}
}
// lesCoef est la plage D3:G3 si 4 devoirs
var lesCoef = "OFFSET(D3;0;0;1;"+nbDevoirs+")";
for ( i = 4; i < (nbEleves +3) ; i++) {
// lesNotes est par exemple la plage D7:G7 (eleve n° 5)
var lesNotes = "OFFSET(D"+ i + ";0;0;1;"+nbDevoirs+")";
//la moyenne
var leNum ="SUM(IF(ISNUMBER("+lesCoef+");IF(ISNUMBER("+lesNotes+");"+lesNotes+"*"+lesCoef+";0);"+lesNotes+"))";
var leDeno = "SUM(IF(ISNUMBER("+lesNotes+");IF(ISNUMBER("+lesCoef+");"+lesCoef+";1);0))";
laFormule = "=ARRAYFORMULA(" + leNum + "/" + leDeno + ")"
var laCelluleBcle = SpreadsheetApp.getActiveSheet().getRange( i , 1 );
laCelluleBcle.setFormula(laFormule);
}
}
Sub calculerMoyenne()
Dim laCellule As Object, laCelluleBcle As Object, laPlage As Object
Dim laNote As Double, nbNoteI As Double
Dim nbEleves As Integer, nbDevoirs As Integer
Dim i As Integer
Dim nomCel As String, laFormule As String
Dim lesCoef As Object, lesNotes As Object
Dim leNum As String, leDeno As String
'
Set laCellule = ActiveSheet.Range("A2")
laCellule.Value = "Moyenne pond."
'
'le nombre d'élèves
'=NBVAL($C:$C)- 1
Set laPlage = ActiveSheet.Range("$C:$C")
nbEleves = WorksheetFunction.CountA(laPlage) - 1
'
Set laCellule = ActiveSheet.Range("A1")
nbDevoirs = 0
For i = 3 To nbEleves + 2
'COUNT(OFFSET($" + CStr(i) + ";0;0;1;COUNTA($" + CStr(i) + ":$" + CStr(i) + ")))"
' la fonction OFFSET pas définie dans worksheetFunction d'ou adaptation
Set laPlage = ActiveSheet.Range("$" + CStr(i) + ":$" + CStr(i))
Set laPlage = ActiveSheet.Range("C" + CStr(i)).Offset(0, 0).Resize(1, WorksheetFunction.CountA(laPlage))
nbNoteI = WorksheetFunction.Count(laPlage)
If nbDevoirs < nbNoteI Then
nbDevoirs = nbNoteI
End If
Next i
'
'lesCoef est la plage D2:2 si 4 devoirs
'lesCoef = "OFFSET(D2,0,0,1," + CStr(nbDevoirs) + ")"
Set lesCoef = ActiveSheet.Range("D2").Offset(0, 0).Resize(1, nbDevoirs)
'
For i = 3 To nbEleves + 2
'lesNotes est par exemple la plage D7:G7 (eleve n° 5)
'lesNotes = "OFFSET(D" + CStr(i) + ",0,0,1," + CStr(nbDevoirs) + ")"
Set lesNotes = ActiveSheet.Range("D" + CStr(i)).Offset(0, 0).Resize(1, nbDevoirs)
'la moyenne
leNum = "SUM(IF(ISNUMBER(" + lesCoef.Address + "),IF(ISNUMBER(" + lesNotes.Address + ")," + lesNotes.Address + "*" + lesCoef.Address + ",0)," + lesNotes.Address + "))"
leDeno = "SUM(IF(ISNUMBER(" + lesNotes.Address + "),IF(ISNUMBER(" + lesCoef.Address + ")," + lesCoef.Address + ",1),0))"
laFormule = "=" + leNum + "/" + leDeno
Set laCelluleBcle = ActiveSheet.Cells(i, 1)
laCelluleBcle.FormulaArray = laFormule
Next i
End Sub
function calculerMoyenne ()
Dim FAS as Object
Dim laFeuille as object, laCellule as object, laCelluleBcle as Object
Dim leForm as Object
Dim laZone as object
Dim laPlage as object
Dim info as object
Dim laNote as double
Dim nbEleves as integer, nbNoteI as integer, nbDevoirs as Integer
dim i as integer
Dim lesCoef as string, lesNotes as string, leNum as string, leDeno as string
FAS = CreateUnoService("com.sun.star.sheet.FunctionAccess")
laFeuille =thisComponent.currentController.activeSheet
LaCellule = laFeuille.getCellRangeByName("A2")
laCellule.string ="Moyenne pond."
'la cellule A1 contient le nb de ligne contenant des noms d'élèves
'=NBVAL($C:$C)- 2
LaCellule = laFeuille.getCellRangeByName("A1")
laZone = thisComponent.currentController.activeSheet.getCellRangeByName("$C:$C")
nbEleves = FAS.callFunction("COUNTA", Array(laZone))
nbDevoirs=0
for i = 4 to nbEleves +1
laFormule ="=COUNT(OFFSET($"+i+":$"+ i+";0;3;1;COUNTA($"+i+ ":$"+i+")))"
laCellule.Formula = laFormule
nbNoteI = laCellule.Value
if nbDevoirs < nbNoteI then
nbDevoirs = nbNoteI
end if
next i
'lesCoef est la plage D3:G3 si 4 devoirs
lesCoef = "OFFSET(D2;0;0;1;"+Cstr(nbDevoirs)+")"
for i = 3 to nbEleves
'lesNotes est par exemple la plage D7:G7 (eleve n° 5)
lesNotes = "OFFSET(D"+ CStr(i) + ";0;0;1;"+CStr(nbDevoirs) +")"
'la moyenne
leNum ="SUM(IF(ISNUMBER("+lesCoef+");IF(ISNUMBER("+lesNotes+");"+lesNotes+"*"+lesCoef+";0);"+lesNotes+"))"
leDeno = "SUM(IF(ISNUMBER("+lesNotes+");IF(ISNUMBER("+lesCoef+");"+lesCoef+";1);0))"
laFormule = "=" + leNum + "/" + leDeno
laCelluleBcle = laFeuille.getCellByPosition( 0 , i -1 )
laCelluleBcle.ArrayFormula = laFormule
next i
end function