User501
User501

Reputation: 347

Count of the different element code should show at the end

I'm totally having 10 Detail element like below, In that I want to split and arrange the Code element value as per the Indicator Plan value.

<Details>
    <Detail>
        <Code>001</Code>
        <Type>Alt</Type>
        <Plan>N</Plan>
    </Detail>
    <Detail>
        <Code>002</Code>
        <Type>Alt</Type>
        <Plan>N</Plan>
    </Detail>
    <Detail>
        <Code>003</Code>
        <Type>Alt</Type>
        <Plan>N</Plan>
    </Detail>
    <Detail>
        <Code>004</Code>
        <Type>Alt</Type>
        <Plan>N</Plan>
    </Detail>
    <Detail>
        <Code>005</Code>
        <Type>Alt</Type>
        <Plan>N</Plan>
    </Detail>
    <Detail>
        <Code>006</Code>
        <Type>Alt</Type>
        <Plan>Y</Plan>
    </Detail>
    <Detail>
        <Code>007</Code>
        <Type>Alt</Type>
        <Plan>Y</Plan>
    </Detail>
    <Detail>
        <Code>008</Code>
        <Type>Alt</Type>
        <Plan>N</Plan>
    </Detail>
    <Detail>
        <Code>009</Code>
        <Type>Alt</Type>
        <Plan>N</Plan>
    </Detail>
    <Detail>
        <Code>010</Code>
        <Type>Alt</Type>
        <Plan>N</Plan>
    </Detail>
</Details>

Per page I need only 5 Code or rows to allow, in that I want the Plan='N' code should come first and Plan='Y' Code should come next and I have tried the below code

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

<xsl:template match="Details">
    <xsl:call-template name="Table_Left">
        <xsl:with-param name="start_row" as="xs:integer">1</xsl:with-param>
        <xsl:with-param name="end_row" as="xs:integer">10</xsl:with-param>
    </xsl:call-template>
</xsl:template>

<xsl:template name="Table_Left">
    <xsl:param name="start_row" as="xs:integer">0</xsl:param>
    <xsl:param name="end_row" as="xs:integer">0</xsl:param>
    <xsl:variable name="Details" select="Details"/>
    <xsl:variable name="total_rows">
        <xsl:value-of select="count($Details//Detail[Type = 'Alt'])"/>
    </xsl:variable>
    <xsl:variable name="sorted_plans_alt" as="element()">
        <sorted_plans>
            <xsl:for-each select="$Details//Detail[Type = 'Alt']">
                <xsl:sort select=".//Detail[Type = 'Alt']"
                        order="ascending" data-type="number"/>
                <xsl:sequence select="."/>
            </xsl:for-each>
        </sorted_plans>
    </xsl:variable>
    <xsl:variable name="plans_subset">
        <xsl:sequence
                select="
                subsequence($sorted_plans_alt//Detail[Type = 'Alt'], $start_row, $end_row)"
            />
    </xsl:variable>
    <xsl:variable name="preceding_plan" as="element()">
        <preceding_plan>
            <xsl:choose>
                <xsl:when test="$start_row = 1">
                    <empty/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:sequence
                            select="subsequence($sorted_plans_alt//Detail[Type = 'Alt'], $start_row - 1, 1)"
                        />
                </xsl:otherwise>
            </xsl:choose>
        </preceding_plan>
    </xsl:variable>
    <xsl:variable name="rows_to_output" as="xs:integer">
        <xsl:variable name="number_of_r_plans" as="xs:integer">
            <xsl:value-of
                    select="count($Details//Detail[Type = 'Alt'])"/>
        </xsl:variable>
        <xsl:choose>
            <xsl:when test="$number_of_r_plans > 0">
                <xsl:value-of select="$end_row - 5"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$end_row"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    <xsl:variable name="left_table" as="element()">
        <xsl:for-each select="($plans_subset//Detail[Type = 'Alt'][Plan='N'])[position() &lt;= $rows_to_output]">
            <xsl:value-of select="current()/Code"/>
        </xsl:for-each>
        <xsl:for-each select="($plans_subset//Detail[Type = 'Alt'][Plan='Y'])[position() &lt;= $rows_to_output]">
            <xsl:value-of select="current()/Code"/>
        </xsl:for-each>
    </xsl:variable>
    <xsl:variable name="right_table">
        <topic id="topic_1" xml:lang="en-US">
            <xsl:apply-templates select="$left_table/*"/>
        </topic>
    </xsl:variable>
    <xsl:sequence select="$right_table"/>
    <xsl:choose>
        <xsl:when test="$start_row + $rows_to_output &lt;= $total_rows">
            <xsl:call-template name="Table_Left">
                <xsl:with-param name="start_row" as="xs:integer"
                        select="$start_row + $rows_to_output"/>
                <xsl:with-param name="end_row" as="xs:integer" select="$end_row"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:when test="$start_row = 999"/>
    </xsl:choose>
</xsl:template>
 </xsl:stylesheet>

In the output below I'm getting 7 codes in the first Page (expecting only 5 codes per page even mentioned in the xsl). But I'm getting Plan='Y'(006 and 007) codes also.

1st page    2nd page
001         008 
002         009
003         010
004         
005         
006
007

Expected output would be like below, 5 codes in the first page and remaning codes in the 2nd page. I need Plan=N Code should come first and Plan=Y Code should follow

1st page    2nd page
001         008 
002         009
003         010
004         006
005         007
     

Upvotes: 0

Views: 55

Answers (2)

y.arazim
y.arazim

Reputation: 3250

The format of the expected output is not clear.

Try something along the lines of:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="/Details">
    <!-- sort plan N first -->
    <xsl:variable name="sorted">
        <xsl:perform-sort>
            <xsl:sort select="Plan='Y'"/>
            <xsl:sequence select="Detail"/>
        </xsl:perform-sort>     
    </xsl:variable>
    <!-- output in groups of 5 per page -->
    <output>
        <xsl:for-each-group select="$sorted/Detail" group-adjacent="(position() - 1) idiv 5">
            <page num="{position()}">
                <xsl:copy-of select="current-group()/Code"/>
            </page>
        </xsl:for-each-group>
    </output>   
</xsl:template>

</xsl:stylesheet>

With the provided input, the output will be:

<?xml version="1.0" encoding="UTF-8"?>
<output>
   <page num="1">
      <Code>001</Code>
      <Code>002</Code>
      <Code>003</Code>
      <Code>004</Code>
      <Code>005</Code>
   </page>
   <page num="2">
      <Code>008</Code>
      <Code>009</Code>
      <Code>010</Code>
      <Code>006</Code>
      <Code>007</Code>
   </page>
</output>

Upvotes: 1

Michael Kay
Michael Kay

Reputation: 163595

It's not clear to me exactly what output you want, but here are some observations about your code, some of them purely stylistic.

<xsl:with-param name="start_row" as="xs:integer">1</xsl:with-param>

Use

<xsl:with-param name="start_row" as="xs:integer" select="1"/>

It avoids a type conversion (from a text node to an integer). Some people advocate always using an as clause but I would leave it out here because it's obvious that select="1" gives you an xs:integer.

<xsl:variable name="total_rows">
    <xsl:value-of select="count($Details//Detail[Type = 'Alt'])"/>
</xsl:variable>

Use

<xsl:variable name="total_rows" select="count($Details/Detail[Type = 'Alt'])"/>

You really don't want to construct an XML document here, you want to compute an integer.

And there's no point searching all descendants (//) when you know that the Detail elements will be immediate children of Details.

<xsl:for-each select="$Details//Detail[Type = 'Alt']">
                <xsl:sort select=".//Detail[Type = 'Alt']"

This is wrong. The sort key is computed relative to the element you are sorting, your select expression will select nothing. I don't know how to correct it because I don't know what you actually want to sort on.

<xsl:variable name="left_table" as="element()">
    <xsl:for-each select="($plans_subset//Detail[Type = 'Alt'][Plan='N'])[position() &lt;= $rows_to_output]">
        <xsl:value-of select="current()/Code"/>
    </xsl:for-each>
    <xsl:for-each select="($plans_subset//Detail[Type = 'Alt'][Plan='Y'])[position() &lt;= $rows_to_output]">
        <xsl:value-of select="current()/Code"/>
    </xsl:for-each>
</xsl:variable>

This is wrong because your variable will consist of text nodes (produced by xsl:value-of), not a single element as declared in the as attribute. Perhaps you intended xsl:copy-of, but then it would be element()*.

And current()/Code is a longwinded way of saying Code.

I'm afraid these are points of detail, and I suspect I'm not seeing the wood for the trees. I suspect there's something much more fundamental that's wrong; I haven't understood what you want to achieve, but I don't see anything in the requirement that suggests that a recursive named template is an appropriate way of achieving it.

Upvotes: 0

Related Questions