在 XQuery 中为循环构建字典并计算相似节点的出现次数

Build dictionary in XQuery for loop and count occurrences of similar nodes

我正在尝试计算字典(baseX 映射)中 for 循环期间字符串的出现次数。似乎每次迭代后都会清除字典的内容。有没有办法在整个循环中保留信息?

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 
}

最后我必须从中构建一些文本,这是输出的最后一部分。

现在我在每次迭代中只得到当前的 key/value 对,所以 $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,它在第一次迭代时就返回了所有节点。 非常感谢您的帮助!

你的整个方法是有缺陷的。 XQuery 是一种函数式语言,您描述问题和编写查询的方式表明您还没有完全掌握函数式编程范式(这是完全可以理解的,因为它与过程式编程有很大不同)。我建议您通读该主题。

您可以使用 FLWOR 表达式和 group by:

而不是以程序方式遍历所有元素
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