Search code examples
xsltcountingdistinct-valuesxslt-grouping

Counting distinct items in XSLT independent of depth


If I run the following XSLT code:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:key name="kValueByVal" match="variable_name/@value"
  use="."/>

 <xsl:template match="assessment">
  <xsl:for-each select="
   /*/*/variable/attributes/variable_name/@value
             [generate-id()
             =
              generate-id(key('kValueByVal', .)[1])
             ]
   ">
     <xsl:value-of select=
     "concat(., ' ', count(key('kValueByVal', .)), '&#xA;')"/>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

on the following XML:

<assessment>
    <variables>
        <variable>
            <attributes>
                <variable_name value="FRED"/>
            </attributes>
        </variable>
    </variables>
    <variables>
        <variable>
            <attributes>
                <variable_name value="MORTIMER"/>
            </attributes>
        </variable>
    </variables>
    <variables>
        <variable>
            <attributes>
                <variable_name value="FRED"/>
            </attributes>
        </variable>
    </variables>
</assessment>

I get the desired output:

FRED 2
MORTIMER 1

(See my original question for more info, if you wish.)

However, if I run it on this input:

<ExamStore>
    <assessment>
        <variables>
            <variable>
                <attributes>
                    <variable_name value="FRED"/>
                </attributes>
            </variable>
        </variables>
        <variables>
            <variable>
                <attributes>
                    <variable_name value="MORTIMER"/>
                </attributes>
            </variable>
        </variables>
        <variables>
            <variable>
                <attributes>
                    <variable_name value="FRED"/>
                </attributes>
            </variable>
        </variables>
    </assessment>
</ExamStore>

I get nothing. (Note that I just wrapped the original input in an ExamStore tag.) I was expecting and hoping to get the same output.

Why don't I? How can I change the original XSLT code to get the same output?


Solution

  • Well your select xpath /*/*/variable/attributes/variable_name/... is no longer correct because you added another node higher in the node-tree.

    If you want to have true independence you need to use something like:

    //variable/attributes/variable_name/...
    

    ...(not the double slash at the start) but this is fairly dangerous because it will catch all occurences of that structure - be really sure that's what you mean.

    Otherwise, just prepend your xpath with another /*