XSLT2+ 如何合并含任意匹配元素的分组并去除元素重复项
XSLT 连通分组合并的替代实现思路
你当前实现的是存在公共元素的分组合并+元素去重需求,本质是求集合的连通分量,你用的逐元素递归查找所有连通节点的思路是可行的,以下是几个更高效或适配不同场景的可替代实现思路:
思路1:迭代合并分组(递归层级更低,性能更优)
直接以初始g分组为单位进行合并,不需要逐元素递归查找,适合分组数量较少的场景:
<xsl:variable name="initial-groups" select="$in/g"/> <xsl:function name="k2:merge-groups"> <xsl:param name="remaining-groups"/> <!-- 取第一个组,匹配所有和它有公共元素的组 --> <xsl:variable name="first-group" select="$remaining-groups[1]"/> <xsl:variable name="merge-targets" select="$remaining-groups[i = $first-group/i]"/> <!-- 合并后去重的元素集合 --> <xsl:variable name="merged-items" select="distinct-values($merge-targets/i)"/> <!-- 剩余未参与合并的组 --> <xsl:variable name="rest-groups" select="$remaining-groups[not(i = $first-group/i)]"/> <xsl:sequence select=" if (empty($rest-groups)) then <g>{for $item in $merged-items return <i>{$item}</i>}</g> else (<g>{for $item in $merged-items return <i>{$item}</i>}</g>, k2:merge-groups($rest-groups)) "/> </xsl:function> <!-- 调用入口 --> <xsl:template match="/"> <out> <xsl:copy-of select="k2:merge-groups($initial-groups)"/> </out> </xsl:template>
思路2:用Key优化查找,降低重复计算开销
你的原有实现中递归函数每次都会遍历所有g节点查找匹配元素,用key预建立元素到所属分组的映射,可以大幅减少重复查找的开销,适合数据量较大的场景:
<!-- 预定义映射:按i的数值查询所属的g节点 --> <xsl:key name="item-to-group" match="g" use="i/number(.)"/> <xsl:function name="k2:find-connected" as="node()*"> <xsl:param name="current-items" as="node()*"/> <xsl:param name="processed-items" as="xs:integer*"/> <!-- 找到当前所有i对应的分组,取出未处理的关联元素 --> <xsl:variable name="related-items" select="key('item-to-group', $current-items/number(.))/i[not(number(.) = $processed-items)]/number(.)"/> <xsl:sequence select=" if (empty($related-items)) then $current-items else k2:find-connected(($current-items, $related-items), ($processed-items, $related-items)) "/> </xsl:function>
思路3:XSLT 3.0 累加器实现(适配大文件流式处理)
如果你的输入数据量达到GB级,可以用XSLT3.0的流式累加器逐节点处理,不需要一次性加载所有节点到内存,避免内存溢出:
<xsl:mode streamable="yes" use-accumulators="connection-map"/> <xsl:accumulator name="connection-map" as="map(xs:integer, xs:integer*)" initial-value="map{}"> <!-- 逐g节点处理,自动维护连通映射关系 --> <xsl:accumulator-rule match="g" select=" let $item-vals := ./i!number(.), $exist-keys := map:keys($value)[. = $item-vals], $connected-vals := distinct-values(($item-vals, for $k in $exist-keys return map:get($value, $k))) return map:put(map:remove($value, $exist-keys), head($connected-vals), $connected-vals) "/> </xsl:accumulator>
内容的提问来源于stack exchange,提问作者ktwice
相关产品推荐
相关产品推荐

