Reputation: 1675
I have inherited code, where I'm not fully understanding why it's not working, but I also want it to do more. The obvious issue is where the group-by
is never going to find a group, because the attribute it's looking for is always a unique value for each item. Beyond that, I am also looking for a natural order sort. From what I can find, I think I may need to first sort the group by letters, and then sort that result by numbers, but I'm not sure. Currently, even the basic sort that is there, isn't working. The existing XSLT code is:
<xsl:for-each-group select="datafield[@tag='856']" group-by="subfield[@code='u']">
<xsl:sort select="number(normalize-space(substring-after(subfield[@code='z'], ',')))"/>
<xsl:copy-of select="."/>
</xsl:for-each-group>
Using this data:
<collection>
<record>
<datafield ind1="4" ind2="1" tag="856">
<subfield code="u">https://www.example.com/ride02meys</subfield>
<subfield code="z">Digital item, v.2</subfield>
</datafield>
<datafield ind1="4" ind2="1" tag="856">
<subfield code="u">https://www.example.com/ride01meys</subfield>
<subfield code="z">Digital item, v.1</subfield>
</datafield>
<datafield ind1="4" ind2="1" tag="856">
<subfield code="u">https://www.example.com/ride12meys</subfield>
<subfield code="z">Digital item, v.12</subfield>
</record>
</collection>
I'm trying to get the following, where it's sorted as 1, 2, 12, and not 1, 12, 2.
<collection>
<record>
<datafield ind1="4" ind2="1" tag="856">
<subfield code="u">https://www.example.com/ride01meys</subfield>
<subfield code="z">Digital item, v.1</subfield>
</datafield>
<datafield ind1="4" ind2="1" tag="856">
<subfield code="u">https://www.example.com/ride02meys</subfield>
<subfield code="z">Digital item, v.2</subfield>
</datafield>
<datafield ind1="4" ind2="1" tag="856">
<subfield code="u">https://www.example.com/ride12meys</subfield>
<subfield code="z">Digital item, v.12</subfield>
</record>
</collection>
I'm admittedly weak with XSLT, so any guidance would be appreciated.
Thanks, in advance
Upvotes: 0
Views: 146
Reputation: 167716
You should be able to use a collation
like http://www.w3.org/2013/collation/UCA?lang=en;numeric=yes
on the xsl:sort
, as in
<xsl:template match="record">
<xsl:copy>
<xsl:for-each-group select="datafield[@tag='856']" group-by="subfield[@code='u']">
<xsl:sort select="normalize-space(substring-after(subfield[@code='z'], ','))"
collation="http://www.w3.org/2013/collation/UCA?lang=en;numeric=yes"/>
<xsl:copy-of select="."/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
https://xsltfiddle.liberty-development.net/ncdD7nt
Read more about collations at https://www.w3.org/TR/xslt-30/#uca-collations.
For older Saxon 9 versions you can use a different collation:
<xsl:template match="record">
<xsl:copy>
<xsl:for-each-group select="datafield[@tag='856']" group-by="subfield[@code='u']">
<xsl:sort select="normalize-space(substring-after(subfield[@code='z'], ','))"
collation="http://saxon.sf.net/collation?lang=en;alphanumeric=yes"/>
<xsl:copy-of select="."/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
http://xsltransform.net/nbiCsZq has an example, documentation is at http://saxonica.com/html/documentation9.6/extensibility/config-extend/collation/implementing-collation.html.
Upvotes: 0
Reputation: 29042
To sort your data first alphanumerically by the string before the dot and then numerically by the string after the dot you can use two xsl:sort
s like this:
<xsl:for-each-group select="datafield[@tag='856']" group-by="subfield[@code='u']">
<xsl:sort select="normalize-space(substring-before(subfield[@code='z'],'.'))" data-type="text" order="ascending" />
<xsl:sort select="normalize-space(substring-after(subfield[@code='z'], '.'))" data-type="number" order="ascending" />
<xsl:copy-of select="."/>
</xsl:for-each-group>
This assumes that the dot .
can be used as a delimiter. If the delimiter varies, this approach couldn't be used.
As a side note:
the group-by="subfield[@code='u']"
of the xsl:for-each-group
has the effect that only the first of each subfield
with an identical value is processed. If you need all subfield
s, either iterate over the current-group()
with xsl:for-each
or use xsl:for-each
in the first place.
Upvotes: 1