Как с помощью XSLT / XPath создать список через запятую?

Я знаю, что этот вопрос слишком стар, но я не видел никакой реализации, подобной моей. Эта версия основана на символе преобразования Шварца .

function sortByAttribute(array, ...attrs) {
  // generate an array of predicate-objects contains
  // property getter, and descending indicator
  let predicates = attrs.map(pred => {
    let descending = pred.charAt(0) === '-' ? -1 : 1;
    pred = pred.replace(/^-/, '');
    return {
      getter: o => o[pred],
      descend: descending
    };
  });
  // schwartzian transform idiom implementation. aka: "decorate-sort-undecorate"
  return array.map(item => {
    return {
      src: item,
      compareValues: predicates.map(predicate => predicate.getter(item))
    };
  })
  .sort((o1, o2) => {
    let i = -1, result = 0;
    while (++i < predicates.length) {
      if (o1.compareValues[i] < o2.compareValues[i]) result = -1;
      if (o1.compareValues[i] > o2.compareValues[i]) result = 1;
      if (result *= predicates[i].descend) break;
    }
    return result;
  })
  .map(item => item.src);
}

Вот пример того, как его использовать:

let games = [
  { name: 'Pako',              rating: 4.21 },
  { name: 'Hill Climb Racing', rating: 3.88 },
  { name: 'Angry Birds Space', rating: 3.88 },
  { name: 'Badland',           rating: 4.33 }
];

// sort by one attribute
console.log(sortByAttribute(games, 'name'));
// sort by mupltiple attributes
console.log(sortByAttribute(games, '-rating', 'name'));

29
задан Anders Sandvig 20 March 2009 в 15:16
поделиться

7 ответов

Посмотрите на функции position(), count() и last(); например, test="position() &lt; last()".

25
ответ дан Willie Wheeler 20 March 2009 в 15:16
поделиться

Это довольно распространенная модель:

<xsl:for-each select="*">
   <xsl:value-of select="."/>
   <xsl:if test="position() != last()">
      <xsl:text>,</xsl:text>
   </xsl:if>
</xsl:for-each>
53
ответ дан Luc Hermitte 20 March 2009 в 15:16
поделиться

Для опции XSLT 2.0 вы можете использовать атрибут separator в xsl:value-of.

Это xsl:value-of:

<xsl:value-of select="/root/item" separator=", "/>

будет выдавать этот вывод:

apple, orange, banana

Вы также можете использовать больше, чем просто запятую для разделителя. Например, это:

<xsl:text>'</xsl:text>
<xsl:value-of select="/root/item" separator="', '"/>
<xsl:text>'</xsl:text>

выдаст следующий вывод:

'apple', 'orange', 'banana'

Другой вариант XSLT 2.0 - string-join() ...

<xsl:value-of select="string-join(/*/item,', ')"/>
11
ответ дан Daniel Haley 20 March 2009 в 15:16
поделиться
<xsl:if test="following-sibling::*">,</xsl:if>

или (возможно, более эффективно, но вам придется проверить):

<xsl:for-each select="*[1]">
   <xsl:value-of select="."/>
   <xsl:for-each select="following-sibling::*">
       <xsl:value-of select="concat(',',.)"/>
   </xsl:for-each>
</xsl:for-each>
9
ответ дан Marc Gravell 20 March 2009 в 15:16
поделиться

Простой однострочный XPath 1.0 :

& nbsp; & nbsp; & nbsp; & nbsp; concat(., substring(',', 2 - (position() != last())))

Положите его в это преобразование:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

    <xsl:template match="/*">
      <xsl:for-each select="*">
        <xsl:value-of select=
         "concat(., substring(',', 2 - (position() != last())))"
         />
      </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

и примените его к документу XML :

<root>
    <item>apple</item>
    <item>orange</item>
    <item>banana</item>
</root>

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

apple,orange,banana

РЕДАКТИРОВАТЬ:

Вот комментарий Роберта Росни к этому ответу:

Это довольно непрозрачно код для человека, чтобы читать. Это требует, чтобы вы знали две неочевидные вещи о XSLT: 1) что делает функция подстроки, если ее индекс выходит за пределы диапазона, и 2) что логические значения могут быть неявно преобразованы в числовые.

и вот мой ответ :

Ребята, никогда не стесняйтесь узнавать что-то новое. На самом деле, это все о переполнении стека, не так ли? :)

7
ответ дан Dimitre Novatchev 20 March 2009 в 15:16
поделиться

Роберт дал классу not(position() = last()) ответ. Для этого требуется обработать весь текущий список узлов, чтобы получить размер контекста, а в больших входных документах это может заставить преобразование потреблять больше памяти. Поэтому я обычно инвертирую тест, чтобы быть первым

<xsl:for-each select="*">
  <xsl:if test="not(position() = 1)>, </xsl:if>
  <xsl:value-of select="."/>   
</xsl:for-each>
7
ответ дан jelovirt 20 March 2009 в 15:16
поделиться

Вот так у меня получилось. Я проверил это по твоему списку:

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

<xsl:template match="root">
    <xsl:call-template name="comma-join"><xsl:with-param name="list" select="item"/></xsl:call-template>
</xsl:template>

<xsl:template name="comma-join">
    <xsl:param name="list" />
    <xsl:for-each select="$list">
        <xsl:value-of select="." />
        <xsl:if test="position() != last()">
            <xsl:text>, </xsl:text>
        </xsl:if>
    </xsl:for-each>
</xsl:template> 
</xsl:stylesheet>
2
ответ дан Louise Rogow Howard 20 March 2009 в 15:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: