Я знаю, что этот вопрос слишком стар, но я не видел никакой реализации, подобной моей. Эта версия основана на символе преобразования Шварца .
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'));
Посмотрите на функции position()
, count()
и last()
; например, test="position() < last()"
.
Это довольно распространенная модель:
<xsl:for-each select="*">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
Для опции 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,', ')"/>
<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>
Простой однострочный 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) что логические значения могут быть неявно преобразованы в числовые.
и вот мой ответ :
Ребята, никогда не стесняйтесь узнавать что-то новое. На самом деле, это все о переполнении стека, не так ли? :)
Роберт дал классу not(position() = last())
ответ. Для этого требуется обработать весь текущий список узлов, чтобы получить размер контекста, а в больших входных документах это может заставить преобразование потреблять больше памяти. Поэтому я обычно инвертирую тест, чтобы быть первым
<xsl:for-each select="*">
<xsl:if test="not(position() = 1)>, </xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
Вот так у меня получилось. Я проверил это по твоему списку:
<?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>