xsl-list
[Top] [All Lists]

Re: [xsl] Group and sort nodes by attribut in child node

2011-10-17 03:43:41
Yes, the approach can be adapted fairly easily.  We can start with the
same first step, building a list of the records sorted by the
Start_Date field, then output that list in order, except that whenever
we hit the first record in each group, we output the header and walk
the rest of the list, outputting only records in that group, then
continue with the rest of the list where we left off.

The only part of the transform that needs to change is the
"group-records" template.

  <xsl:template name="group-records">
    <xsl:param name="id-list" select="''"/>
    <xsl:param name="do-group" select="''"/>
    <xsl:param name="done" select="' '"/>
    <xsl:if test="$id-list">
      <xsl:variable name="record" select="key('records',
substring-before($id-list, ' '))"/>
      <xsl:variable name="group"
select="string($record/column[@field='Group']/@value)"/>
      <xsl:choose>
        <xsl:when test="$group = $do-group">
          <xsl:if test="$group"><xsl:text> </xsl:text></xsl:if>
          <xsl:value-of
select="concat($record/column[@field='keyword']/@value, ' - ',
$record/column[@field='Start_Date']/@value, '&#10;')"/>
        </xsl:when>
        <xsl:when test="not($do-group) and not(contains($done,
concat(' ', $group, ' ')))">
          <xsl:value-of select="concat($group, '&#10;')"/>
          <xsl:call-template name="group-records">
            <xsl:with-param name="id-list" select="$id-list"/>
            <xsl:with-param name="do-group" select="$group"/>
          </xsl:call-template>
        </xsl:when>
      </xsl:choose>
      <xsl:call-template name="group-records">
        <xsl:with-param name="id-list" select="substring-after($id-list, ' ')"/>
        <xsl:with-param name="do-group" select="$do-group"/>
        <xsl:with-param name="done" select="concat($done, $group, ' ')"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

-Brandon :)


On Mon, Oct 17, 2011 at 3:27 AM, Jens Burkhardt 
<JensBurkhardt(_at_)web(_dot_)de> wrote:
Hello Brandon,

i was able to test your approach and you´re right, i have to group the 
records by their group (with its own group for null values) and sort the 
values within their group by date and finally
sort the groupy by their date.
The output should be like Martin´s output:

A - 23.12.2010
X_Group
  C - 23.12.2010
  B - 24.12.2010
  D - 25.12.2010
Y_Group
  D - 25.12.2010
  E - 26.12.2010

Howevery, is this possible? I will try to rewrite your approach but will be 
thankful for every hint :).

Thanks in advance.

Best,
Jens


-----Ursprüngliche Nachricht-----
Von: "Brandon Ibach" <brandon(_dot_)ibach(_at_)single-sourcing(_dot_)com>
Gesendet: 14.10.2011 21:22:49
An: xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
Betreff: Re: [xsl] Group and sort nodes by attribut in child node

Here's a version of Martin's approach that works in XSLT 1.0.
However, is this really a match for your requirements? I got the
impression from your initial post that the goal was more like "group
the records by their 'Group' value, putting each record with a null
'Group' in its own group, then sort the records by date within their
group and sort the groups by the earliest date in the group".

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; 
version="1.0">
<xsl:output method="text"/>

<xsl:key name="records" match="record" use="generate-id(.)"/>

<xsl:template match="records">
<xsl:call-template name="group-records">
<xsl:with-param name="id-list">
<xsl:for-each select="record">
<xsl:sort
select="concat(substring-after(substring-after(column[@field='Start_Date']/@value,
'.'), '.'),

substring-before(substring-after(column[@field='Start_Date']/@value,
'.'), '.'),

substring-before(column[@field='Start_Date']/@value, '.'))"/>
<xsl:value-of select="generate-id(.)"/><xsl:text> </xsl:text>
</xsl:for-each>
</xsl:with-param>
</xsl:call-template>
</xsl:template>

<xsl:template name="group-records">
<xsl:param name="id-list" select="''"/>
<xsl:param name="last" select="''"/>
<xsl:if test="$id-list">
<xsl:variable name="record" select="key('records',
substring-before($id-list, ' '))"/>
<xsl:variable name="group"
select="string($record/column[@field='Group']/@value)"/>
<xsl:if test="$group and $group != $last">
<xsl:value-of select="concat($group, ' ')"/>
</xsl:if>
<xsl:if test="$group"><xsl:text> </xsl:text></xsl:if>
<xsl:value-of
select="concat($record/column[@field='keyword']/@value, ' - ',
$record/column[@field='Start_Date']/@value, ' ')"/>
<xsl:call-template name="group-records">
<xsl:with-param name="id-list" select="substring-after($id-list, ' ')"/>
<xsl:with-param name="last" select="$group"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

-Brandon :)


On Fri, Oct 14, 2011 at 11:21 AM, Jens Burkhardt 
<JensBurkhardt(_at_)web(_dot_)de> wrote:
Wow. Thanks for the quick reply. First of all, sorry for not telling that i 
have to use XSLT 1.0. I knew i forgot something. The result your style 
sheet produces meets my requirement
perfectly but i need it, as I said for XSLT 1.0.
Nodes where the group value is empty should not be in the same group so 
what you did is perfect. Is there any way to do this for xslt 1.0?

Thank you.

Jens

Martin Honnen wrote:
Jens Burkhardt wrote:

The result should be like this:
A - 23.10.2010
X_Group
C - 23.10.2010
B - 24.12.2010
D - 25.10.2010
Y_Group
D - 25.12.2010
E - 26.12.2010

I want to group by the value of the group field in the column node and
everything should be sorted by start_date. The name of the group (e.g.
X_Group) should start a group-block, followed by the record nodes
which belong to the group value.
Another problem is that i don´t know the group value in advance,
because they can be set to whatever the user want.

Do you use XSLT 2.0 or 1.0?
And what about the record with keyword value "A" and the one with
keyword value "D" where the group value is an empty string? Shouldn't
they be in the same group?

Here is an XSLT 2.0 stylesheet that sorts first and the groups adjacent
records in the sorted sequence:

<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:output method="text"/>

<xsl:template match="records">
<xsl:variable name="sorted" as="element(record)*">
<xsl:perform-sort select="record">
<xsl:sort select="xs:date(concat(substring(column[@field =
'Start_Date']/@value, 7), '-',
substring(column[@field =
'Start_Date']/@value, 4, 2), '-',
substring(column[@field =
'Start_Date']/@value, 1, 2)))"/>
</xsl:perform-sort>
</xsl:variable>
<xsl:for-each-group select="$sorted" group-adjacent="column[@field
= 'Group']/@value">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:value-of select="current-grouping-key()"/>
<xsl:text> </xsl:text>
<xsl:value-of select="current-group()/concat(' ',
column[@field = 'keyword']/@value, ' - ', column[@field =
'Start_Date']/@value)"
separator=" "/>
<xsl:text> </xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="current-group()/concat(column[@field =
'keyword']/@value, ' - ', column[@field = 'Start_Date']/@value)"/>
<xsl:text> </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>

</xsl:stylesheet>

With that stylesheet Saxon 9.3 HE, when applying it to the input

<records>
<record>
<column field="keyword" value="A"></column>
<column field="Start_Date" value="23.12.2010"></column>
<column field="Group" value=""></column>
</record>
<record>
<column field="keyword" value="B"></column>
<column field="Start_Date" value="24.12.2010"></column>
<column field="Group" value="X_Group"></column>
</record>
<record>
<column field="keyword" value="D"></column>
<column field="Start_Date" value="25.12.2010"></column>
<column field="Group" value=""></column>
</record>
<record>
<column field="keyword" value="C"></column>
<column field="Start_Date" value="23.12.2010"></column>
<column field="Group" value="X_Group"></column>
</record>
<record>
<column field="keyword" value="D"></column>
<column field="Start_Date" value="25.12.2010"></column>
<column field="Group" value="Y_Group"></column>
</record>
<record>
<column field="keyword" value="E"></column>
<column field="Start_Date" value="26.12.2010"></column>
<column field="Group" value="Y_Group"></column>
</record>
</records>

outputs

A - 23.12.2010
X_Group
C - 23.12.2010
B - 24.12.2010
D - 25.12.2010
Y_Group
D - 25.12.2010
E - 26.12.2010


Does that solution meet your requirement?



___________________________________________________________
SMS schreiben mit WEB.DE FreeMail - einfach, schnell und
kostenguenstig. Jetzt gleich testen! http://f.web.de/?mc=021192

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: 
<mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--



--~------------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: 
<mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--



___________________________________________________________
SMS schreiben mit WEB.DE FreeMail - einfach, schnell und
kostenguenstig. Jetzt gleich testen! http://f.web.de/?mc=021192

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: 
<mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--



--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--