xml - Sorting data from lists and grouping in a table with XSLT -


four types of data need selected list , grouped in table. example of xml input looks follows:

<div>   <ul>     <li>fr0.1.1 : en1.1.1</li>     <li>fr0.2.1 : en1.2.1</li>     <li>fr0.4.1 : en1.3.1</li>     <li>fr0.6.1 : en1.4.1</li>     <li>fr0.5.1 : en1.5.1</li>     <li>fr.0.7.1 : en1.5.1</li>     <li>        : en1.6.1</li>   </ul </div> 

the 2nd digit in each 3-digit number having same prefix (e.g. ‘fr’) relative 2nd digit of preceding , following 3-digit number, e.g. fr0.1.1, fr0.2.1, fr0.3.1 continuous sequence in context, while fr0.1.1, fr0.3.1, fr0.2.1 or fr0.1.1, fr0.3.1 discontinuous.

i need code find gaps , discrepancies in these relations among numbers prefixed ‘fr’ on left side of each <li> element. if there number missing in such sequence, code needs retrieve , list in first column named ‘absent(fr)’. if 2nd digits not arrange in growing order go down list of 3-digit numbers on left, code should identify number 2nd digit discrepancy in sequence of growing order, e.g. it’s fr0.6.1 in input example above (0.)4(.1)<(0.)6(.1)>(0.)5(.1). if such number found, should listed in 2nd column called ‘discrepant'. third column should list number discrepant number matches on right.

the fourth column called ‘no-match(en)’ should list numbers prefixed ’en’ if not have match prefixed ‘fr’ on right. in example such number 1.6.1.

the fifth column colled ‘repetition’ should list number prefixed ‘en’ if occur more once in input list. , code should retrieve matches prefixed ‘fr’ repetitive number , list in last column ‘matches’.

the table in html output should (style such color or bold not important!):

table

the xml output this:

<table>  <tr>       <th>absent(en)</th>    <th>discrepant</th>    <th>match</th>    <th>no-match(en)</th>    <th>repetition</th>   <th>matches</th>  </tr>  <tr>    <td>0.3.1</td>    <td>0.6.1</td>    <td>1.4.1</td>    <td>1.6.1</td>    <td>1.5.1</td>    <td>0.5.1, 0.7.1</td>  </tr> </table> 

i won't answer whole question. bit stackoverflow. answer core part of question (first column). should enough extrapolate full answer. if having problems extrapolating answer, subdivide unresolved parts of problem smaller tasks , post separate question on each, of course, after have had attempt @ yourself.

this input document ... (slightly different yours correct formation errors , not dealing case of last li)

<div>   <ul>     <li>fr0.1.1 : en1.1.1</li>     <li>fr0.2.1 : en1.2.1</li>     <li>fr0.4.1 : en1.3.1</li>     <li>fr0.6.1 : en1.4.1</li>     <li>fr0.5.1 : en1.5.1</li>     <li>fr0.7.1 : en1.5.1</li>   </ul> </div> 

... when applied xslt 2.0 stylesheet ...

<xsl:stylesheet version="2.0"   xmlns:xsl="http://www.w3.org/1999/xsl/transform"   xmlns:fn="http://www.w3.org/2005/xpath-functions"   xmlns:xs="http://www.w3.org/2001/xmlschema"   xmlns:so="http://stackoverflow.com/questions/17776650"   exclude-result-prefixes="xsl xs fn so"> <xsl:output indent="yes" omit-xml-declaration="yes" /> <xsl:strip-space elements="*" />  <xsl:function name="so:middle-number" as="xs:integer">  <xsl:param name="dotted-text" as="xs:string" />  <xsl:sequence select="fn:replace( $dotted-text, 'fr0\.(\d+)\.1.*', '$1') cast xs:integer" /> </xsl:function>  <xsl:function name="so:delta" as="xs:integer">  <!-- difference between node , previous.       count first node having difference = 1 . -->  <xsl:param name="li" as="element()" />  <xsl:sequence select="    if ($li/preceding-sibling::li)        so:middle-number($li/text()) - so:middle-number($li/preceding-sibling::li[1]/text())      else        1" /> </xsl:function>  <xsl:function name="so:in-between" as="xs:integer*">  <xsl:param name="lower-bound" as="xs:integer" />  <xsl:param name="upper-bound" as="xs:integer" />  <xsl:variable name="diff" select="$upper-bound - $lower-bound" />  <xsl:choose>   <xsl:when test="$diff eq 0">      <xsl:sequence select="$lower-bound" />   </xsl:when>   <xsl:when test="$diff eq 1">      <xsl:sequence select="$lower-bound, $lower-bound+1" />   </xsl:when>   <xsl:when test="($diff ge 2)">      <xsl:variable name="half-way" select="fn:round( $lower-bound + ($diff div 2))" />     <xsl:sequence select="so:in-between($lower-bound, $half-way) , so:in-between( $half-way+1, $upper-bound)" />   </xsl:when>   <xsl:otherwise />     </xsl:choose> </xsl:function>  <xsl:template match="/*/ul">  <xsl:variable name="groups">   <xsl:for-each-group select="li" group-adjacent="so:delta(.)">    <so:group>     <so:start><xsl:value-of select="so:middle-number( current-group()[1     ]/text())" /></so:start>     <so:end>  <xsl:value-of select="so:middle-number( current-group()[last()]/text())" /> </so:end>    </so:group>   </xsl:for-each-group>  </xsl:variable>  <xsl:apply-templates select="$groups" />  </xsl:template>  <xsl:template match="so:group[1]" />  <xsl:template match="so:group">   <xsl:variable name="this" select="so:start/text() cast xs:integer" as="xs:integer" />   <xsl:variable name="prev" select="preceding-sibling::so:group[1]/so:end/text() cast xs:integer" as="xs:integer" />   <xsl:for-each select="for $x in so:in-between( $prev + 1, $this - 1) return $x">    <td><xsl:value-of select="concat('0.',.,'.1')" /></td>   </xsl:for-each>  </xsl:template>  </xsl:stylesheet> 

... yields result ...

<td>0.3.1</td> <td>0.6.1</td> 

.. 2 absent li values.

take-home point

you can use xsl:for-each-group/@group-adjacent instruction, grouping on delta between value of 1 li node , predecessor. gives groups of sequential nodes. span difference between end of 1 group , start of next, , have absent values. gives first column.

use similar techniques other columns.


update

i realised don't need so:inbetween() function. can use to operator. sorry that. changing to operator should simplify stylesheet.


update 2

here to version ... think satisfactorily small!

<xsl:stylesheet version="2.0"   xmlns:xsl="http://www.w3.org/1999/xsl/transform"   xmlns:fn="http://www.w3.org/2005/xpath-functions"   xmlns:xs="http://www.w3.org/2001/xmlschema"   xmlns:so="http://stackoverflow.com/questions/17776650"   exclude-result-prefixes="xsl xs fn so"> <xsl:output indent="yes" omit-xml-declaration="yes" /> <xsl:strip-space elements="*" />  <xsl:function name="so:middle-number" as="xs:integer">  <xsl:param name="dotted-text" as="xs:string" />  <xsl:sequence select="fn:replace( $dotted-text, 'fr0\.(\d+)\.1.*', '$1') cast xs:integer" /> </xsl:function>  <xsl:function name="so:delta" as="xs:integer">  <!-- difference between node , previous.       count first node having difference = 1 . -->  <xsl:param name="li" as="element()" />  <xsl:sequence select="    if ($li/preceding-sibling::li)        so:middle-number($li/text()) - so:middle-number($li/preceding-sibling::li[1]/text())      else        1" /> </xsl:function>  <xsl:template match="/*/ul">  <xsl:variable name="groups">   <xsl:for-each-group select="li" group-adjacent="so:delta(.)">    <so:group>     <so:start><xsl:value-of select="so:middle-number( current-group()[1     ]/text())" /></so:start>     <so:end>  <xsl:value-of select="so:middle-number( current-group()[last()]/text())" /> </so:end>    </so:group>   </xsl:for-each-group>  </xsl:variable>  <xsl:apply-templates select="$groups" />  </xsl:template>  <xsl:template match="so:group[1]" />  <xsl:template match="so:group">   <xsl:variable name="this" select="so:start/text() cast xs:integer" as="xs:integer" />   <xsl:variable name="prev" select="preceding-sibling::so:group[1]/so:end/text() cast xs:integer" as="xs:integer" />   <xsl:for-each select="for $x in $prev + 1 $this - 1 return $x">    <td><xsl:value-of select="concat('0.',.,'.1')" /></td>   </xsl:for-each>  </xsl:template>  </xsl:stylesheet> 

about xslt being hard

in reference comment xslt being head around: please remember xslt other language. cant expect pick language posting 1 or 2 questions on stackoverflow. suspect in long run, interests best served investment. pick book on xslt 2 , read it. surprisingly easy language learn if pick right book. did, , have found return on investment ample.


Comments

Popular posts from this blog

html5 - What is breaking my page when printing? -

c# - must be a non-abstract type with a public parameterless constructor in redis -

ajax - PHP/JSON Login script (Twitter style) not setting sessions -