xslt 2.0 - Comparing to two lists -
this long, long question. apologies.
my xslt not bad can see reputation. i've been struggling day solve coding problem , have, in end, come solution don't it.
it seems me have managed code procedural solution in functional language , welcome more elegant, cleaner solutions more in spirit of xslt.
i doing data reconciliation exercise between 2 computer systems holding similar data.
the data in question public transport routes, each route consisting of list of points e.g.
<routes> <route id="1"> <point id="1"/> <point id="2"/> <point id="3"/> <point id="4"/> <point id="5"/> </route> </routes>
none of id's simple, incrementing integers in reality of course.
for 'business reasons' route may appear in other system as
<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>
we can assume point id's match between systems enough
now, have code compares route 1 in 1 system , routes start 1 in other system , produces like:
<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>
where in='y' means point in system b route
this sort of output little difficult business understand. deal following easier
<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>
obviously, don't show them thing this. shows them excel sheets text description of things, want reduce points of list not change status sections start , ends more easy understand in business terms.
in other words want see route same first half of other route skips bunch of points matches again.
so....
how reduce sequences of y , n elements element started saying y here till here said n here here , n last few. hope makes sense
my test data:
<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>
my solution:
<?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 , $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 , $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 , $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>
given format
<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>
you can use group-adjacent
with
<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>
to get
<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>
Comments
Post a Comment