xsl totals composed from variables

Discussion in 'XML' started by the_jos, Nov 3, 2006.

  1. the_jos

    the_jos Guest

    Dear reader,

    I am trying some things with xml/xsl and cannot find a solution for
    what I would like to do.

    I have 2 base items with name and price and two that are composed of
    base two (given name and quantity).
    The composition never changes, only the price can change.
    I can calculate the total value (price*quantity) with a for-each loop
    (using xsl:key) for each set of components, listing below.

    But now I want to calculate the total value of the components used in
    the composed items.

    I have looked into a 'loop' template, but this does not give the
    results I want.
    I tried various layouts, but only get a NaN value or the original value
    passed to the loop.

    I inserted the loop call right after I got the value p
    (price*quantity).

    Could someone point me to a working solution?

    Thanks

    Jos


    ------- loop template -----------
    <xsl:template name="loop">
    <xsl:param name="pr"/>
    <xsl:param name="t" select="0"/> <!-- not sure about this one -->
    <xsl:choose>
    <xsl:when test="$pr">
    <xsl:call-template name="loop">
    <xsl:with-param name="t" select="$pr+$t"/>
    </xsl:call-template>
    </xsl:when>
    <xsl:eek:therwise>
    <xsl:value-of select="$t" />
    </xsl:eek:therwise>
    </xsl:choose>

    </xsl:template>


    Call:
    <xsl:call-template name="loop">
    <xsl:with-param name="pr" select="$p"/>
    </xsl:call-template>



    ---- working code -----
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:eek:utput method="xml" omit-xml-declaration="yes"/>
    <xsl:key name="comp" match="item" use="name" />
    <xsl:template match="/">
    <table border="1">
    <xsl:apply-templates/>
    </table>
    </xsl:template>
    <xsl:template match="item">
    <tr><td><xsl:value-of select="name" /> </td>
    <td><xsl:value-of select="price" /></td>
    <xsl:for-each select="component">
    <td><xsl:value-of select="cname" /></td>
    <td><xsl:value-of select="quantity" /></td>
    <xsl:variable name="cn" select="cname" />
    <xsl:variable name="q" select="quantity" />

    <xsl:for-each select="key('comp', $cn)">
    <xsl:variable name="p" select="price*$q" />
    <td><xsl:value-of select="$p"/></td>
    </xsl:for-each>

    <!-- <xsl:value-of select="quantity" /> -->
    </xsl:for-each>
    </tr>
    </xsl:template>

    </xsl:stylesheet>

    ---- Sample XML ----

    <items>
    <item>
    <name>A</name>
    <price>10</price>
    </item>
    <item>
    <name>B</name>
    <price>20</price>
    </item>
    <item>
    <name>C</name>
    <price>10</price>
    <component>
    <cname>A</cname>
    <quantity>2</quantity>
    </component>
    <component>
    <cname>B</cname>
    <quantity>5</quantity>
    </component>
    </item>
    <item>
    <name>D</name>
    <price>10</price>
    <component>
    <cname>A</cname>
    <quantity>2</quantity>
    </component>
    <component>
    <cname>B</cname>
    <quantity>5</quantity>
    </component>
    </item>
    </items>
     
    the_jos, Nov 3, 2006
    #1
    1. Advertising

  2. Very unclear explanation -- try to provide a better one.

    Cheers,
    Dimitre Novatchev


    "the_jos" <> wrote in message
    news:...
    > Dear reader,
    >
    > I am trying some things with xml/xsl and cannot find a solution for
    > what I would like to do.
    >
    > I have 2 base items with name and price and two that are composed of
    > base two (given name and quantity).
    > The composition never changes, only the price can change.
    > I can calculate the total value (price*quantity) with a for-each loop
    > (using xsl:key) for each set of components, listing below.
    >
    > But now I want to calculate the total value of the components used in
    > the composed items.
    >
    > I have looked into a 'loop' template, but this does not give the
    > results I want.
    > I tried various layouts, but only get a NaN value or the original value
    > passed to the loop.
    >
    > I inserted the loop call right after I got the value p
    > (price*quantity).
    >
    > Could someone point me to a working solution?
    >
    > Thanks
    >
    > Jos
    >
    >
    > ------- loop template -----------
    > <xsl:template name="loop">
    > <xsl:param name="pr"/>
    > <xsl:param name="t" select="0"/> <!-- not sure about this one -->
    > <xsl:choose>
    > <xsl:when test="$pr">
    > <xsl:call-template name="loop">
    > <xsl:with-param name="t" select="$pr+$t"/>
    > </xsl:call-template>
    > </xsl:when>
    > <xsl:eek:therwise>
    > <xsl:value-of select="$t" />
    > </xsl:eek:therwise>
    > </xsl:choose>
    >
    > </xsl:template>
    >
    >
    > Call:
    > <xsl:call-template name="loop">
    > <xsl:with-param name="pr" select="$p"/>
    > </xsl:call-template>
    >
    >
    >
    > ---- working code -----
    > <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    > version="1.0">
    > <xsl:eek:utput method="xml" omit-xml-declaration="yes"/>
    > <xsl:key name="comp" match="item" use="name" />
    > <xsl:template match="/">
    > <table border="1">
    > <xsl:apply-templates/>
    > </table>
    > </xsl:template>
    > <xsl:template match="item">
    > <tr><td><xsl:value-of select="name" /> </td>
    > <td><xsl:value-of select="price" /></td>
    > <xsl:for-each select="component">
    > <td><xsl:value-of select="cname" /></td>
    > <td><xsl:value-of select="quantity" /></td>
    > <xsl:variable name="cn" select="cname" />
    > <xsl:variable name="q" select="quantity" />
    >
    > <xsl:for-each select="key('comp', $cn)">
    > <xsl:variable name="p" select="price*$q" />
    > <td><xsl:value-of select="$p"/></td>
    > </xsl:for-each>
    >
    > <!-- <xsl:value-of select="quantity" /> -->
    > </xsl:for-each>
    > </tr>
    > </xsl:template>
    >
    > </xsl:stylesheet>
    >
    > ---- Sample XML ----
    >
    > <items>
    > <item>
    > <name>A</name>
    > <price>10</price>
    > </item>
    > <item>
    > <name>B</name>
    > <price>20</price>
    > </item>
    > <item>
    > <name>C</name>
    > <price>10</price>
    > <component>
    > <cname>A</cname>
    > <quantity>2</quantity>
    > </component>
    > <component>
    > <cname>B</cname>
    > <quantity>5</quantity>
    > </component>
    > </item>
    > <item>
    > <name>D</name>
    > <price>10</price>
    > <component>
    > <cname>A</cname>
    > <quantity>2</quantity>
    > </component>
    > <component>
    > <cname>B</cname>
    > <quantity>5</quantity>
    > </component>
    > </item>
    > </items>
    >
     
    Dimitre Novatchev, Nov 4, 2006
    #2
    1. Advertising

  3. Dimitre Novatchev wrote:
    > Very unclear explanation -- try to provide a better one.


    For example: Show us what output you're trying to get, and explain
    specifically how that output was obtained.

    Or, if you want to figure this out yourself: Put some output (either
    document output, or xsl:message calls) into your loop template,
    displaying the values of your variables. The first thing you'll discover
    is that because you failed to explicitly pass $pr into the recursion, it
    isn't set the second time through, so your "loop" isn't doing much looping.


    --
    () ASCII Ribbon Campaign | Joe Kesselman
    /\ Stamp out HTML e-mail! | System architexture and kinetic poetry
     
    Joe Kesselman, Nov 4, 2006
    #3
  4. For example:

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:eek:utput method="xml" omit-xml-declaration="yes"/>
    <xsl:key name="comp" match="item" use="name" />
    <xsl:template match="/">
    <table border="1">
    <xsl:apply-templates/>
    </table>
    </xsl:template>
    <xsl:template match="item">
    <tr><td><xsl:value-of select="name" /> </td>
    <td><xsl:value-of select="price" /></td>
    <xsl:for-each select="component">
    <td><xsl:value-of select="cname" /></td>
    <td><xsl:value-of select="quantity" /></td>
    <xsl:variable name="cn" select="cname" />
    <xsl:variable name="q" select="quantity" />

    <xsl:for-each select="key('comp', $cn)">
    <xsl:variable name="p" select="price*$q" />
    <td><xsl:value-of select="$p"/></td>

    ---------------------------
    Test (must be within scope of the $p variable):

    <xsl:call-template name="loop">
    <xsl:with-param name="pr" select="$p"/>
    </xsl:call-template>
    ---------------------------

    </xsl:for-each>

    <!-- <xsl:value-of select="quantity" /> -->
    </xsl:for-each>
    </tr>

    </xsl:template>

    <xsl:template name="loop">
    <xsl:param name="pr"/>
    <xsl:param name="t" select="0"/>
    <xsl:message>pr=<xsl:value-of select="$pr"/>, t=<xsl:value-of
    select="$t"/></xsl:message>
    <xsl:choose>
    <xsl:when test="$pr">
    <xsl:call-template name="loop">
    <xsl:with-param name="t" select="$pr+$t"/>
    <!-- I presume you meant to add: -->
    <xsl:with-param name="pr" select="$pr -1"/>
    </xsl:call-template>
    </xsl:when>
    <xsl:eek:therwise>
    <xsl:value-of select="$t" />
    </xsl:eek:therwise>
    </xsl:choose>

    </xsl:template>
    </xsl:stylesheet>

    --
    () ASCII Ribbon Campaign | Joe Kesselman
    /\ Stamp out HTML e-mail! | System architexture and kinetic poetry
     
    Joe Kesselman, Nov 4, 2006
    #4
  5. the_jos

    the_jos Guest

    Joe Kesselman wrote:
    > Dimitre Novatchev wrote:
    >
    > For example: Show us what output you're trying to get, and explain
    > specifically how that output was obtained.
    >
    > Or, if you want to figure this out yourself: Put some output (either
    > document output, or xsl:message calls) into your loop template,
    > displaying the values of your variables. The first thing you'll discover
    > is that because you failed to explicitly pass $pr into the recursion, it
    > isn't set the second time through, so your "loop" isn't doing much looping.


    Joe,

    Thanks for the reply.
    I'll look into your example and your surgestion above tomorrow (it's
    getting late here in Europe).

    I'm trying to get a better understanding about xml/xsl and am trying to
    do that with something that interests me. So I am trying to build a xml
    document that determines the minimum net. value of some components in
    an online game.
    I can do this in a program language (like java), but am looking for a
    more code-independent solution. I then found out that xml/xsl does not
    handle variables the way a program language does. But that's not
    entirely strange, given the nature of xsl.

    Let's give a somewhat real-life example:

    Lets say I want a table set.

    I know the components are:
    4 chairs and one table.
    A chair is 4 legs and one chair_top.
    A table is 4 legs and one table_top.
    A table_top is 2 hours of work and 2 wood. Or 100 euro's
    A chair_top is 1 hour of work and 1 wood. Or 50 euro's.
    Legs are 1 hour of work and 1 wood. Or 25 euro's.

    Wood is 5 euro's, work is 20 euro's.
    Prices vary, but components remain the same.

    For my question, I just wanted to determine the value of legs (25),
    chair_top (25) and table_top (50) given the price of wood and work.

    I figured out that when I have those values, I can determine the
    minimum value of those components.
    Given that, I can determine the minimum value of chair and table and
    with those the minimum value of 'table_set'.
    There willl be various combinations for table_set (like 6 chairs/one
    table, 4 chairs /2 tables).
    So I figured to make 2 xml sets.
    One for each final table_set. This one contains the set name and the
    number of tables/chairs.
    And one for the base components (in this case wood and work) and the
    various combined components (like the legs and tops and ultimate
    tables/chairs).

    The table_set will inherent / import the data from the list of
    base/combined components, which are 'processed' so they will only give
    the minimum values.
    So when I look up 'table', it calculates building legs and table_top,
    determines if building or buying is cheaper, calculate the minimum
    value for table based on those results and returns that value.

    This would lead to a layout like:
    Table_set 1: Tables=1 Chairs=4 Price=650 (150+500)
    Table_set 2: Tables=2 Chairs=6 Price=900 (150+750)

    Now that I'm thinking about it, is it best to handle the 'combined'
    components in the xsl file (put price in xml file and the 'combining
    logic' in xsl). Or should I just leave it the way it is right now?

    I think I will be working on this idea for the next weeks, but that's
    part of learning something new.
    It won't be a big xml set for the base components.
    I think there's 20 base materials (like wood and work) and 15 combined
    (like legs/tops).
    The 'table' and 'chair' components are rare, I think there are about 4.
    So I figured handling this with xsl should not cause too much resource
    problems.
     
    the_jos, Nov 5, 2006
    #5
  6. the_jos wrote:
    > I then found out that xml/xsl does not
    > handle variables the way a program language does. But that's not
    > entirely strange, given the nature of xsl.


    Actually, XSLT handles variables very much the same way other
    nonprocedural programming langauges do -- single-assignment, lexically
    scoped.

    > For my question, I just wanted to determine the value of legs (25),
    > chair_top (25) and table_top (50) given the price of wood and work.


    This is as much a data-representation problem as a stylesheet problem.
    If your data hierarchy matches the structural hierarchy, gathering the
    structured information becomes much easier -- for example, if the data
    is something like

    <set>
    <table quantity="1">
    <top quantity="1" hours="2" wood="2" price="100"/>
    <leg quantity="4" hours="1" wood="1" price="25"/>
    </table>
    <chair quantity="4">
    <top quantity="1" hours="1" wood="1" price="50"/>
    <leg quantity="4" hours="1" wood="1" price="25"/>
    </chair>
    </set>

    then this becomes a fairly straightforward recursive
    multiply-and-accumulate problem. (Value at any node is sum of values of
    its children times their quantity, plus its own value if any.) That
    isn't necessarily the best solution; there are several other possible
    ways to structure this.

    Note that this provides all the information in a single document. If you
    really wanted to separate the values from the

    Picking the minimum of two results is simple once you've got the values,
    of course.


    This isn't a typical use of XSLT, of course. It's more typical of what
    one will do using XQuery... but XQuery and XSLT 2.0 are essentially the
    same language with different syntax.


    --
    () ASCII Ribbon Campaign | Joe Kesselman
    /\ Stamp out HTML e-mail! | System architexture and kinetic poetry
     
    Joe Kesselman, Nov 6, 2006
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. iksrazal
    Replies:
    2
    Views:
    318
    Marcin Grunwald
    Mar 8, 2005
  2. Francesc Guim Bernat

    Defining composed keys [XSD]

    Francesc Guim Bernat, Jul 29, 2003, in forum: XML
    Replies:
    0
    Views:
    486
    Francesc Guim Bernat
    Jul 29, 2003
  3. lvcargnini

    Matrix composed by two matrix

    lvcargnini, Jul 4, 2006, in forum: VHDL
    Replies:
    3
    Views:
    2,725
    Jonathan Bromley
    Jul 5, 2006
  4. =?Utf-8?B?Um9iZXJ0IENoYXBtYW4=?=

    Running totals in gridview (when you have many totals required)

    =?Utf-8?B?Um9iZXJ0IENoYXBtYW4=?=, Feb 15, 2007, in forum: ASP .Net
    Replies:
    3
    Views:
    894
    Alexey Smirnov
    Feb 16, 2007
  5. Replies:
    6
    Views:
    363
Loading...

Share This Page