XSL, SUM & Multiply with Condition - xslt

Im doing an assignment for University (so im new to XSL coding) in making a quasi ecommerce site, and will provide as much detail as i can so it makes sense.
Sample XML Data:
<Items>
<Item>
<ItemID>50001</ItemID>
<ItemName>Samsung Galaxy S4</ItemName>
<ItemPrice>629</ItemPrice>
<ItemQty>14</ItemQty>
<ItemDesc>4G Mobile</ItemDesc>
<QtyHold>0</QtyHold>
<QtySold>1</QtySold>
</Item>
<Item>
<ItemID>50002</ItemID>
<ItemName>Samsung Galaxy S5</ItemName>
<ItemPrice>779</ItemPrice>
<ItemQty>21</ItemQty>
<ItemDesc>4G Mobile</ItemDesc>
<QtyHold>0</QtyHold>
<QtySold>1</QtySold>
</Item>
</Items>
Website
So the process is, when a person clicks 'Add to Cart' in the top Table, the ItemQty is decreased by 1 on the ItemQty in the XML, while it increases by 1 in the QtyHold in the XML. (QtyHold represents what has been added to the shopping Cart. Thus if QtyHold is >0 then its been added to the Cart)
My problem refers to the 2nd Table (code below), where the Total figure works - only if dealing with 1 Item. Thus, if Item Number '50001' is added a 2nd time, the Total wont change.
<xsl:template match="/">
<fieldset>
<legend>Shopping Cart</legend>
<BR />
<table border="1" id="CartTable" align="center">
<tr><th>Item Number</th>
<th>Price</th>
<th>Quantity</th>
<th>Remove</th></tr>
<xsl:for-each select="/Items/Item[QtyHold > 0]">
<tr><td><xsl:value-of select="ItemID"/></td>
<td>$<xsl:value-of select="ItemPrice"/></td>
<td><xsl:value-of select="QtyHold"/></td>
<td><button onclick="addtoCart({ItemID}, 'Remove')">Remove from Cart</button></td> </tr>
</xsl:for-each>
<tr><td ALIGN="center" COLSPAN="3">Total:</td><td>$<xsl:value-of select="sum(//Item[QtyHold >0]/ItemPrice)"/></td></tr>
</table>
<BR />
<button onclick="Purchase()" class="submit_btn float_l">Confirm Purchase</button>
<button onclick="CancelOrder()" class="submit_btn float_r">Cancel Order</button>
</fieldset>
</xsl:template>
</xsl:stylesheet>
So what needs to happen is within the following code, while it checks if the QtyHold is greater than 0 (which would mean its in the shopping Cart) & to sum these values, it also needs to multiply QtyHold & ItemPrice.
<xsl:value-of select="sum(//Item[QtyHold >0]/ItemPrice)"/>
I tried many variations of Code like this below... but can't seem to make anything work.
select="sum(//Item[QtyHold >0]/ItemPrice)/(QtyHold*ItemPrice"/>

If you are using XSLT 2.0, the expression you could use would be this:
<xsl:value-of select="sum(//Item[QtyHold >0]/(ItemPrice * QtyHold))"/>
However, in XSLT 1.0 that is not allowed. Instead, you could achieve the result you need with an extension function. In particular the "node-set" function. First you would create a variable like this, in which you construct new nodes holding each item total
<xsl:variable name="itemTotals">
<xsl:for-each select="//Item[QtyHold >0]">
<total>
<xsl:value-of select="ItemPrice * QtyHold" />
</total>
</xsl:for-each>
</xsl:variable>
Ideally, you would like to do sum($itemTotals/total), but this won't work, because itemTotals is a "Result Tree Fragment" and the sum function only accepts a node-set. So you use the node-set extension function to convert it. First declare this namespace in your XSLT...
xmlns:exsl="http://exslt.org/common"
Then, your sum function would look like this:
<xsl:value-of select="sum(exsl:node-set($itemTotals)/total)"/>
Alternatively, if you couldn't even use an extension function, you could use the "following-sibling" approach, to select each Item at a time, and keep a running total. So, you would have a template like this:
<xsl:template match="Item" mode="sum">
<xsl:param name="runningTotal" select="0" />
<xsl:variable name="newTotal" select="$runningTotal + ItemPrice * QtyHold" />
<xsl:variable name="nextItem" select="following-sibling::Item[1]" />
<xsl:choose>
<xsl:when test="$nextItem">
<xsl:apply-templates select="$nextItem" mode="sum">
<xsl:with-param name="runningTotal" select="$newTotal" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$newTotal" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
And to call it, to get the sum, you just start off by selecting the first node
<xsl:apply-templates select="(//Item)[1]" mode="sum" />
Try this XSLT which demonstrates the various approaches
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl">
<xsl:output method="html" indent="yes" />
<xsl:template match="/">
<table border="1" id="CartTable" align="center">
<tr><th>Item Number</th>
<th>Price</th>
<th>Quantity</th>
</tr>
<xsl:for-each select="/Items/Item[QtyHold > 0]">
<tr>
<td><xsl:value-of select="ItemID"/></td>
<td>$<xsl:value-of select="ItemPrice"/></td>
<td><xsl:value-of select="QtyHold"/></td>
</tr>
</xsl:for-each>
<tr>
<td ALIGN="center" COLSPAN="2">Total:</td>
<xsl:variable name="itemTotals">
<xsl:for-each select="//Item[QtyHold >0]">
<total>
<xsl:value-of select="ItemPrice * QtyHold" />
</total>
</xsl:for-each>
</xsl:variable>
<td>
<!-- XSLT 2.0 only: $<xsl:value-of select="sum(//Item[QtyHold >0]/(ItemPrice * QtyHold))"/>-->
$<xsl:value-of select="sum(exsl:node-set($itemTotals)/total)"/>
$<xsl:apply-templates select="(//Item)[1]" mode="sum" />
</td>
</tr>
</table>
</xsl:template>
<xsl:template match="Item" mode="sum">
<xsl:param name="runningTotal" select="0" />
<xsl:variable name="newTotal" select="$runningTotal + ItemPrice * QtyHold" />
<xsl:variable name="nextItem" select="following-sibling::Item[1]" />
<xsl:choose>
<xsl:when test="$nextItem">
<xsl:apply-templates select="$nextItem" mode="sum">
<xsl:with-param name="runningTotal" select="$newTotal" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$newTotal" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
As a final thought, why don't you just a new Total element to each Item element in your XML. Initially, it would be set to 0, like QtyHold. Then, when you increment QtyHold by 1, by what ever process you do, you can also increment Total by the amount held in ItemPrice. That way, you can just sum this Total node to get the overall total, without the need for extension functions or recursive templates.

Related

XSL conditions: how to choose a value from list only when the condition is met?

I am trying to implement a logic which would do choosing a value if it matches the condition. The problem is, if my list contains more elements than one, it wouldn't display the value which matches the condition, but only the first one. Could you provide some advice? thanks. I need this to implement multiple filtering by conditions. Meaning that in this for-each I would display only books with year 2008, but in another table I could also use 2010 from the list of years. The below example is a part of the template for one table which uses 2008, but I will have multiple tables where I would filter books by 2010 as well.
XSL:
<xsl:for-each select="years">
<xsl:choose>
<xsl:when test="year=2008">
<td class="year"><xsl:value-of select="year"/></td>
</xsl:when>
</xsl:choose>
</xsl:for-each>
XML:
<book>
<title>Professional ASP.NET 4 in C# and VB</title>
<author>Bill Evjen, Scott Hanselman, Devin Rader</author>
<country>USA</country>
<price>580</price>
<years>
<year>2010</year>
<year>2008</year>
</years>
</book>
This one would populate 2010 into my HTML, not 2008 as I would expect. Is this doable at all?
Update:
I've tried this approach for example to filter against multiple conditions separately:
<xsl:for-each select="library/book">
<tr>
<td class="filterTd title"><xsl:value-of select="title"/></td>
<td class="filterTd author"><xsl:value-of select="author"/></td>
<td class="filterTd price"><xsl:value-of select="price"/></td>
<xsl:for-each select="years/year[.2008]">
<td class="year">
<xsl:value-of select="."/>
</td>
</xsl:for-each>
<xsl:for-each select="years/year[.2010]">
<td class="year">
<xsl:value-of select="."/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
This would not populate any td at all in my case..
If you only want to display books for a specific year, you would put a condition in the xsl:for-each that selects the book
<xsl:for-each select="library/book[years/year=$year]">
Where $year is a variable (or parameter) containing the year you want
Do note, if you know you are dealing with a specific year, then you don't actually need to do <xsl:value-of select="year" />, you can just output the year value you are working with.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="/">
<xsl:call-template name="table">
<xsl:with-param name="year" select="2008" />
</xsl:call-template>
</xsl:template>
<xsl:template name="table">
<xsl:param name="year" />
<table>
<xsl:for-each select="library/book[years/year=$year]">
<tr>
<td class="filterTd title"><xsl:value-of select="title"/></td>
<td class="filterTd author"><xsl:value-of select="author"/></td>
<td class="filterTd price"><xsl:value-of select="price"/></td>
<td class="year">
<xsl:value-of select="$year"/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Note, you could also use a key here to look up books by year
<xsl:key name="booksByYear" match="book" use="years/year" />
Then, the xsl:for-each to select books, looks like this:
<xsl:for-each select="key('booksByYear', $year)">

Adding data from one node to another in an XML doc using XSLT

I have an XML doc with two sets of data in it. I need to output a HTML table that combines the data.
The XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<fdata>
<RepairHistory>
<RepairList>
<RepairJob>
<RepairRef>56740408</RepairRef>
<RepairDescription>Baths</RepairDescription>
<RepairReportedDate>20180515</RepairReportedDate>
<RepairCompletedDate></RepairCompletedDate>
<RepairStartDate>20180515</RepairStartDate>
</RepairJob>
<RepairJob>
<RepairRef>56735043</RepairRef>
<RepairDescription>Basins</RepairDescription>
<RepairReportedDate>20180515</RepairReportedDate>
<RepairCompletedDate></RepairCompletedDate>
<RepairStartDate>20180515</RepairStartDate>
</RepairJob>
<RepairJob>
<RepairRef>56740415</RepairRef>
<RepairDescription>Showers</RepairDescription>
<RepairReportedDate>20180515</RepairReportedDate>
<RepairCompletedDate></RepairCompletedDate>
<RepairStartDate>20180515</RepairStartDate>
</RepairJob>
</RepairList>
</RepairHistory>
<map>
<entry>
<string>GUID</string>
<string>be0f53f5-7a09-47cd-9928-0c865b6450a5</string>
</entry>
<entry>
<string>JOBNUMBER</string>
<string>56740408</string>
</entry>
</map>
<map>
<entry>
<string>GUID</string>
<string>5ce2e8fe-7735-4f98-b3a9-3bd386edb338</string>
</entry>
<entry>
<string>JOBNUMBER</string>
<string>56740415</string>
</entry>
</map>
</fdata>
JOBNUMBER has to match RepairRef. Not all of the data at the bottom matches the data at the top.
I need to output the data in a table as follows:
Date Ref Details GUID
20180515 56740408 Baths be0f53f5-7a09-47cd-9928-0c865b6450a5
20180515 56735043 Basins No GUID
20180515 56740415 Showers 5ce2e8fe-7735-4f98-b3a9-3bd386edb338
I have tried to output the table using the following xsl, but it doesn't work...
<xsl:template match="/">
<h3>Repair history</h3>
<table>
<tr>
<th>Date</th>
<th>Ref</th>
<th>Details</th>
<th>GUID</th>
</tr>
<xsl:apply-templates select ="//RepairJob" />
</table>
</xsl:template>
<xsl:template match="RepairJob">
<xsl:variable name="JobNumber" select="RepairRef" />
<tr>
<td><xsl:value-of select="RepairStartDate"/></td>
<td><xsl:value-of select="RepairRef"/></td>
<td><xsl:value-of select="RepairDescription"/></td>
<td>
<xsl:choose>
<xsl:when test="count(//map//entry[2]//string[2] = $JobNumber) > 0">
<xsl:value-of select="//map//entry[2]//string[2] = $JobNumber"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>No GUID</xsl:text>
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:template>
I can see that I am not selecting the correct node to display, but the matching is not working either. Can anyone help?
Your current xsl:value-of evaluates to a boolean ("true" or "false"). The expression you actually want is this...
<xsl:value-of select="//map[entry[2]/string[2] = $JobNumber]/entry[1]/string[2]"/>
The xsl:when needs a tweak too, so the whole xsl:choose should look like this:
<xsl:choose>
<xsl:when test="//map[entry[2]/string[2] = $JobNumber]">
<xsl:value-of select="//map[entry[2]/string[2] = $JobNumber]/entry[1]/string[2]"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>No GUID</xsl:text>
</xsl:otherwise>
</xsl:choose>
However, consider using a key to look up the guids....
<xsl:key name="map" match="map" use="entry[string[1] = 'JOBNUMBER']/string[2]" />
Then your xsl:value-of becomes this
<xsl:value-of select="key('map',$JobNumber)/entry[string[1] = 'GUID']/string[2]"/>
Note, this also allows for the entry elements to be in any order within a map element (i.e. the JOBNUMBER could be the first entry, and the GUID the second)
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:key name="map" match="map" use="entry[string[1] = 'JOBNUMBER']/string[2]" />
<xsl:template match="/">
<h3>Repair history</h3>
<table>
<tr>
<th>Date</th>
<th>Ref</th>
<th>Details</th>
<th>GUID</th>
</tr>
<xsl:apply-templates select ="//RepairJob" />
</table>
</xsl:template>
<xsl:template match="RepairJob">
<xsl:variable name="JobNumber" select="RepairRef" />
<tr>
<td><xsl:value-of select="RepairStartDate"/></td>
<td><xsl:value-of select="RepairRef"/></td>
<td><xsl:value-of select="RepairDescription"/></td>
<td>
<xsl:choose>
<xsl:when test="key('map',$JobNumber)">
<xsl:value-of select="key('map',$JobNumber)/entry[string[1] = 'GUID']/string[2]"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>No GUID</xsl:text>
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>

XPath: Cant get the right value out of a selected Tag

this is my first question here on stackoverflow, so please dont kill me.
But lets get right to the problem. I have a given xhtml and need to get specific information out of it.
Here is my xsl file so far:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://www.w3.org/1999/xhtml">
<xsl:output method="xml" indent="yes" encoding="UTF-8"
omit-xml-declaration="no" />
<xsl:template match="html">
<xsl:element name="Speisekarte">
<xsl:attribute name="xsi:noNamespaceSchemaLocation"
namespace="http://www.w3.org/2001/XMLSchema-instance">Speiseplan_Mensa_Schema.xsd</xsl:attribute>
<xsl:for-each select=".//table[#class='table module-food-table']">
<xsl:element name="Speisen">
<xsl:element name="namen">
<xsl:value-of select="./thead/tr/th[1]" />
</xsl:element>
<xsl:for-each select="./tbody/tr">
<xsl:element name="zutaten">
<xsl:value-of select="./td" />
</xsl:element>
<xsl:element name="preis">
<xsl:element name="Studenten">
<xsl:value-of select="./td[2]" />
</xsl:element>
<xsl:element name="Mitarbeiter">
<xsl:value-of select="./td[3]" />
</xsl:element>
<xsl:element name="Gäste">
<xsl:value-of select="./td[4]" />
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
My problem is by the element "zutaten". I need to get "Hackbällchen in Rahmsauce" out of this xhtml file:
<tbody>
<tr>
<td style="width:70%">
Hackbällchen in <sup>(R)</sup> Rahmsauce <sup>(9,G,A,I)</sup>
<div class="price hidden-sm hidden-md hidden-lg">
<span>1,50 €</span><span>2,15 €</span><span>2,80 €</span>
</div>
</td>
<td class="hidden-xs" style="text-align:center">
1,50 €
</td>
<td class="hidden-xs" style="text-align:center">
2,15 €
</td>
<td class="hidden-xs" style="text-align:center">
2,80 €
</td>
</tr>
</tbody>
I tried:
<xsl:value-of select="./td/text()" />
which give me this: Hackbällchen in Rahmsauce 1,50 2,15 € 2,80 €
or i tried:
<xsl:value-of select="./td/text()[not(self::div)]" />
which give me an error.
And i tried many different things. Can u guys help me with this?
Hope my english was good enough to understand my problem.
Thanks in advance.
The expression you want is this...
<xsl:value-of select="td[1]/text()" />
By doing td/text() (the ./ prefix is unnecessary here), you are getting all td nodes, and all the text underneath all these nodes.
You are missing the predicate on the first column. The XPath ./td will select ALL of the td elements in that row. Using xsl:value-of will produce the computed text value of whatever you have selected, which happens to be all of the td elements (to include the text() node descendants). You can verify this by changing xsl:value-of to xsl:copy-of.
If you want just the text() from the first td column, use td[1]/text().

How to get the sum of values in attributes in xslt

I am trying to get the sum. Here is the xslt code.
<xsl:template match="Entry">
<xsl:if test="position() <= 10">
<tr>
<td>
<xsl:value-of select="substring-before(#Value,'||')"/>
</td>
<td>
<xsl:value-of select="format-number(substring(substring-after(#Value,'||||'),1,10),'#.#')"/>
</td>
</tr>
</xsl:if>
</xsl:template>
above code will fillter data as two coloums. It is ok. Now I need to get the sum of <xsl:value-of select="format-number(substring(substring-after(#Value,'||||'),1,10),'#.#')"/>
I am from procedural programming. I read many articles but I still coudnt figure out that how to get the sum of this. can anybody help me?
Here is the xml
<TopHoldings Currency="xxx">
<Entry Type="CName||C||S||Fund Weight (%)||Benchmark weight (%)" Value="Ab||U||||1.2170000000000||" Date="8/31/2011" />
here is the whole xslt
<table style="width:50%;font-size:12px;" cellspacing="0" cellpadding="0">
<tr style="width:50%; text-align:left;background-color:E6F1F9;">
<th> </th>
<th> % of funds </th>
</tr>
<xsl:apply-templates select="$items">
<xsl:sort select="format-number(substring(substring-after(#Value,'||||'),1,10),'#.#')" order="descending"/>
<xsl:sort select="substring-before(#Value,'||')"/>
</xsl:apply-templates>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="Entry">
<xsl:if test="position() <= 10">
<tr>
<td>
<xsl:value-of select="substring-before(#Value,'||')"/>
</td>
<td>
<xsl:value-of select="format-number(substring(substring-after(#Value,'||||'),1,10),'#.#')"/>
</td>
</tr>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When you need to sum the values of multiple values on XSLT 1.0 you have to rely on recursion [EDIT: in XSLT 1.0 the function sum it is also available] (in XSLT 2.0 there is an XPath function sum()).
The following template performs the sum of the given elements through the elements-to-sum parameter, extracting the value to sum from the attribute #Value as you specified.
<?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" />
<xsl:template match="TopHoldings">
<table>
<xsl:call-template name="sum">
<xsl:with-param name="elements-to-sum"
select="Entry" />
</xsl:call-template>
</table>
</xsl:template>
<xsl:template name="sum">
<xsl:param name="elements-to-sum" />
<xsl:param name="sum" select="'0'" />
<!-- Store in variables the following operations to avoid performing them more than once -->
<xsl:variable name="current-element" select="$elements-to-sum[1]" />
<xsl:variable name="current-value" select="format-number(substring(substring-after($current-element/#Value,'||||'),1,10),'#.#')" />
<!-- Output the table row -->
<tr>
<td><xsl:value-of select="substring-before($current-element/#Value, '||')" /></td>
<td><xsl:value-of select="$current-value" /></td>
</tr>
<!-- Determine whether continue -->
<xsl:choose>
<!-- Case END: we have just one element to sum so we perform the sum and we output the desired result -->
<xsl:when test="count($elements-to-sum) = 1">
<tr>
<td>Result:</td>
<td><xsl:value-of select="$current-value + $sum" /></td>
</tr>
</xsl:when>
<!-- Case RECURSION : we call this template again adding the current value to the sum and removing the first element from the parameter elements-to-sum -->
<xsl:otherwise>
<xsl:call-template name="sum">
<xsl:with-param name="elements-to-sum"
select="$elements-to-sum[position() > 1]" />
<xsl:with-param name="sum"
select="$sum + $current-value" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I assumed that you wanted to display the result of the sum as a new row in the same table, if that is not the case and you want to display the result elsewhere the solution would be slightly different. Tell me if this solution is acceptable to you or you need to display the sum outside the element.
NOTE: Instead of doing those 'complex' string operations in the attribute #Value, I would consider splitting all the information within that attribute into different attributes.
Just use the XPath function sum(), it is available in both XPath 2.0 for XSLT 2.0 and XPath 1.0 for XSLT 1.0 as described in http://www.w3.org/TR/xpath/#function-sum. If the numbers you want to get the sum of are attributes "Value" to an element "Entry" use sum(//Entry/#Value)" to grab all and sum up. Change this to get the 10 elements you want in your xml data.

Change order of list

Current List
CurrentList
Access Autocad Burger
Layout Photoshop Sandwich
VisualStudio
I have used a boxed view and edited source like below to get three columns
<xsl:when test="$Position mod 3 = 0">
But the order of items needed is like this
Access Layout VisualStudio
Autocad Photoshop
Burger Sandwich
Can someone suggest a way to achieve this?
This is part of xml
<xsl:template name="NewTRJumbo" ddwrt:ghost="">
<xsl:param name="Position" select="1"/>
<xsl:param name="Collapse" select="."/>
<xsl:choose>
<xsl:when test="$Position mod 3 = 0">
<xsl:text disable-output-escaping="yes"></tr></xsl:text>
<xsl:call-template name="NewTR">
<xsl:with-param name="Collapse" select="$Collapse"/>
<xsl:with-param name="EmptyLine" select="1"/>
</xsl:call-template>
<xsl:text disable-output-escaping="yes"><td></td></tr></xsl:text>
<xsl:call-template name="NewTR">
<xsl:with-param name="Collapse" select="$Collapse"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<td width="1.5%">
</td>
</xsl:otherwise>
</xsl:choose>
I am assuming you have an XML document of elements, for example, something like this
<items>
<item>Access</item>
<item>Autocad</item>
<item>Burger</item>
<item>Layout</item>
<item>Photoshop</item>
<item>Sandwich</item>
<item>VisualStudio</item>
</items>
And you want to group them into 3 columns, but with the items running top-to-bottom first, rather than left-to-right. You current test of $Position mod 3 = 0 is partially along the right lines, but you are actually starting a new row each here, and so end up going left-to-right.
One thing to mention is your use of this line of code
<xsl:text disable-output-escaping="yes"></tr></xsl:text>
It looks like you are simply iterating over the elements, and trying to output a closing tr tag after every third one. XSLT is a functional language, so you need to approach with a different mind set, and do things differentially than you would in a normal procedural language.
Firstly, you need to find the number of rows you will beed to output
<xsl:param name="columns" select="3"/>
<xsl:variable name="items" select="count(/*/item)"/>
<xsl:variable name="rows" select="ceiling($items div $columns)"/>
i.e The rows is the total number of items divided by the number of columns.
Then, you need to select the first item of each row, which is straight-forward because these will be the first 'n' items in your list (Note the use of the mode because the final XSLT will have two templates matching the item element)
<xsl:apply-templates select="item[position() <= $rows]" mode="row"/>
Within the template matching the starting items, you then need to output the current item, and the following sibling items which are in the row
<xsl:apply-templates select=".|following-sibling::item[position() mod $rows = 0]"/>
The only other thing to consider is adding a blank cell to the end of the some rows. This can be done by calculating the number of remaining items compared to the total items
<xsl:variable name="lastItem" select="(position() + $rows * ($columns - 1) - $items)" />
<xsl:if test="$lastItem > 0">
<td colspan="{ceiling($lastItem div $rows)}"></td>
</xsl:if>
Try the following XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="columns" select="3"/>
<xsl:variable name="items" select="count(/*/item)"/>
<xsl:variable name="rows" select="ceiling($items div $columns)"/>
<xsl:template match="/*">
<table>
<xsl:apply-templates select="item[position() <= $rows]" mode="row"/>
</table>
</xsl:template>
<xsl:template match="item" mode="row">
<tr>
<xsl:apply-templates select=".|following-sibling::item[position() mod $rows = 0]"/>
<xsl:variable name="lastItem" select="(position() + $rows * ($columns - 1) - $items)" />
<xsl:if test="$lastItem > 0">
<td colspan="{ceiling($lastItem div $rows)}"></td>
</xsl:if>
</tr>
</xsl:template>
<xsl:template match="item">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
</xsl:stylesheet>
When applied to the XML shown at the beginning of the question, the following is output
<table>
<tr>
<td>Access</td>
<td>Layout</td>
<td>VisualStudio</td>
</tr>
<tr>
<td>Autocad</td>
<td>Photoshop</td>
<td colspan="1"></td>
</tr>
<tr>
<td>Burger</td>
<td>Sandwich</td>
<td colspan="1"></td>
</tr>
</table>