How to iterate over a map in XQuery? - xpath

Let's say I have map defined in XQuery like this:
declare namespace map="http://www.w3.org/2005/xpath-functions/map";
let $x := map{'a':1, 'b':2}
How do I iterate through $x without knowing the keys?
For example:
for $key, $value in $x
(: Some processing and output :)

From the BaseX wiki on XQuery 3.1 features:
The fact that a map is a function item allows it to be passed as an argument to higher-order functions that expect a function item as one of their arguments. As an example, the following query uses the higher-order function fn:map($f, $seq) to extract all bound values from a map:
let $map := map { 'foo': 42, 'bar': 'baz', 123: 456 }
return fn:for-each(map:keys($map), $map)
So, let's say that you wanted to call do-some-processing() with each key and value:
declare namespace map="http://www.w3.org/2005/xpath-functions/map";
declare variable $map := map{'a':1, 'b':2};
declare function local:do-some-processing($key, $value) {
(: deciding what to put here is your problem, not mine :)
<key id="{$key}">{$value}</key>
};
fn:for-each(
map:keys($map),
function($k) { local:do-some-processing($k, $map($k)) }
)

Both of my examples return the key with the value incremented by 1.
The more "imperative" way would be to get a list of all keys, loop over them and look up the values for each of them:
declare namespace map="http://www.w3.org/2005/xpath-functions/map";
let $x := map{'a':1, 'b':2}
for $key in map:keys($x)
return $key || $x($key) + 1
A more elegant, functional approach would be to use map:for-each to "map" a (possibly anonymous) function onto the map (this is the term in functional programming when applying a function to each value of a list or sequence):
declare namespace map="http://www.w3.org/2005/xpath-functions/map";
let $x := map{'a':1, 'b':2}
return map:for-each($x, function($key, $value) { $key || $value + 1 })
Unlike for most other XQuery features and functions, the BaseX documentation (which usually limits to BaseX-specific contents) offers a rather easy to read and understand reference and tutorial on the XQuery map feature.

Related

In XPath 3.1's array:filter, can I specify a filtering function that takes more than one argument (i.e. a value to test the item *against*)?

I want to filter an array of maps based on a value for one of the keys. The problems seems to be that the second parameter of array:filter() is a function that accepts only a single item parameter: array:filter($array as array(*), $function as function(item()*) as xs:boolean) as array(*) according to the XPath and XQuery Operators 3.1 specs.
But when I do array:filter($routingTable, function ($i) {$i?input ne $wid}), with $wid being defined somewhere before this call, this variable is not being passed into the function and the comparison misses the crucial entries. (Checking the value of $wid with debugging output right before the call to array:filter confirms it has the correct value. Checking $i?input inside the anonymous functions confirms this value, too. But checking $wid inside the anonymous function makes it appear it is empty.)
So I thought maybe I need to pass the value to compare against to the filtering function as a second parameter, but when I do array:filter($routingTable, function ($i, $wid) {etc... , I get a java.lang.ArrayIndexOutOfBoundsException error. I assume this is due to the excess argument of the function. How should I go about this?
For what it's worth, my XQuery processor is eXist-db (6.1.0) and here is more complete code (the call that's at issue (I assume) is the second line of the last function):
(: for a sequence of nodes, I build maps and put them into an array. that is my routing table that is then posted :)
declare function my:createRoutes($wid as xs:string) {
let $index := doc($config:index-root || "/" || $wid || ".xml")/my:index
let $routingTable := array{fn:for-each($index//my:node, function($k) {my:buildRoutingInfoNode($wid, $k)} )}
return my:postRoutingTable($routingTable)
};
(: helper function to create a map from a node :)
declare function my:buildRoutingInfoNode($wid as xs:string, $item as element(my:node)) {
map { "input" : concat($wid, ":", $item/#citeID/string()), "outputs" : array { ( $item/#crumb/string(), 'yes' ) } }
};
(: here the routing table is posted. However, if the entries are already present in the "live" table, I need to clean them from there first :)
declare function my:postRoutingTable($routes as array(*)) as xs:integer {
if (array:size($routes) = 0) then
0
else
let $testmap := $routes?1 (: okay, that's a bit cheap: I just check the first of the new entries. :)
let $src := $testmap?input
let $dest := $testmap?outputs
return if (not(my:isInRoutingTable($src, $dest))) then
... post via http request ...
else (: At least one key is already present, need to clean routing table for $wid first :)
let $wid := substring-before($src, ":")
let $cleanStatus := my:cleanRoutingTable($wid)
return if ($cleanStatus ge 0) then
my:postRoutingTable($routes) (: retry ... :)
else
-1 (: cleaning failed ... :)
};
(: remove all entries about the $wid (so that I can add them again) :)
declare function my:cleanRoutingTable($wid as xs:string) as xs:integer {
let $routingTable := my:getRoutingTable() (: get "live" table :)
let $cleanedRT := array:filter($routingTable, function ($i) {
substring($i?input, 1, 5) ne $wid
}) (: remove all entries concerning the to-be-posted $wid :)
let $deleteStatus := my:deleteRoutingTable() (: drop the complete live table :)
return if (array:size($cleanedRT) > 0) then (: if after removing entries, anything is left of the original "live" table, :)
my:postRoutingTable($cleanedRT) (: then re-post this "cleaned" table :)
else -1
};
On the face of it this looks like an eXist-db bug. The variable $wid is part of the closure of the anonymous function and its value should be accessible; your code looks fine -- though without a complete repro (source document and expected results) I haven't tested it elsewhere.
Passing a second variable to a filter function
To dynamically filter a list of items, one often needs to pass a second, dynamic parameter.
The usual way of a lambda or anonymous function that reads a scoped variable would be:
let $upper-bound := 4 (: this might be read from user input :)
return filter(1 to 9, function ($item) {
$item < $upper-bound
})
With a little meta programming and leveraging the argument placeholder ? this can also be rewritten as
declare function local:filter ($item, $upper-bound) {
$item < $upper-bound
};
let $upper-bound := 4
return filter(1 to 9, local:filter(?, $upper-bound))
local:filter(?, $upper-bound) will return a function with an arity of 1 which is exactly what fn:filter and array:filter expect.
Main benefit is that it allows to reuse filter functions (here local:filter).
This function can now also be defined in an imported module.

Use JSONata to alpha sort keys in a JSON object

I'll start off by saying I'm aware that, acording to json.org, "An object is an unordered set of name/value pairs." Nonetheless, in the real world, sometimes it would be nice to view keys in alphabetical order.
Unsorted:
{"B":2,"A":1,"C":3}
Sorted:
{"A":1,"B":2,"C":3}
Is there a way to do this in JSONata? (I understand I can pre- or post-process the data outside JSONata, but am curious whether there's a way to do this via JSONata.)
Thank you.
I have just written the following function:
$sortObjectAlphabetically := function($obj){
(
$keys := [$keys($obj)];
$keys := $keys^(<$);
$merge([$map($keys,function($e){
(
{
$e : $lookup($obj,$e)
}
)
})]);
)
};
I've done an example at this link: https://try.jsonata.org/RbJqQxO0i

Convert: "preg_replace" -> "preg_replace_callback"

I'm trying to update my code but I'm stuck at this codeline.
How do I proceed to convert this to preg_replace_callback?
$buffer = preg_replace("#§([a-z0-9-_]+)\.?([a-z0-9-_]+)?#ie","\$templ->\\1(\\2)",$buffer);
Here is the process of converting preg_replace (with the e modifier) to preg_replace_callback. You create a function that will act on all of the matches that it finds. Normally this is pretty simple, however with your case it is a little more complex as the function returns the value of an object. To accommodate this, you can use an anonymous function (a function without a name) and attach the USE keyword with your object to it. This can be done inline, however for the sake of clarity, I have made it its own variable.
Take a look at this portion of the complete code below:
$callback_function = function($m) use ($templ) {
I created a variable named callback_function that will be used in the preg_replace_callback function. This function will be fed each match as the variable $m automatically. So within the function you can use $m[1] and $m[2] to access the parts of the expression that it matched. Also note that I've attached the $templ variable with the USE keyword so that $templ will be available within the function.
Hopefully that makes sense. Anyway, here is the complete code:
<?php
// SET THE TEXT OF THE BUFFER STRING
$buffer = 'There are a bunch of §guns.roses growing along the side of the §guns.road.';
// THIS IS JUST A SAMPLE CLASS SINCE I DO NOT KNOW WHAT YOUR CLASS REALLY LOOKS LIKE
class Test {
// FUNCTION NAMED 'guns' WITH A SPACE FOR A PARAMETER
public function guns($info) {
return '<b>BLUE '.strtoupper($info).'</b>';
}
}
// INSTANTIATE A NEW 'Test' CLASS
$templ = new Test();
// THIS IS THE FUNCTION THAT YOUR CALLBACK WILL USE
// NOTICE THAT IT IS AN ANONYMOUS FUNCTION (THERE IS NO FUNCTION NAME)
$callback_function = function($m) use ($templ) {
return $templ->$m[1]($m[2]);
};
// THIS USES PREG_REPLACE_CALLBACK TO SUBSTITUTE OUT THE MATCHED TEXT WITH THE CALLBACK FUNCTION
$buffer = preg_replace_callback('/§([a-z0-9-_]+)\.?([a-z0-9-_]+)?/i', $callback_function, $buffer);
// PRINT OUT THE FINAL VERSION OF THE STRING
print $buffer;
This outputs the following:
There are a bunch of <b>BLUE ROSES</b> growing along the side of the <b>BLUE ROAD</b>.

SCSS : Checking if a value exists in a multi dimensional list

I'm using Sass (v3.3.0.alpha.392), and I have a multi-dimensional list that looks somewhat like this,
$list: (
config: (
foo: (
a: 1,
b: 2,
),
bar: (
a: 3,
b: 4,
),
));
I have a #mixin that takes an argument, and will output based on which argument is passed if it exists in the config $list. Something like this,
#mixin foo($arg) {
#if $arg exists in $list == true { // is 'foo' or 'bar'
// Do something
} #else {
#warn "failed.";
}
}
Usage,
.test {
#include foo(bar); // should return true and run code block
#include foo(data); // should return false and #warn
}
But, I cannot find the correct function to check if $arg exists in $list. I have tried small functions that check the index,
#function exists($n) {
#return (false == index($list, config $n));
}
But, this don't seem to work (I might just be using it incorrectly). I basically need this to switch between different operations based on if the arg is contained in the $list.
Also, might be a double question, but is there also any way to check if the $arg is an integer or string? Would also be very helpful.
Thanks!
Your $list is actually a map:
type-of($list) == map
And if I understand correctly, you are interested in matching your keys to a value (e.g. bar).
There is a function called map-has-key($map, $key) that returns true if it finds the matching key, but in a multidimensional map it only matches keys in the topmost level. However, you can write a simple wrapper function that recursively apply map-has-key through all the levels of your map until it finds a match. The wrapper function could be something like this:
#function rmhk($m, $a){
#if map-has-key($m, $a) { #return true; };
#each $key,$e in $m {
#if (type-of($e) == map) and rmhk($e, $a) { #return true; }
}
#return false;
}
DEMO
The same way (with a recursive function) you can also match the values in your map in addition to the keys or traverse a list instead of a map (DEMO with value-matching in a multidimensional list).
And, as already pointed out by #Jeremy, you can use type-of() to check for the type of a variable (like string,number,list,map,color), but if for some reason you need to check for something more specific you would need a custom function (for example, this short function for checking if a value is an integer: DEMO).
You can use type-of for your second question like so:
#if type-of($arg) == number {
...
}
for the first question, you cannot access a list with a non-numeral index so, your helper index($list, config $n) breaks. I don't know why you are using a multi-dimensional list, sass isn't really built for it and there isn't really a need. On a normal list using
#if index($list, $arg) != false {
...
}
would be just fine, my suggestion is to go in and refactor your code so it doesn't need the 2d list but if you must then you must and you have to iterate over each item on each level to check if $arg exists in each "list within a list" if that makes sense...
so you would get (my sass is a little rusty I dont know if #each is the iterating function on a list)
#each $list in lists {
#if type-of($list) == list {
#if index($list, $arg) != false {
...
}
}
#else {
go deeper
}
}
This is ugly, and to get it to run in any kind of way is going to be a problem. What you are trying to do is use Sass in a way that it is not, you are trying to make sass lists behave like javascript objects and they are very very different. You should look into just using the new map functions for their key-values which will really be all you should need to do anything (in fact making a map as a value in a map is what a javascript object is and you could do it that way)

What are nested functions? What are they for?

I've never used nested functions, but have seen references to them in several languages (as well as nested classes, which I assume are related).
What is a nested function?
Why?!?
What can you do with a nested function that you cannot do any other way?
What can you do with a nested function this is difficult or inelegant without nested functions?
I assume nested functions are simply an artifact of treating everything as an object, and if objects can contain other objects then it follows.
Do nested functions have scope (in general, I suppose languages differ on this) just as variables inside a function have scope?
Please add the language you are referencing if you're not certain that your answer is language agnostic.
-Adam
One popular use of nested functions is closures. In a lexically scoped language with first-class functions it's possible to use functions to store data. A simple example in Scheme is a counter:
(define (make-counter)
(let ((count 0)) ; used to store the count
(define (counter) ; this is the counter we're creating
(set! count (+ count 1)) ; increment the count
count) ; return the new count
counter)) ; return the new counter function
(define mycounter (make-counter)) ; create a counter called mycounter
(mycounter) ; returns 1
(mycounter) ; returns 2
In this example, we nest the function counter inside the function make-counter, and by returning this internal function we are able to access the data available to counter when it was defined. This information is private to this instance of mycounter - if we were to create another counter, it would use a different spot to store the internal count. Continuing from the previous example:
(define mycounter2 (make-counter))
(mycounter2) ; returns 1
(mycounter) ; returns 3
It's useful for recursion when there is only 1 method that will ever call it
string[] GetFiles(string path)
{
void NestedGetFiles(string path, List<string> result)
{
result.AddRange( files in the current path);
foreach(string subPath in FoldersInTheCurrentPath)
NestedGetFiles(subPath, result);
}
List<string> result = new List<string>();
NestedGetFiles(path, result);
return result.ToArray();
}
The above code is completely made up but is based on C# to give the idea of what I mean. The only method that can call NestedGetFiles is the GetFiles method.
Nested functions allow you to encapsulate code that is only relevant to the inner workings of one function within that function, while still allowing you to separate that code out for readability or generalization. In some implementations, they also allow access to outer scope. In D:
int doStuff() {
int result;
void cleanUpReturn() {
myResource1.release();
myResource2.release();
return result * 2 + 1;
}
auto myResource1 = getSomeResource();
auto myResource2 = getSomeOtherResource();
if(someCondition) {
return cleanUpReturn();
} else {
doSomeOtherStuff();
return cleanUpReturn();
}
}
Of course, in this case this could also be handled with RAII, but it's just a simple example.
A nested function is simply a function defined within the body of another function. Why? About the only reason I could think of off the top of my head is a helper or utility function.
This is a contrived example but bear with me. Let's say you had a function that had to act on the results two queries and fill an object with values from one of the queries. You could do something like the following.
function process(qryResult q1, qryResult q2) {
object o;
if (q1.someprop == "useme") {
o.prop1 = q1.prop1;
o.prop2 = q1.prop2;
o.prop3 = q1.prop3;
} else if (q2.someprop == "useme") {
o.prop1 = q2.prop1;
o.prop2 = q2.prop2;
o.prop3 = q2.prop3;
}
return o;
}
If you had 20 properties, you're duplicating the code to set the object over and over leading to a huge function. You could add a simple nested function to do the copy of the properties from the query to the object. Like this:
function process(qryResult q1, qryResult q2) {
object o;
if (q1.someprop == "useme") {
fillObject(o,q1);
} else if (q2.someprop == "useme") {
fillObject(o,q2);
}
return o;
function fillObject(object o, qryResult q) {
o.prop1 = q.prop1;
o.prop2 = q.prop2;
o.prop3 = q.prop3;
}
}
It keeps things a little cleaner. Does it have to be a nested function? No, but you may want to do it this way if the process function is the only one that would have to do this copy.
(C#) :
I use that to simplify the Object Browser view, and to structure my classes better.
As class Wheel nested in Truck class.
Don't forget this detail :
"Nested types can access private and protected members of the containing type, including any inherited private or protected members."
They can also be useful if you need to pass a function to another function as an argument. They can also be useful for making factory functions for factory functions (in Python):
>>> def GetIntMaker(x):
... def GetInt():
... return x
... return GetInt
...
>>> GetInt = GetIntMaker(1)
>>> GetInt()
1
A nested function is just a function inside another function.
Yes, it is a result of everything being an object. Since you can have variables only visible in the function's scope and variables can point to functions you can have a function that is referenced by a local variable.
I don't think there is anything that you can do with a nested function that you absolutely couldn't do without. A lot of the times it makes sense, though. Namely, whenever a function is a "sub-function" of some other function.
A common use-case for me is when a function performs a lot of complicated logic but what the function computes/returns is easy to abstract for all the cases dictated by the logic.

Resources