Генерация последовательности, которая будет отсортирована в алфавитном порядке

#xslt-2.0 #xpath-2.0

#xslt-2.0 #xpath-2.0

Вопрос:

Есть ли встроенный или простой способ генерировать последовательность значений, которые будут сортироваться в алфавитном порядке?

В качестве примера, следующие элементы элемента имеют дочерний элемент последовательности, предназначенный для сортировки. Данные генерируются для другой системы, которой требуется эта информация, поскольку она не работает с упорядочением документов.

 <list>
    <item>
        <sequence>1</sequence>
    </item>
    <item>
        <sequence>2</sequence>
    </item>
    <item>
        <sequence>3</sequence>
    </item>
    <item>
        <sequence>4</sequence>
    </item>
    ...
    <item>
        <sequence>14</sequence>
    </item>
    <item>
        <sequence>15</sequence>
    </item>
 

Вместо числовой сортировки система сортирует в алфавитном порядке и не может изменить это поведение. В результате элементы сортируются как 1,10,11,12,13,14,15,2,3,4,5,6,7,8,9

Кроме того, фактическое количество элементов не ограничено, поэтому должен быть общий способ генерации алфавитной последовательности, которая будет сортироваться в правильном порядке. Тем не менее, количество элементов должно быть относительно небольшим, менее 1000.

Это то, что у меня есть до сих пор:

 <xsl:variable name="idchars" as="xs:string *" 
    select="
        ('0','1','2','3','4','5','6','7','8','9',
         'A','B','C','D','E','F','G','H','I','J','K','L','M',
         'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
         'a','b','c','d','e','f','g','h','i','j','k','l','m',
         'n','o','p','q','r','s','t','u','v','w','x','y','z')
    "
/>

<xsl:variable name="idlen" as="xs:integer" select="count($idchars)" />

<xsl:function name="f:genId" as="xs:string">
    <xsl:param name="pSeq" as="xs:integer" />
    <xsl:variable name="vLen" as="xs:integer" select="$pSeq idiv $idlen" />
    
    <xsl:value-of>
        <xsl:for-each select="1 to $vLen">
            <xsl:sequence select="$idchars[$idlen]" />
        </xsl:for-each>
        <xsl:sequence select="$idchars[$pSeq mod $idlen]" />
    </xsl:value-of>
</xsl:function>
 

Комментарии:

1. Я не понял, что вы хотите сделать с образцом данных. Какой вывод вы хотите создать? Или где / как бы вы использовали «последовательность, которая будет сортироваться в алфавитном порядке»?

2. Образец — это то, что выводится в данный момент, но item элементы сортируются на основе sequence элементов в алфавитном порядке, а не в числовом порядке системой, получающей данные. Получатель данных не может изменить это поведение, поэтому вместо этого я пытаюсь сгенерировать алфавитную последовательность. функция, которая у меня есть, работает, но мне любопытно, есть ли лучший или более стандартный способ.

3. Можете ли вы показать, где вы используете опубликованный код? Я не совсем понимаю, как / где вы используете его в контексте сортировки представленных вами входных данных.

4. Этого действительно легко и просто достичь в XSLT, используя <xsl:sort> дочерний элемент <xsl:apply-templates> or <xsl:for-each> (и в XSLT 2.0 также of <xsl:for-each-group> )

Ответ №1:

Вы могли бы использовать format-number() для того, чтобы сгенерировать заполненную слева последовательность чисел с переменной длиной начальных нулей, затем преобразовать каждую из этих цифр в букву, используя codepoints-to-string() , а затем соединить их вместе с string-join() :

 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:f="local">
    <xsl:output indent="yes"/>

    <xsl:function name="f:genId" as="xs:string">
      <xsl:param name="pSeq" as="xs:integer" />
      <xsl:param name="digits" as="xs:integer"/>
        
      <xsl:variable name="picture" select="string-join( ((1 to $digits) ! '0'), '')" /> 
      <xsl:variable name="padded-string" select="format-number($pSeq, $picture)" as="xs:string"/>
      <xsl:variable name="padded-letter-seq" select="(1 to string-length($picture)) ! substring($padded-string, ., 1) ! codepoints-to-string(xs:integer(.)   65)" as="xs:string*"/>
      <xsl:sequence select="string-join($padded-letter-seq, '')"/>
    </xsl:function>

    <xsl:function name="f:genId" as="xs:string">
        <xsl:param name="pSeq" as="xs:integer"/>
        <xsl:sequence select="f:genId($pSeq, 4)"/>
    </xsl:function>

    <xsl:template match="/">
        <xsl:sequence select="f:genId(3)"/>
        
        <xsl:sequence select="f:genId(3,5)"/>
    </xsl:template>
    
</xsl:stylesheet>
 

Ответ №2:

Просто используйте элемент <xsl:sort> xslt, просто так:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:apply-templates select="*">
         <xsl:sort select="sequence"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>
 

Когда это преобразование применяется к предоставленному XML-документу:

 <list>
    <item>
        <sequence>1</sequence>
    </item>
    <item>
        <sequence>2</sequence>
    </item>
    <item>
        <sequence>3</sequence>
    </item>
    <item>
        <sequence>4</sequence>
    </item>
    ...
    <item>
        <sequence>14</sequence>
    </item>
    <item>
        <sequence>15</sequence>
    </item>
</list>
 

получен требуемый правильный результат:

 <list>
   <item>
      <sequence>1</sequence>
   </item>
   <item>
      <sequence>14</sequence>
   </item>
   <item>
      <sequence>15</sequence>
   </item>
   <item>
      <sequence>2</sequence>
   </item>
   <item>
      <sequence>3</sequence>
   </item>
   <item>
      <sequence>4</sequence>
   </item>
</list>
 

Обратите внимание:

Это:

 <xsl:sort select="sequence"/>
 

эквивалентно:

 <xsl:sort select="sequence" data-type="text"/>
 

поскольку «текст» является значением по умолчанию для data-type атрибута.

Если необходимо выполнить числовую сортировку, следует явно использовать:

 <xsl:sort select="sequence" data-type="number"/>