比较两个列表

时间:2014-05-17 05:19:15

标签: xslt-2.0

这是一个漫长而长的问题。道歉。

我的XSLT并不是太糟糕,因为你可以从我的声誉中看出来。我一整天都在努力解决编码问题,并且最终提出了一个解决方案,但我不喜欢它。

在我看来,我已经设法用函数式语言编写程序解决方案,我希望更多优雅,更清晰的解决方案更符合XSLT的精神。

我正在两个持有非常相似数据的计算机系统之间进行数据协调。

有问题的数据是公共交通路线,每条路线包括一个点列表,例如

<Routes>
    <Route Id="1">
            <Point Id="1"/>          
            <Point Id="2"/>          
            <Point Id="3"/>          
            <Point Id="4"/>          
            <Point Id="5"/>          
    </Route>
</Routes>

当然,没有一个Id是简单的,实际上是递增整数。

对于商业原因&#39;此路线可能会在其他系统中显示为

<Routes>
    <Route Id="1">
            <Point Id="1"/>          
            <Point Id="2"/>          
    </Route>
    <Route Id="1A">
            <Point Id="3"/>          
            <Point Id="4"/>          
            <Point Id="5"/>          
    </Route>
</Routes>

我们可以假设Point Id在系统之间经常匹配

现在,我有一个代码比较一个系统中的路由1和另一个系统中以1开头的路由,它产生的结果如下:

<Routes>
    <Route>
        <Point Id="1" In="Y"/>
        <Point Id="2" In="Y"/>
        <Point Id="3" In="N"/>
        <Point Id="4" In="N"/>
        <Point Id="5" In="Y"/>
        <Point Id="6" In="Y"/>
        <Point Id="7" In="N"/>
        <Point Id="8" In="N"/>
        <Point Id="9" In="N"/>                
    </Route>
</Routes>

In =&#39; Y&#39;表示此路线的点也在系统B中

这种输出对于企业来说有点难以理解。他们可以处理以下更容易的

<Routes>
    <Route>
        <Route>
            <Group startPoint="1" endPoint="2" In="Y"/>
            <Group startPoint="3" endPoint="4" In="N"/>
            <Group startPoint="5" endPoint="6 "In="Y"/>
            <Group startPoint="7" endPoint="9" In="Y"/>
        </Route>
    </Route>
</Routes>

显然,我并没有真正向他们展示这样的事情。我向他们展示了包含事物文本描述的Excel表格,但我确实希望减少列表中不会将状态更改为具有开头和结尾的部分的点,因为这在业务术语中更容易理解。

换句话说,他们希望看到这条路线与另一条路线的前半部分相同,然后跳过一堆积分然后再次匹配。

...所以

如何将Y和N元素的序列减少到我们在这里开始说Y的元素,然后我们从这里说N到这里然后N说最后几个。希望这是有道理的

我的测试数据:

<Routes>
    <Route>
        <Point Id="1" In="Y"/>
        <Point Id="2" In="Y"/>
        <Point Id="3" In="N"/>
        <Point Id="4" In="N"/>
        <Point Id="5" In="Y"/>
        <Point Id="6" In="Y"/>
        <Point Id="7" In="N"/>
        <Point Id="8" In="N"/>
        <Point Id="9" In="N"/>                
    </Route>
</Routes>

我的解决方案:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">    

<xsl:template match="/">
    <Routes>
        <xsl:apply-templates select="/Routes"/>
    </Routes>
</xsl:template>

<xsl:template match="/Routes">
    <Route>
        <xsl:apply-templates select="Route"/>
    </Route>
</xsl:template>

<xsl:template match="/Routes/Route">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates select="." mode="Pointy">
            <xsl:with-param name="posn" select="1" as="xs:integer"/>
            <xsl:with-param name="startPosn" select="1" as="xs:integer"/>
        </xsl:apply-templates>
    </xsl:copy>
</xsl:template>   

<xsl:template match="/Routes/Route" mode="Pointy">
    <xsl:param name="posn" as="xs:integer"/>
    <xsl:param name="startPosn" as="xs:integer"/>
    <xsl:variable name="groupType" select="Point[position()=$startPosn]/@In"/>

    <xsl:if test="$posn!=1 and $groupType != Point[$posn]/@In">
        <Group>
            <xsl:attribute name="startPoint" select="Point[$startPosn]/@Id"/>
            <xsl:attribute name="endPoint" select="Point[$posn - 1]/@Id"/>
        </Group>
    </xsl:if>

    <xsl:if test="$posn = count(Point)">
        <Group>
            <xsl:attribute name="startPoint" select="Point[$startPosn]/@Id"/>
            <xsl:attribute name="endPoint" select="Point[$posn]/@Id"/>
        </Group>            
    </xsl:if>

    <xsl:if test="$groupType = Point[$posn]/@In and $posn != count(Point)">
        <xsl:apply-templates select="." mode="Pointy">
            <xsl:with-param name="posn" select="$posn + 1" as="xs:integer"/>
            <xsl:with-param name="startPosn" select="$startPosn" as="xs:integer"/>
        </xsl:apply-templates>            
    </xsl:if>

    <xsl:if test="$groupType != Point[$posn]/@In and $posn != count(Point)">
        <xsl:apply-templates select="." mode="Pointy">
            <xsl:with-param name="posn" select="$posn + 1" as="xs:integer"/>
            <xsl:with-param name="startPosn" select="$posn" as="xs:integer"/>
        </xsl:apply-templates>            
    </xsl:if>

</xsl:template>   

</xsl:stylesheet>

1 个答案:

答案 0 :(得分:2)

给定格式

<Routes>
    <Route>
        <Point Id="1" In="Y"/>
        <Point Id="2" In="Y"/>
        <Point Id="3" In="N"/>
        <Point Id="4" In="N"/>
        <Point Id="5" In="Y"/>
        <Point Id="6" In="Y"/>
        <Point Id="7" In="N"/>
        <Point Id="8" In="N"/>
        <Point Id="9" In="N"/>                
    </Route>
</Routes>

您可以将group-adjacent

一起使用
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output indent="yes"/>

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

<xsl:template match="Route">
  <xsl:copy>
    <xsl:for-each-group select="Point" group-adjacent="@In">
      <Group startPoint="{@Id}" endPoint="{current-group()[last()]/@Id}" In="{@In}"/>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

获取

<Routes>
    <Route>
      <Group startPoint="1" endPoint="2" In="Y"/>
      <Group startPoint="3" endPoint="4" In="N"/>
      <Group startPoint="5" endPoint="6" In="Y"/>
      <Group startPoint="7" endPoint="9" In="N"/>
   </Route>
</Routes>