Home > Software engineering >  Better way to merge node and sum attribute value based on another attribute value using single templ
Better way to merge node and sum attribute value based on another attribute value using single templ

Time:07-13

I need to get unique role with bar as child nodes where role/@totalQty is sum of all qty for that role. This has to be in 1 template using XSLT 1.0

I have written the following code. Is there a better way I can do it with lesser loops as I am looping 4 times , twice in for-loop ,once in sum & once in key ?

Input

<foo>
<bar  qty="1" unit="123">
<var  role="abc"/>
</bar>
<bar qty="1" unit="123">
<var  role="efg"/>
</bar>
<bar qty="2" unit="501">
<var  role="efg"/>
</bar>
<bar qty="1" unit="123">
<var  role="efg"/>
</bar>
<bar qty="1" unit="123">
<var  role="abc"/>
</bar>
<bar qty="1" unit="9085">
<var  role="abc"/>
</bar>
</foo>

Output

<foo>
<vars>
<var role="abc" totalQty="3">
<bar qty="1" unit="123" />
<bar qty="1" unit="123" />
<bar qty="1" unit="9085" />
</var>
<var role="efg" totalQty="4">
<bar qty="1" unit="123" />
<bar qty="2" unit="501" />
<bar qty="1" unit="123" />
</var>
</vars>
</foo>

xslt 1.0

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:key name="uniquerole" match="//bar/var" use="@role"/>
    <xsl:template match="/">
        <foo>
            <vars>
                <xsl:for-each select="//bar/var[generate-id() = generate-id(key('uniquerole', @role))]">
                    <xsl:variable name="vRole">
                        <xsl:value-of select="@role"/>
                    </xsl:variable>
                    <xsl:variable name="tot_qty">
                        <xsl:value-of select="sum(//bar[var/@role=$vRole]/@qty)"/>
                    </xsl:variable>
                    <var>
                        <xsl:attribute name="role">
                            <xsl:value-of select="$vRole"/>
                        </xsl:attribute>
                        <xsl:attribute name="totalQty">
                            <xsl:value-of select="$tot_qty"/>
                        </xsl:attribute>
                        <xsl:for-each select="//bar[var/@role=$vRole]">
                            <bar>
                                <xsl:attribute name="qty">
                                    <xsl:value-of select="@qty"/>
                                </xsl:attribute>
                                <xsl:attribute name="unit">
                                    <xsl:value-of select="@unit"/>
                                </xsl:attribute>
                            </bar>
                        </xsl:for-each>
                    </var>
                </xsl:for-each>
            </vars>
        </foo>
    </xsl:template>
</xsl:stylesheet>

CodePudding user response:

Why not simply:

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:key name="bar-by-role" match="bar" use="var/@role"/>

<xsl:template match="/foo">
    <foo>
        <vars>
            <xsl:for-each select="bar[generate-id() = generate-id(key('bar-by-role', var/@role)[1])]">
                <xsl:variable name="grp" select="key('bar-by-role', var/@role)" />
                <var role="{var/@role}" totalQty="{sum($grp/@qty)}">
                    <xsl:for-each select="$grp">
                        <bar qty="{@qty}" unit="{@unit}"/>
                    </xsl:for-each>
                </var>
            </xsl:for-each>
        </vars>
    </foo>
</xsl:template>

</xsl:stylesheet>
  • Related