How to repeat a function call in XQuery?

Viewed 53

In this example, I have a string $text and would like to call fn:replace on it four times to get the result "zyxw".

xquery version "1.0-ml";
let $text := "abcd"
let $patterns := ("a", "b", "c", "d")
let $replaces := ("z", "y", "x", "w")
for $i in (1 to fn:count($patterns))
  let $p := $patterns[$i]
  let $r := $replaces[$i]
  let $text := fn:replace($text, $p, $r)
  return $text

Instead, I get the sequence:

("zbcd", "aycd", "abxd", "abcw")

I get why that's happening. I'm just not sure how to accomplish what I'm trying to do in XQuery. The JavaScript equivalent of what I want is this:

'use strict';
let text = "abcd";
let patterns = ["a", "b", "c", "d"];
let replaces = ["z", "y", "x", "w"];
for(let i = 0, len = patterns.length; i < len; ++i){
  text = fn.replace(text, patterns[i], replaces[i]);
}
text
3 Answers

Most natural solution in XQuery would be to use recursion:

declare function local:recurse($text, $patterns, $replaces) {
  let $text := fn:replace($text, fn:head($patterns), fn:head($replaces))
  let $remainder := fn:tail($patterns)
  return
    if (fn:exists($remainder)) then
      local:recurse($text, $remainder, fn:tail($replaces))
    else
      $text
};

let $text := "abcd"
let $patterns := ("a", "b", "c", "d")
let $replaces := ("z", "y", "x", "w")
return local:recurse($text, $patterns, $replaces)

HTH!

Try a somewhat different approach:

for $i in (1 to fn:string-length($text))  
  let $target := substring($text,$i,1)
  let $fin := $replaces[index-of($patterns, $target) ]
return
concat($fin,"")

Output should be:

z y x w

This should also work for situations where your $text is not in the same order as $patterns; for example, where it is "bdca".

Yes recursion is one legit way of doing as suggested by grtjn. Another way, I just made a small tweak in your code for desired output. Try this -

let $text := "abcd"
let $patterns := ("a", "b", "c", "d")
let $replaces := ("z", "y", "x", "w")
let $replace-op :=
  for $i in (1 to fn:count($patterns))
  let $p := $patterns[$i]
  let $r := $replaces[$i]
  let $new-text := fn:replace($text, $p, $r)
  return xdmp:set($text, $new-text)
return $text

And I would suggest, if $pattern and $replaces variables are large, then use proper map technique to bind them.

Related