3

I am trying to print an average with xslt and having a hard time trying to figure out a way. I have answers in a separate node and questions in a separate node. Need to match the question id with answer and calculate average

<?xml version="1.0" encoding="ISO-8859-1"?>
 <?xml-stylesheet type="text/xsl" href="test.xsl"?>
   <details>
     <detail>
      <answersall>
        <answers>
          <question id="1"/>
          <answer>4</answer>
          <note>test</note>
      </answers>
      <answers>
        <question id="2"/>
        <answer>2</answer>
      </answers>
      <answers>
        <question id="3"/>
        <answer>2</answer>
      </answers>
      <answers>
        <question id="4"/>
        <answer>3</answer>
      </answers>
    </answerall>
  </detail>
  <questions>
   <question id="1" text="Hello how are you" section="a"/>
   <question id="2" text="how was your day" section="a"/>
   <question id="3" text="it was good" section="b"/>
   <question id="4" text="take care" section="b"/>
  </questions>
</details>

I am trying to print something like this
Question Answer
Section A 3 (average)
1. Hello how are you 4
2.How was your day 2
Section B 2.5
3. It was good 2
4. Take Care 3

I have got the part to print the questions and answers but not the average. I know i cannot use a variable inside a for loop.

Below is my xslt.

<?xml version="1.0" ?>
 <xsl:stylesheet version="1.0"    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
   <html>
     <body>
      <h2><center>Details</center></h2>
       <table>
         <tr>
           <td>Section A</td>
           <td>????</td>
         </tr>
         <xsl:for-each select="details/questions/question[@section='a']">
           <xsl:variable name="id" select="@id"/>
             <tr>
                <td><xsl:value-of select="@text"/></td>
                <td><xsl:value-of select="//answers[question/@id=$id]/answer"/></td>
             </tr>
         </xsl:for-each>
     </table>
  </body>
</html>

user4925190
  • 205
  • 3
  • 11
  • Basically, this is a *grouping* question. See http://www.jenitennison.com/xslt/grouping/muenchian.html and numerous examples here on SO. -- Note also: your XML is not well-formed, and the XSLT you show us uses an undefined variable. – michael.hor257k May 22 '15 at 18:38
  • I look at this example. http://stackoverflow.com/questions/16222647/get-the-average-of-elements-values-in-xsl. But i am not able to figure out how to point to picks question based on the section and go find the average in a seperate node – user4925190 May 22 '15 at 18:39
  • Will there always be exactly two sections, `a` and `b`? – michael.hor257k May 22 '15 at 18:49
  • There are actually 5. For simplicity i added 2. – user4925190 May 22 '15 at 18:50
  • Yes, but are they *constant*? IOW, would be alright to hard-code them into the stylesheet? – michael.hor257k May 22 '15 at 18:54
  • They are constants. For now they will remain hardcoded and i will later add them to xml and derive from there. – user4925190 May 22 '15 at 18:58

1 Answers1

3

Here's something you could use as your starting point:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="question-by-section" match="question" use="@section" />
<xsl:key name="answer-by-question" match="answers" use="question/@id" />

<xsl:template match="/">
    <table border="1">
        <tr>
            <th>Question</th>
            <th>Answer</th>
        </tr>
        <xsl:call-template name="generate-section">     
            <xsl:with-param name="section">a</xsl:with-param>
        </xsl:call-template>
        <xsl:call-template name="generate-section">     
            <xsl:with-param name="section">b</xsl:with-param>
        </xsl:call-template>
     </table>
</xsl:template>

<xsl:template name="generate-section">
    <xsl:param name="section"/>
    <xsl:variable name="questions" select="key('question-by-section', $section)" />
    <xsl:variable name="answers" select="key('answer-by-question', $questions/@id)" />
    <tr>
        <th>
            <xsl:value-of select="concat('Section ', $section)" />
        </th>
        <th>
            <xsl:value-of select="sum($answers/answer) div count($answers)" />          
        </th>
    </tr>
    <xsl:for-each select="$questions">
        <tr>
            <td><xsl:value-of select="@text"/></td>
            <td><xsl:value-of select="key('answer-by-question', @id)/answer"/></td>
        </tr>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

Applied to your input (after correcting the answersall vs answerall mismatch!), the result will look like this:

enter image description here

I would urge you strongly to adopt Muenchian grouping to select distinct sections, instead of the hard-coded repeated call to a named template shown here.

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51