我试图在字典中的for循环期间计算字符串的出现次数(baseX map)。似乎每次迭代后都会清除字典的内容。有没有办法在整个循环中保留信息?
declare variable $dict as map(*) := map:merge(());
for $x at $cnt in //a order by -$cnt
let $l := (if (map:contains($dict, $x/@line)) then (fn:number(map:get($dict, $x/@line))) else (0))
let $dict := map:put($dict, $x/@line, 1 + $l)
return (
$dict,
if ($x[@speaker="player.computer" or @speaker = "event.object"])
then ( <add sel="(//{fn:name($x)}[@line='{$x/@line}'])[{fn:string(map:get($dict, $x/@line))}]" type="@hidechoices">false</add> )
else ( <remove sel="(//{fn:name($x)}[@line='{$x/@line}'])[1]" />)
)
对于这个xml:
<a line="x" />
<a line="y" />
<a line="y" />
<a line="z" />
我应该为第一个得到这样的东西:
{
"x": 1
}
这是最后一次迭代:
{
"x": 1,
"y": 2,
"z": 1
}
我必须在最后构造一些文本,这就是输出的最后一部分。
现在我只在每次迭代时得到当前的键/值对,所以$ dict在整个执行过程中只有一个条目,$ l总是为0。
值得庆幸的是:
for $x at $cnt in //a
let $dict := map:merge((
for $y at $pos in //a
let $line := $y/@line
where $pos <= $cnt
group by $line
return map:entry($line, count($y))
))
return (
$dict,
if ($x[@speaker="player.computer" or @speaker = "event.object"])
then ( <add sel="(//{fn:name($x)}[@line='{$x/@line}'])[{fn:string(map:get($dict, $x/@line))}]" type="@hidechoices">false</add> )
else ( <remove sel="(//{fn:name($x)}[@line='{$x/@line}'])[1]" />)
)
由于某种原因无法使用position()来限制内部for,它在第一次迭代时返回所有节点。 非常感谢你的帮助!
答案 0 :(得分:3)
你的整个方法都存在缺陷。 XQuery是一种函数式语言,你描述问题的方式和你编写的查询表明你还没有完全掌握函数式编程范式(这是完全可以理解的,因为它与程序编程完全不同)。我建议你一般性地阅读这个主题。
不是以过程方式迭代所有元素,而是可以使用group by来使用FLWOR表达式:
let $map := map:merge((
for $x in //a
let $line := $x/@line
group by $line
return map:entry($line, count($x))
))
这保留了您期望的结果。它迭代a
个元素,并按line
属性将它们组合在一起。
另一个评论:sel
属性中的输出XML看起来很像某个元素的路径。您是否了解fn:path函数,它能为您提供哪些功能?
根据您对评论的更新,您可以多次计算地图,但只能计算当前位置:
for $y at $pos in //a
let $map := map:merge((
for $x in //a[position() <= $pos]
let $line := $x/@line
group by $line
return map:entry($line, count($x))
))
return $map