D.Q.
D.Q.

Reputation: 547

How to match two set of nodes using their attribute value without using two <xsl:for-each>

I'm using XSLT 1.0 to process a xml document which has the following sample structure:

<root>
    <descriptions>
        <description name="abc">
            <detail>XXXXXXXXXXXXX</detail>
        </description>
        <description name="def">
            <detail>XXXXXXXXXXXXX</detail>
        </description>
        <description name="ghi">
            <detail>XXXXXXXXXXXXX</detail>
        </description>
        <description name="lmn">
            <detail>XXXXXXXXXXXXX</detail>
        </description>
        // ....... several more description elements
    </descriptions>

    <list>
        <book name="abc"/>
        <book name="def"/>
        <book name="lmn"/>
    </list>
</root> 

I was hoping to match the 'book' under 'list' node with the 'description' under 'descriptions' using the 'name' attribute. So the output would be like:

abc
XXXXXXXXXXXXX

def
XXXXXXXXXXXXX

lmn
XXXXXXXXXXXXX

I tried:

<?xml version="1.0" encoding="ISO-8859-1"?>

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

<xsl:for-each select="root/list/book">
    <xsl:param name="bookName" select="@name"/>
    <xsl:for-each select="root/descriptions/description">
        <xsl:if test="$bookName = @name">
            <h3><xsl:value-of select="$bookName"/></h3>
            <p><xsl:value-of select="detail"/></p>
        </xsl:if>
    </xsl:for-each>
</xsl:for-each>
</xsl:stylesheet>

I think there must be a more efficient way to achieve this than using nested for-each, but I couldn't think of one..So could anyone give me some help? Thank you!!

Upvotes: 2

Views: 95

Answers (1)

LarsH
LarsH

Reputation: 28014

You could use a predicate, which is more concise and probably more efficient (depending on your XSLT processor):

<xsl:for-each select="root/list/book">
    <xsl:param name="bookName" select="@name"/>
    <xsl:variable name="desc"
          select="/root/descriptions/description[@name = $bookName]" />
    <h3><xsl:value-of select="$bookName"/></h3>
    <p><xsl:value-of select="$desc/detail"/></p>
</xsl:for-each>

For a probably greater efficiency improvement, use a key. E.g. put this declaration after <xsl:output>:

<xsl:key name="descriptions-by-name" match="description" use="@name" />

Then change the <xsl:variable> select attribute to use the key:

    <xsl:variable name="desc"
          select="key('descriptions-by-name', $bookName)" />

Upvotes: 2

Related Questions