I need to calculate a simple weighted average in a XForms form. How can I do that in an elegant and declarative way using XPath and/or XQuery ?
[EDITED] This is the source XML document :
<Examens>
<Examen>
<ExamenId>1</ExamenId>
<Coef>1</Coef>
<Notes>
<Note>
<EleveId>100</EleveId>
<Valeur>4</Valeur>
</Note>
<Note>
<EleveId>101</EleveId>
<Valeur>4.2</Valeur>
</Note>
<Note>
<EleveId>102</EleveId>
<Valeur>3.8</Valeur>
</Note>
</Notes>
</Examen>
<Examen>
<ExamenId>2</ExamenId>
<Coef>2</Coef>
<Notes>
<Note>
<EleveId>100</EleveId>
<Valeur>5</Valeur>
</Note>
<Note>
<EleveId>101</EleveId>
<Valeur/>
</Note>
<Note>
<EleveId>102</EleveId>
<Valeur>3.5</Valeur>
</Note>
</Notes>
</Examen>
<Examen>
<ExamenId>3</ExamenId>
<Coef>3</Coef>
<Notes>
<Note>
<EleveId>100</EleveId>
<Valeur>6</Valeur>
</Note>
<Note>
<EleveId>101</EleveId>
<Valeur>5.4</Valeur>
</Note>
<Note>
<EleveId>102</EleveId>
<Valeur>2</Valeur>
</Note>
</Notes>
</Examen>
</Examens>
The following snippet is correctly displaying the values (= ./Valeur) and the weight (= ./../../Coef) :
<xforms:repeat nodeset="$currentBranche//Note[EleveId=$currentEleveId]">
<xforms:output ref="./Valeur"/>
<xforms:output ref="./../../Coef"/>
</xforms:repeat>
BTW, I also need to exclude the nodes for which Valeur is an empty string. For example, in the following simple average calculation with the XPath avg() function, I got an error (“Cannot convert ” to double”) if one node’s content is an empty string. This is a problem, because the node exist (it’s part of a model instance) and the value is an empty string when the user has not yet entered a value.
<xforms:output ref="round(avg($currentBranche//Note[EleveId=$currentEleveId]/Valeur)*100) div 100"/>
[EDITED]
The correct calculations are :
If EleveId=100 : weighted average = (1*4+2*5+3*6) / (1+2+3) = 5.333
If EleveId=101 : weighted average = (1*4.2+3*5.4) / (1+3) = 5.1
If EleveId=102 : weighted average = (1*3.8+2*3.5+3*2) / (1+2+3) = 2.8
In XPath 1.0 Use:
In Xpath 2.0 (XQuery) use:
If all
Valeurvalues are guaranteed to be castable asxs:decimal, then use:In this case there won’t be (noticeable) loss of precision and you can use later the
format-number()function to get the wanted number of digits after the decimal point.II. Producing “weighted average”:
Given the provided XML document:
this XPath 2.0 expression produces the weighted average:
and the expected, correct result is produced: