Home > Enterprise >  Lookup in loop using xslt 3.0
Lookup in loop using xslt 3.0

Time:11-26

I need to do a lookup in a recursive loop. For example below is a xml:

    <?xml version="1.0" encoding="utf-8"?>
    <AggregatedData>
       <wd:Report_Data xmlns:wd="urn:com.workday/bsvc">
          <wd:Report_Entry>
             <wd:num>1</wd:num>
          </wd:Report_Entry>
          <wd:Report_Entry>
             <wd:num>2</wd:num>
          </wd:Report_Entry>
          <wd:Report_Entry>
             <wd:num>4</wd:num>
          </wd:Report_Entry>
          <wd:Report_Entry>
             <wd:num>5</wd:num>
          </wd:Report_Entry>
       </wd:Report_Data>
     
       <root>
          <row>
             <my_num>6</my_num>    --->> Can be any number
          </row>
       </root>
    </AggregatedData>

Now, I need to lookup my_num in Report_Data if available. If found then I need to subtract 1 from my_num and then lookup again until "not found"

For example, if my_num is 6, I need to subtract 1 from 6 (which is 5) and then lookup if 5 is found in the report_Data. If found, Need to subtract 1 from 5 now (which is 4) and then again lookup 4 in the report_data. Keep iterating this loop until "not found"

Expected output: Not found: 3

Below is a xslt I was able to prepare so far but facing error xsl:template must be at top level, but I am not able to reframe the xslt:

        <xsl:template match="AggregatedData">
            <FinalData>
                <xsl:iterate select="*">
                    <xsl:param name="numMap" as="map(xs:integer,element(wd:Report_Entry))" select="map{}"/>
                    
                    <xsl:choose>
                        
                        <xsl:when test="self::wd:Report_Data">
                            <xsl:next-iteration>
                                <xsl:with-param name="numMap"
                                    select="fold-left(
                                    wd:Report_Entry/copy-of(),
                                    map{},
                                    function($map,$entry) {
                                    let $key:=xs:integer($entry/wd:num) return
                                    if ($key)
                                    then map:put($map,$key,$entry)
                                    else
                                    $map})"
                                />  
                            </xsl:next-iteration>
                        </xsl:when>                  
                        
                        
                        <xsl:when test="self::root">
    
                            <xsl:for-each select="row/copy-of()">
                                <rows>
                                    <xsl:copy-of select="*"/>
                                    
                                    <splitExistFlag>

                                        <xsl:choose> 
                                            <xsl:when test="exists($numMap(current()/xs:integer(my_num - 1))/wd:num)">
                                                <xsl:value-of select="'found'"/> 

                                          ------<< Need a recursive loop here after further subtracting 1 from my_num >>------

                                            </xsl:when>
                                            <xsl:otherwise>
                                                <xsl:value-of select="'Not found'"/>                                            
                                            </xsl:otherwise>
                                        </xsl:choose>
                            </splitExistFlag>                                

                        </rows>
                    </xsl:for-each>
                </xsl:when>
            </xsl:choose>
        </xsl:iterate>
    </FinalData>
</xsl:template> 

</xsl:stylesheet>

Please help!

CodePudding user response:

You're thinking far too procedurally:

For example, if my_num is 6, I need to subtract 1 from 6 (which is 5) and then lookup if 5 is found in the report_Data. If found, Need to subtract 1 from 5 now (which is 4) and then again lookup 4 in the report_data. Keep iterating this loop until "not found"

Try to express it more declaratively, and you'll find it's easier to write the code.

As far as I can see, you want the highest integer less than my_num that is not present in report_Data. That is, you want

max((1 to my_num - 1)[not(. = //wd:num)])

or if the numbers are much larger, it might be a microsecond or two faster to do:

reverse(1 to my_num - 1)[not(. = //wd:num)][1]

CodePudding user response:

I could not follow the logic of your example. Here is something simple that you might be able to adapt:

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wd="urn:com.workday/bsvc"
exclude-result-prefixes="wd">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="entry" match="wd:Report_Entry" use="wd:num" />

<xsl:template match="/AggregatedData">
    <xsl:variable name="data" select="wd:Report_Data" />
    <results>
        <xsl:for-each select="root/row">
            <row start="{my_num}">
                <xsl:for-each select="reverse(1 to my_num)">
                    <result n="{.}">
                        <xsl:if test="not(key('entry', string(.), $data))">Not </xsl:if>
                        <xsl:text>found</xsl:text>
                    </result>
                </xsl:for-each>
            </row>
        </xsl:for-each>
    </results>
</xsl:template>

</xsl:stylesheet>

Applied to your (updated) example input, this will produce:

Result

<?xml version="1.0" encoding="UTF-8"?>
<results>
   <row start="6">
      <result n="6">Not found</result>
      <result n="5">found</result>
      <result n="4">found</result>
      <result n="3">Not found</result>
      <result n="2">found</result>
      <result n="1">found</result>
   </row>
</results>

Demo: https://xsltfiddle.liberty-development.net/3MXNWN3/1

  • Related