在this question我询问如何执行条件增量。提供的答案有效,但在大型数据集上无法很好地扩展。
输入:
<Users>
<User>
<id>1</id>
<username>jack</username>
</User>
<User>
<id>2</id>
<username>bob</username>
</User>
<User>
<id>3</id>
<username>bob</username>
</User>
<User>
<id>4</id>
<username>jack</username>
</User>
</Users>
所需的输出(以最佳时间复杂度):
<Users>
<User>
<id>1</id>
<username>jack01</username>
</User>
<User>
<id>2</id>
<username>bob01</username>
</User>
<User>
<id>3</id>
<username>bob02</username>
</User>
<User>
<id>4</id>
<username>jack02</username>
</User>
</Users>
为此目的,
会很好有什么想法吗?
答案 0 :(得分:2)
这有点难看,我不喜欢使用xsl:for-each
,但它应该比使用前兄弟姐妹更快,并且不需要2遍方法:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:key name="count" match="User" use="username" />
<xsl:template match="Users">
<Users>
<xsl:for-each select="User[generate-id()=generate-id(key('count',username)[1])]">
<xsl:for-each select="key('count',username)">
<User>
<xsl:copy-of select="id" />
<username>
<xsl:value-of select="username" />
<xsl:number value="position()" format="01"/>
</username>
</User>
</xsl:for-each>
</xsl:for-each>
</Users>
</xsl:template>
</xsl:stylesheet>
如果你真的需要它之后按ID排序,你可以将它包装成一个双遍模板:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:key name="count" match="User" use="username" />
<xsl:template match="Users">
<xsl:variable name="pass1">
<xsl:for-each select="User[generate-id()=generate-id(key('count',username)[1])]">
<xsl:for-each select="key('count',username)">
<User>
<xsl:copy-of select="id" />
<username>
<xsl:value-of select="username" />
<xsl:number value="position()" format="01"/>
</username>
</User>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="pass1Nodes" select="msxsl:node-set($pass1)" />
<Users>
<xsl:for-each select="$pass1Nodes/*">
<xsl:sort select="id" />
<xsl:copy-of select="." />
</xsl:for-each>
</Users>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:1)
这是一个微小的变化,但可能不会大幅提高效率
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="xml" indent="yes"/>
<xsl:key name="User" match="User" use="username" />
<xsl:template match="username/text()">
<xsl:value-of select="." />
<xsl:variable name="id" select="generate-id(..)" />
<xsl:for-each select="key('User', .)">
<xsl:if test="generate-id(username) = $id">
<xsl:number value="position()" format="01"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
这样做是定义一个按用户名分组用户的密钥。然后,对于每个用户名元素,您将查看该用户名的键中的元素,并在找到匹配项时输出该位置。
这种方法的一个小优点是您只查看具有相同名称的用户记录。如果您没有大量相同的名称,这可能会更有效。
答案 2 :(得分:1)
此转换完全生成指定的所需结果且效率高(O(N)):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kUserByName" match="User" use="username"/>
<xsl:key name="kUByGid" match="u" use="@gid"/>
<xsl:variable name="vOrderedByName">
<xsl:for-each select=
"/*/User[generate-id()=generate-id(key('kUserByName',username)[1])]">
<xsl:for-each select="key('kUserByName',username)">
<u gid="{generate-id()}" pos="{position()}"/>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="username/text()">
<xsl:value-of select="."/>
<xsl:variable name="vGid" select="generate-id(../..)"/>
<xsl:for-each select="ext:node-set($vOrderedByName)[1]">
<xsl:value-of select="format-number(key('kUByGid', $vGid)/@pos, '00')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
应用于提供的XML文档时:
<Users>
<User>
<id>1</id>
<username>jack</username>
</User>
<User>
<id>2</id>
<username>bob</username>
</User>
<User>
<id>3</id>
<username>bob</username>
</User>
<User>
<id>4</id>
<username>jack</username>
</User>
</Users>
产生了想要的正确结果:
<Users>
<User>
<id>1</id>
<username>jack01</username>
</User>
<User>
<id>2</id>
<username>bob01</username>
</User>
<User>
<id>3</id>
<username>bob02</username>
</User>
<User>
<id>4</id>
<username>jack02</username>
</User>
</Users>