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>