zneak
zneak

Reputation: 138261

Using XSL-T 1.0, how can I generate different output for duplicate nodes?

I'm using client-side XSL-T to generate an index from an XML document. The index looks like this:

<index>
    <topic name="A">
        <page no="127"/>
    </topic>
    <topic name="B">
        <page no="126"/>
    </topic>
    <topic name="C">
        <page no="125"/>
        <page no="128"/>
    </topic>
</index>

The XSL-T template looks like this:

<xsl:template match="/index">
    <table>
        <xsl:for-each select="topic/page">
            <xsl:sort select="@no"/>
            <tr>
                <td><xsl:value-of select="@no"/></td>
                <td><xsl:value-of select="../@name"/></td>
            </tr>
        </xsl:for-each>
    </table>
</xsl:template>

This sorts the <index> by page number and displays a table row for each page and topic. However, I found that sometimes my input has duplicate pages:

<index>
    <topic name="A">
        <page no="126"/>
    </topic>
    <topic name="B">
        <page no="126"/>
    </topic>
</index>

In that case, I still want to output one row for each duplicate topic and page, but I'd like to print a number next to the page number to indicate that it's a second occurence. I'd like my output to look like this:

<table>
    <tr>
        <td>126</td>
        <td>Topic A</td>
    </tr>
    <tr>
        <td>126 (1)</td>
        <td>Topic B</td>
    </tr>
</table>

Unfortunately, I can't nail down a way to tell that I'm on the second occurrence of the page number. I can't use preceding-sibling because it gives me the preceding input sibling, which is not sorted. Is there a way to do this?

Upvotes: 2

Views: 68

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167726

It sounds like a grouping problem you can solve in XSLT 1 with a key and Muenchian grouping http://www.jenitennison.com/xslt/grouping/muenchian.html:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

<xsl:output method="html" indent="yes" doctype-system="about:legacy-compat"/>

<xsl:key name="dup" match="page" use="@no"/>

<xsl:template match="/index">
    <table>
        <xsl:for-each select="topic/page[generate-id() = generate-id(key('dup', @no)[1])]">
            <xsl:sort select="@no"/>
            <xsl:for-each select="key('dup', @no)">
                <tr>
                    <td>
                        <xsl:value-of select="@no"/>
                        <xsl:if test="position() > 1">
                            <xsl:value-of select="concat(' (', position(), ') ')"/>
                        </xsl:if>
                    </td>
                    <td><xsl:value-of select="../@name"/></td>
                </tr>                
            </xsl:for-each>

        </xsl:for-each>
    </table>
</xsl:template>


</xsl:stylesheet>

http://xsltfiddle.liberty-development.net/948Fn5g

Upvotes: 2

Related Questions