3

I have these id values that I want to have sorted:

<rule id="1.1">
</rule>
<rule id="1.2">
</rule>
<rule id="1.3">
</rule>
<rule id="Id. 4.3">
</rule>
<rule id="Id. 4.9">
</rule>
<rule id="Id. 4.10">
</rule>
<rule id="Id. 4.11">
</rule>
<rule id="Id. 4.12">
</rule>

Currently, I'm trying to sort like so. This works for the id values that only have numbers but not for the ones that also have letters.

<xsl:sort select="substring-before(@id, '.')" data-type="number"/>
<xsl:sort select="substring-after(@id, '.')" data-type="number"/>

The order it is currently giving me is:

Id. 4.10
Id. 4.11
Id. 4.12
Id. 4.3
Id. 4.9
1.1
1.2
1.3

How can I sort it so the order is:

Id. 4.3
Id. 4.9
Id. 4.10
Id. 4.11
Id. 4.12
1.1
1.2
1.3
zx485
  • 28,498
  • 28
  • 50
  • 59
  • Please explain the logic of the required sort (for example, why does "Id." come before an empty string). Also state if using XSLT 1.0 or 2.0. – michael.hor257k Apr 03 '17 at 20:06
  • I have no control over the naming convention of the id. The value is simply given to me as "Id. 4.3" for example so I need to figure out how to sort it. My current logic for the sort sorts the id values with only numbers (1.1, 1.2, 1.3). I don't know what I can do to sort it when the "Id. " is added in as well. I'm using XSLT 2.0 – albinosilver Apr 03 '17 at 20:12
  • This doesn't answer my question. If you want to sort by numbers only, then "1.3" should come before "Id . 4.3". We need some rules, not just an example. – michael.hor257k Apr 03 '17 at 20:21
  • The rule would be to sort all values numerically, but any values that begin with "Id. " should come first. – albinosilver Apr 03 '17 at 20:23
  • Numerically, 4.10 comes before 4.3. – michael.hor257k Apr 03 '17 at 20:41

2 Answers2

2

The order you show can be accomplished by using:

<xsl:sort select="number(starts-with(@id, 'Id. '))" data-type="number" order="descending"/>
<xsl:sort select="substring-before(replace(@id, '^Id. ', ''), '.')" data-type="number"/>
<xsl:sort select="substring-after(replace(@id, '^Id. ', ''), '.')" data-type="number"/>

This could probably be simplified by using a collation attribute (which would also handle more levels in your numbering scheme, e.g. "1.10.2") - however, this depends on which processor you use.

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
2

XSLT 3.0 defines a collation URI for this:

<xsl:sort collation="http://www.w3.org/2013/collation/UCA?numeric=yes"/>

This treats any sequence of digits as a number, so 2.20(a)-3 sorts before 2.20(a)-10 and after 2.8(b)-4.

But this (I think) would put "id. 4.10" after "1.3". To solve that you'll need to precede it with another sort key

<xsl:sort select="not(starts-with(., 'id'))"/>

(false sorts before true)

This is implemented in current Saxon releases. Earlier Saxon releases provide the collation URI

http://saxon.sf.net/collation?alphanumeric=yes

with similar semantics.

If that doesn't work for you, then if you always have the same number of numeric components you can split the value up using regular expressions and use multiple sort keys.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164