Subject: Unique list with two components ...
Posted By: asearle Post Date: 9/29/2006 8:42:48 AM
Dear all,

I need to add a second component to a list of unique values that I retrieve.  Let me explain:

I have the following code which returns a list of unique periods from the base XML (using a key declaration).  This works fine and the syntax may be useful for someone ...

<!-- Key at head of XSL -->
<xsl:key name="kper" match="PERIOD" use="." />

<!-- Display (incorporated in a select box) -->
<xsl:apply-templates select="//PERIOD[generate-id()=generate-id(key('kper',.))]" mode="selectper">

<!-- template to fetch the unique values -->
<xsl:template match="//PERIOD[generate-id()=generate-id(key('kper',.))]" mode="selectper">
  <option value="{.}"><xsl:value-of select="."/></option>
</xsl:template>

However, now I would like to return a list with two items.  For example this could be a list of TEAM_CODE and TEAM_NAME where team code is unique but TEAM_NAME also needs to be displayed.

Is there a way to use the same method to retrieve this list with two components?  Or should I be trying another method?

Any tips would be a great help.

Regards,
Alan Searle.

Reply By: mhkay Reply Date: 10/1/2006 1:13:10 PM
Firstly, your existing code. It's rather wasteful to specify the predicate [generate-id()=generate-id(key('kper',.))] in both the apply-templates call and the template rule. If you're not doing an apply-templates on the elements that don't match the predicate, then the template rule doesn't need to include the predicate, because all the elements that are processed will match it.

Secondly, your question about returning a list with two items. Just use a template of the form:

<xsl:template match="TEAM_CODE">
  <code><xsl:value-of select="."/></code>
  <name><xsl:value-of select="../TEAM_NAME"/></name>
</xsl:template>

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
Reply By: asearle Reply Date: 10/4/2006 9:00:27 AM
Hallo Michael,

> Firstly, your existing code. It's rather wasteful to specify the
> predicate [generate-id()=generate-id(key('kper',.))] in both the
> apply-templates call and the template rule.

Thanks for pointing this out:  I have removed the unecessary code.

And your syntax suggestion for retrieving two items worked perfectly.  Thanks for that.

I have one extra question which maybe you know the answer to:  

I would like to generate another listing similar to the one quoted in my first message in which unique items are retrieved but these must comply with a specific criteria.  For example:

select="xmldata[generate-id()=generate-id(key('kteam',.)) and prod_cnt &gt; 0]

This should return a unique list of teams (allocated by the key kteam) where the product count (prod_cnt) is greater than zero.

The unique list is returned correctly but as soon as I try to add my extra filter the list refuses to return.

I imagine this is due to incorrect syntax?  Or am I trying to do something that is not allowed in XSL?

If you know an easy way to do this, then that would be a great help.

Many thanks,
Alan Searle

Reply By: mhkay Reply Date: 10/4/2006 9:23:19 AM
You're probably making a context error. Is prod_cnt a child of xmldata?

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
Reply By: asearle Reply Date: 10/9/2006 2:20:44 AM
Hi Michael,

>You're probably making a context error. Is prod_cnt a child of
>xmldata?

The structure looks like this ...

<dataroot>
 <xmlout>
  <PERIOD>200608</PERIOD>
  <CTRY_CODE>DE</CTRY_CODE>
  <TEAM_CODE>CAS</TEAM_CODE>
  <SUP_CODE>ABC</SUP_CODE>
  <SUP_NAME>Company ABC & Co.</SUP_NAME>
  <PROD_CNT>2</PROD_CNT>
 </xmlout>
 <xmlout>
etc ...

So both the display Team and/or Supplier (SUP_NAME) and the potential filter (PROD_CNT) are on the same level (and, yes, are children of <xmlout>).

I have been experimenting with syntax and have the following which is the successfully operating code:

<xsl:key name="ksup" match="SUP_CODE" use="." />

[... code ...]

<xsl:apply-templates select="//SUP_CODE[generate-id()=generate-id(key('ksup',.))]" mode="selectsup">
<xsl:sort select="//SUP_NAME" order="ascending" data-type="text"/>

[... code ...]

<xsl:template match="//SUP_CODE" mode="selectsup">
<option value="{.}"><xsl:value-of select="../SUP_NAME"/></option>
</xsl:template>

... but when I try to add the following filter:

<xsl:apply-templates select="//SUP_CODE[generate-id()=generate-id(key('ksup',.)) and (//PROD_CNT = 2)]" mode="selectsupmr">

... then the list returns no data.

If you see any obvious errors in the syntax and/or context then that would be a great help.

I also find that my sort-clause:

<xsl:sort select="//SUP_NAME" order="ascending" data-type="text"/>

... does not work.  Here I am applying the unique key to SUP_CODE (supplier code) but want the display to sort by supplier name (SUP_NAME).  But it won't do that.

Anyway, if you see any obvious errors or have any tips, then that would be a great help.

Many thanks,
Alan Searle.

Reply By: mhkay Reply Date: 10/9/2006 4:50:19 AM
This is clearly wrong:

<xsl:apply-templates select="//SUP_CODE[generate-id()=generate-id(key('ksup',.))]" mode="selectsup">
<xsl:sort select="//SUP_NAME" order="ascending" data-type="text"/>

//SUP_NAME selects from the root of the tree. You want to evaluate the sort key for the element being sorted, that is SUP_CODE, so you want select="../SUP_NAME".

This is also wrong:

<xsl:apply-templates select="//SUP_CODE[generate-id()=generate-id(key('ksup',.)) and (//PROD_CNT = 2)]" mode="selectsupmr">

again, you're throwing in a "//" for luck, but it's quite wrong. You want to select the PROD_CNT from the SUP_CODE, that is ../PROD_CNT

Incidentally, element names starting with "xml" are reserved for future use in standards, so it's safest not to use them.

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
Reply By: asearle Reply Date: 10/9/2006 6:27:05 AM
Hi Michael,

Sorry about that:  Those really were silly syntax errors.

I have implemented your suggestions and they work perfectly (both for sorting and for filtering).  Many thanks:  This will help me add a number of very useful features (especially pre-filtering) to my forms.

But one strange thing is that, although the filters work fine (i.e. first without filter and then with  a filter ... or vice-versa), if I try to filter a second time, then the whole thing seems to hang.  At the moment I am using this syntax ...

<select size="5" name="selsup" id="selsup">
  <xsl:choose>
    <xsl:when test="$pfilttype='prod'">  
     <xsl:apply-templates select="//SUP_CODE[generate-id()=generate-id(key('ksupmr',.)) and (../PROD_CNT &gt; 0)]" mode="selectsupmr">
      <xsl:sort select="../SUP_NAME" order="ascending"/>
     </xsl:apply-templates>
    </xsl:when>
    <xsl:when test="$pfilttype='all'">
      <xsl:apply-templates select="//SUP_CODE[generate-id()=generate-id(key('ksup',.))]" mode="selectsup">
        <xsl:sort select="../SUP_NAME" order="ascending" data-type="text"/>
      </xsl:apply-templates>
     </xsl:when>
    </xsl:choose>
   </select>

[... code ...]

<xsl:template match="//SUP_CODE" mode="selectsup">
  <option value="{.}"><xsl:value-of select="../SUP_NAME"/></option>
</xsl:template>

<xsl:template match="//SUP_CODE" mode="selectsupmr">
  <option value="{.}"><xsl:value-of select="../SUP_NAME"/></option>
</xsl:template>

I have tried including the filter (../PROD_CNT &gt; 0) in both the 'apply-templates' and in the 'template match' clause and in both locations it is the same:  It works fine on the default value (i.e. on opening) and for ONE select only.  Then it hangs.

To fix the problem I tried allocating separate keys 'ksup' and 'ksupmr' (to separate the two selections) but this also didn't make a difference.

This is a shame because this functionality is exactly what I need.  Anyway, I hope it is just another systax issue?  Many thanks for helping with this.

Regards,
Alan Searle

Reply By: asearle Reply Date: 10/9/2006 9:33:40 AM

Strange:  It seems to be working now.  Maybe it was our server playing up.

But anyway, I am very pleased!  Thanks very much for the help.

Rgds,
Alan.


Go to topic 50771

Return to index page 153
Return to index page 152
Return to index page 151
Return to index page 150
Return to index page 149
Return to index page 148
Return to index page 147
Return to index page 146
Return to index page 145
Return to index page 144