Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old June 20th, 2013, 05:24 PM
Authorized User
 
Join Date: Jan 2012
Posts: 17
Thanks: 7
Thanked 0 Times in 0 Posts
Default for-each-group, group-adjacent confusion

Hi all,
I'm having some trouble with getting my groups correct with for-each-group. I'm able to group by <coll>, but I would like to avoid repeating the <county>. I've placed my style sheet below the desired output. Any suggestions would be greatly appreciated.

I've attempted a few different variations using group-adjacent but I'm missing something. Thank you for your time & trouble. I'm using XSLT 2.0.

I have the following input file:
Code:
<base>
    <item>
        <coll>AAA</coll>
        <inst>horn</inst>
        <name>Andy</name>
        <county>AlphaC</county>
    </item>
    <item>
        <coll>BBB</coll>
        <inst>chimes</inst>
        <name>Bob</name>
        <county>BravoC</county>
    </item>
    <item>
        <coll>AAA</coll>
        <inst>bells</inst>
        <name>Cynthia</name>
        <county>CharlieC</county>
    </item>
    <item>
        <coll>CCC</coll>
        <inst>flute</inst>
        <name>Daria</name>
        <county>BravoC</county>
    </item>
    <item>
        <coll/>
        <inst>horn</inst>
        <name>Edgar</name>
        <county>DeltaC</county>
    </item>
    <item>
        <coll>AAA</coll>
        <inst>drum</inst>
        <name>Fred</name>
        <county>AlphaC</county>
    </item>
</base>
I would like to output the following:
Code:
<newDoc>
   <group>
      <doc>Edgar-docTitle</doc>
      <title/>
      <subject>Edgar</subject>
      <subject>DeltaC</subject>
   </group>
   <group>
      <doc>AAA-docTitle</doc>
      <title>AAA</title>
      <subject>Andy</subject>
      <subject>Cynthia</subject>
      <subject>Fred</subject>
      <subject>AlphaC</subject>
      <subject>CharlieC</subject>
   </group>
   <group>
      <doc>BBB-docTitle</doc>
      <title>BBB</title>
      <subject>Bob</subject>
      <subject>BravoC</subject>
   </group>
   <group>
      <doc>CCC-docTitle</doc>
      <title>CCC</title>
      <subject>Daria</subject>
      <subject>BravoC</subject>
   </group>
</newDoc>
This is my style sheet:
Code:
<?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
        
        <xsl:strip-space elements="*"/>
        <xsl:output method="xml" indent="yes"/>
        
        <xsl:template match="/">
            <newDoc>
            <xsl:for-each-group select="base/item" group-by="coll">
                <xsl:sort select="current-grouping-key()"/>
                <xsl:variable name="vResultName">
                    <xsl:choose>
                        <!-- doesn't work. keep working on this. -->
                        <xsl:when test="current-grouping-key() = ''">
                            <xsl:value-of select="concat(name[1], '-docTitle')"/>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:value-of select="concat(current-grouping-key(), '-docTitle')"/>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:variable>
                <group>
                    <doc>
                        <xsl:value-of select="$vResultName"/>
                    </doc>
                    <title>
                        <xsl:value-of select="current-grouping-key()"/>
                    </title>
                    <xsl:for-each select="current-group()">
                        <subject>
                            <xsl:value-of select="name"/>        
                        </subject>
                    </xsl:for-each>
                    <!-- grab county element, unique only -->
                    <xsl:for-each-group select="current-group()" group-adjacent="county">
                        <xsl:sort select="current-grouping-key()"/>
                        <subject>
                            <xsl:value-of select="current-grouping-key()"/>
                        </subject>
                    </xsl:for-each-group>
                </group>    
            </xsl:for-each-group>
            </newDoc>    
        </xsl:template>
</xsl:stylesheet>

Last edited by CanOfBees; June 20th, 2013 at 05:25 PM.. Reason: added [CODE]
 
Old June 21st, 2013, 09:09 AM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

Here is a solution that creates the output you asked for:

Code:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output indent="yes"/>

<xsl:template match="base">
  <newDoc>
    <xsl:for-each-group select="item" group-by="coll/string(.)">
      <xsl:sort select="current-grouping-key()"/>
      <group>
        <doc>
          <xsl:sequence
            select="if (current-grouping-key() eq '')
                    then concat(name, '-docTitle')
                    else concat(current-grouping-key(), 'docTitle')"/>
        </doc>
        <title>
          <xsl:value-of select="current-grouping-key()"/>
        </title>
        <xsl:apply-templates select="current-group()/name"/>
        <xsl:for-each-group select="current-group()/county" group-by=".">
          <xsl:apply-templates select="."/>
        </xsl:for-each-group>
      </group>
    </xsl:for-each-group>
  </newDoc>
</xsl:template>

<xsl:template match="item/name | item/county">
  <subject>
    <xsl:value-of select="."/>
  </subject>
</xsl:template>

</xsl:stylesheet>
Main changes to your code:
If you want to include the value of empty elements in the collection of grouping keys you need to use 'group-by="coll/string(.)"' or at least 'group-by="normalize-string(coll)"' as otherwise the empty element does not contribute a grouping key.

And for the inner grouping I have just used group-by again, I am not sure why you tried with group-adjacent there.

Furthermore I have delegated the transformation of item/name and item/county to a template.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
The Following User Says Thank You to Martin Honnen For This Useful Post:
CanOfBees (June 21st, 2013)
 
Old June 28th, 2013, 02:50 PM
Authorized User
 
Join Date: Jan 2012
Posts: 17
Thanks: 7
Thanked 0 Times in 0 Posts
Default

Martin,
apologies for the delayed response. Thank you for your time & trouble - your solution gave me the results I needed. Also, I appreciate the refactoring.

Best,
CoB





Similar Threads
Thread Thread Starter Forum Replies Last Post
bunches of nodes, how to group them to different group using the business rules JohnKiller XSLT 9 March 7th, 2012 02:42 PM
Xsl: strip-space elements; and 'group-adjacent' ROCXY XSLT 6 July 15th, 2010 08:59 AM
Restart new group number in Group Footer sukarso Crystal Reports 2 October 13th, 2006 12:11 PM
Using - "group-adjacent" ROCXY XSLT 4 January 4th, 2006 11:09 AM
Using Two "group-adjacent" ROCXY XSLT 0 January 4th, 2006 08:29 AM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.