Calculate the sum of array elements without the sum function

I am new to xslt. I have an xml file like

<EmpCollection> <Emp> <Name>Sai</Name> <Sal>7000</Sal> </Emp> <Emp> <Name>Nari</Name> <Sal>7400</Sal> </Emp> <Emp> <Name>Hari</Name> <Sal>9000</Sal> </Emp> <Emp> <Name>Suri</Name> <Sal>8900</Sal> </Emp> </EmpCollection> 

Now I want to add the amount of salaries without using the sum () function. I want to clearly study xslt :-)

+4
source share
2 answers

I. Probably one of the simplest solutions :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:template match="Sal"> <xsl:param name="pSum" select="0"/> <xsl:apply-templates select="following::Sal[1]"> <xsl:with-param name="pSum" select="$pSum + ."/> </xsl:apply-templates> </xsl:template> <xsl:template match="Sal[not(following::Sal)]"> <xsl:param name="pSum" select="0"/> <xsl:value-of select="$pSum + ."/> </xsl:template> <xsl:template match="/"> <xsl:apply-templates select="descendant::Sal[1]"/> </xsl:template> </xsl:stylesheet> 

When this conversion is applied to the provided XML document :

 <EmpCollection> <Emp> <Name>Sai</Name> <Sal>7000</Sal> </Emp> <Emp> <Name>Nari</Name> <Sal>7400</Sal> </Emp> <Emp> <Name>Hari</Name> <Sal>9000</Sal> </Emp> <Emp> <Name>Suri</Name> <Sal>8900</Sal> </Emp> </EmpCollection> 

the desired, correct result is output:

 32300 

II. A more general solution is to use FXSL 2.x f:foldl() :

 <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:f="http://fxsl.sf.net/" exclude-result-prefixes="f"> <xsl:import href="../f/func-foldl.xsl"/> <xsl:import href="../f/func-Operators.xsl"/> <xsl:output encoding="UTF-8" omit-xml-declaration="yes"/> <xsl:template match="/"> <xsl:value-of select="f:foldl(f:add(), 0, /*/*/Sal)"/> </xsl:template> </xsl:stylesheet> 

When this XSLT 2.0 transformation is applied to the same XML document (see above), the same correct and desired result is obtained :

 32300 

You can pass any function with two arguments as an argument to f:foldl() and create solutions to various problems .

For example, passing f:mult() instead of f:add() (and changing the argument "zero" to 1 ) results in a salary product:

 <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:f="http://fxsl.sf.net/" exclude-result-prefixes="f"> <xsl:import href="../f/func-foldl.xsl"/> <xsl:import href="../f/func-Operators.xsl"/> <xsl:output encoding="UTF-8" omit-xml-declaration="yes"/> <xsl:template match="/"> <xsl:value-of select="f:foldl(f:mult(), 1, /*/*/Sal)"/> </xsl:template> </xsl:stylesheet> 

The result of applying this transformation in the same XML document is now the product of all Sal elements :

 4.14918E15 

III. In XSLT 3.0 (XPath 3.0), you can use the standard fold-left() or fold-right() in the same way as in the previous solution .

+1
source

There are probably more elegant ways (and I think sum() :), but the following works, “add up” siblings with the addition . The termination condition is the last brother (i.e., without the following nodes).

 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" omit-xml-declaration="yes"/> <xsl:template match="/EmpCollection"> <result>Result Is : <xsl:apply-templates select="Emp[1]"/> </result> </xsl:template> <xsl:template match="Emp[following-sibling::Emp]"> <xsl:variable name="salThis"> <xsl:value-of select="Sal"/> </xsl:variable> <xsl:variable name="salOthers"> <xsl:apply-templates select="following-sibling::Emp[position()=1]"/> </xsl:variable> <xsl:value-of select="$salThis + $salOthers"/> </xsl:template> <!--Termination - the last employee just returns its salary value--> <xsl:template match="Emp[not(following-sibling::Emp)]"> <xsl:value-of select="Sal"/> </xsl:template> </xsl:stylesheet> 

It gives the result:

 <result>Result Is : 32300</result> 

Edit

After you read your question again, I think this is not a golf question with a code? The following is an easier way to do this with hard-coded element names that instruct the processor to summarize the salaries of the named employees:

 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" omit-xml-declaration="yes"/> <xsl:template match="/EmpCollection"> <result> Result Is : <xsl:value-of select="Emp[Name='Sai']/Sal + Emp[Name='Nari']/Sal + Emp[Name='Hari']/Sal + Emp[Name='Suri']/Sal"/> </result> </xsl:template> </xsl:stylesheet> 

which then should easily understand what sum() does:

 <xsl:template match="/EmpCollection"> <result> Result Is : <xsl:value-of select="sum(Emp/Sal)"/> </result> </xsl:template> 

Edit

The graph is simple:

 <xsl:template match="/EmpCollection"> <result> <xsl:text>Count Is : </xsl:text> <xsl:value-of select="count(Emp)"/> </result> </xsl:template> 

Doing this without count() not so simple. Note that xsl:variable cannot be changed after assignment (immutable). So the following does not work at all.

 <xsl:template match="/EmpCollection"> <xsl:variable name="count" select="0" /> <xsl:for-each select="Emp"> <!--NB : You can't do this. Variables are immutable - XSLT is functional --> <xsl:variable name="count" select="$count + 1"></xsl:variable> </xsl:for-each> <result> Result Is : <xsl:value-of select="$count"/> </result> </xsl:template> 

So, you can iterate over all the nodes and then use position() to determine if this is the last node in the sequence. (Note that we used for-each , but in practice it is better to use use apply-template )

 <xsl:template match="/EmpCollection"> <result> <xsl:text>Count Is : </xsl:text> <xsl:for-each select="Emp"> <xsl:if test="position() = last()"> <xsl:number value="position()" format="1" /> </xsl:if> </xsl:for-each> </result> </xsl:template> 

:(

0
source

Source: https://habr.com/ru/post/1439089/


All Articles