I want to know the length of a multidimensional array in smarty.
I have:{foreach itme=$i from=$var}{/foreach}
I try with {$var.$id|#count}
but not work.
How can I have the length for use this in a {for} cicle?
Your logic is good. You are right about using #count within a foreach loop. but your code contains some errors.
{foreach itme=$i from=$var}{/foreach}
// ^----- should be item, $i should be just i without $ symbol in front
{$var.$id|#count}
// ^-------- this should be just $i according to your foreach loop and without $var.
So a functional code would be :
{foreach item=id from=$var}
{$id|count}
{/foreach}
Note that in Smarty 3 you haven't to add # symbol in front of the count modifier. It is just ignored.
Related
I am quite new to XQuery and I am trying to get a list of all elements and all attributes.
It should look like this:
element1 #attributex, #attribue y, ...
element 2 #attribute x, #attribute y, ...
element 3 #attribute x, #attribute y, ...
I am trying this so far, but the error "Item expected, sequence found":
for $x in collection("XYZ")
let $att := local-name(//#*)
let $ele := local-name(//*)
let $eleatt := string-join($ele, $att)
return $eleatt
I feel like I am turning an easy step into a complicated one. Please help.
Thanks in advance, Eleonore
//#* gives you a sequence of attribute nodes, //* a sequence of element nodes. In general to apply a function like local-name() to each item in a sequence, for nodes you have three options:
Use a final step /local-name() e.g. //#*/local-name() or //*/local-name()
In XQuery 3.1 use the map operator ! e.g. //#*!local-name()
Use a for .. return expression e.g. for $att in //#* return local-name($att)
The local-name() function takes a single node as its argument, not a sequence of nodes. To apply the same function to every node in a sequence, using the "!" operator: //*!local-name().
The string-join() function takes two arguments, a list of strings, and a separator. You're trying to pass two lists of strings. You want
string-join((//*!local-name(), //#*!local-name()), ',')
Of course you might also want to de-duplicate the list using distinct-values(), and to distinguish element from attribute names, or to associate attribute names with the element they appear on. That's all eminently possible. But for that, you'll have to ask a more precise question.
Will elaborate - when I execute the following command :
let $value := xdmp:forest-status(
xdmp:forest-open-replica(
xdmp:database-forests(xdmp:database("Documents"))))
return $value
Above query returns a lot of information about the database "Documents" forest, like - forest-id, host-id, etc.
I only require that it should return only the "state" of my forest. How do I do that?
Use XPath to select what you want to return.
let $value := xdmp:forest-status(
xdmp:forest-open-replica(
xdmp:database-forests(xdmp:database("Documents"))))
return $value/*:state/text()
Also, no need for a FLWOR you could make it a one-liner:
xdmp:forest-status(
xdmp:forest-open-replica(
xdmp:database-forests(xdmp:database("Documents"))))/*:state/text()
Or you may find that using the arrow operator makes things easier to read instead of nested function calls and tons of parenthesis wrapping them:
(xdmp:database("Documents")
=> xdmp:database-forests()
=> xdmp:forest-open-replica()
=> xdmp:forest-status()
)/*:state/text()
The XML elements in the response are in the http://marklogic.com/xdmp/status/forest namespace. So, you would either need to declare the namespace (i.e. declare namespace f = "http://marklogic.com/xdmp/status/forest";) and use the prefix in your XPath (i.e. f:state), or just use the wildcard as I have done *:state
This is my xml-file:
<?xml version="1.0" encoding="UTF-8"?>
<QQ:Envelope xmlns:QQ="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<RR:ABCInfo xmlns:RR="http://abc.test.de/abc/SOAP-Header/1.0">
<RR:Version>2.2.2.2</RR:Version>
<RR:BuildRevision>3333</RR:BuildRevision>
<RR:BuildTimestamp>2019-01-01T00:00:00.000+02:00</RR:BuildTimestamp>
<RR:Start>2019-01-01T10:10:10.101+02:00</RR:Start>
<RR:End>2019-01-01T11:11:11.111+02:00</RR:End>
<RR:Something>2.222 sek.</RR:Something>
<RR:Anything/>
</RR:ABCInfo>
<work:WorkContext xmlns:work="http://test.com/">1234567890abcdefghijklmnopqrstuvwxyz</work:WorkContext>
</SOAP-ENV:Header>
<QQ:Body>
<TT:testA xmlns:TT="http://abc.test.de/XYZ/2.0.1" xmlns:RR="http://abc.test.de/abc/abcdefgh/1.0">
<TT:testB>
<TT:testC>
<TT:testD>
<TT:testE id="1234567" quellID="09876543">
<TT:data>urn:de:abc:test:whatever</TT:data>
<TT:changeDate>2019-02-02T02:02:02.020+02:00</TT:changeDate>
<TT:part1 listURI="urn:de:abc:codeliste:555" listVersionID="V12">
<code>555_777</code>
<name>Fischers Fritze</name>
</TT:part1>
<TT:piece2>Frische Fische fischen</TT:piece2>
<TT:begin>
<TT:date>20191231</TT:date>
</TT:begin>
</TT:testE>
</TT:testD>
</TT:testC>
</TT:testB>
</TT:testA>
</QQ:Body>
</QQ:Envelope>
I have a XQuery, where I have to return XML. The first element in the returning XML is "result". The other elements in the returning XML should be dynamically created.
I get 2 sequences from outside, though I have made 2 fix Sequences in the following example to test it.
In Sequence No 1 I get the names for the other elements.
In Sequence No 2 I get the related path to the element names in Sequence 1.
I open the XML file an read a path (there might be several elements, though in my example is only one.
Then I want to process this result in a loop and return the dynamic elements.
If I access the path with a fix value (variable $c in the following code) I get the correct value, but then I must know the elements in Sequence 1 and the path in Sequence 2.
If I concatenate the path then I get the value from all elements.
This is my XQuery Code:
declare namespace TT="http://abc.test.de/XYZ/2.0.1";
declare namespace QQ="http://schemas.xmlsoap.org/soap/envelope/";
declare function local:getValue($path) as xs:string {
if (fn:exists($path)) then
(
data($path)
) else (
""
)
};
let $a := ('part1', 'piece2', 'beginDate')
let $b := ('TT:part1/name','TT:piece2', 'TT:begin/TT:date')
for $x in doc("Test.XML")/QQ:Envelope/QQ:Body/TT:testA/TT:testB/TT:testC/TT:testD/TT:testE
return <result>
{
for $item at $ind in $a
let $c := local:getValue($x/TT:part1/name)
let $d := local:getValue($x || concat("/", $b[$ind]))
return element { $item } {$c, " --- ", $d}
}
</result>
Is there a possibility to access the path dynamically?
Thank you in advance.
http://www.xqueryfunctions.com/xq/functx_dynamic-path.html could help - at least did it help ME ;)
The functx:dynamic-path function dynamically evaluates a simple path expression. The function only supports element names and attribute names preceded by #, separated by single slashes. The names can optionally be prefixed, but they must use the same prefix that is used in the input document. It does not support predicates, other axes, or other node kinds. Note that most processors have an extension function that evaluates path expressions dynamically in a much more complete way.
In a java program, I am using Saxon Library to parse some XQuery commands.
Now, first I use tokenize() to split some text into a number of text values.
An example of the text is--
Mohan Prakash, Ramesh Saini
I use tokenize() on above text with ',' as the delimiter. And store the result of tokenize in variable $var
After this, I want to loop over those text values, and give as output the following--
Mohan Prakash,1
Ramesh Saini,2
As you can see from above, the last value in each row is a counter- for first row, this value is 1, for second row this value=2 and so on...
I thought of using something like the code below--
for $t in $var/position()
return concat($var[$t], ',', $t)
However I am getting an error that I cannot use position() on $var because $var is expected to be a node.
So there are 2 ways to resolve this--
(a) Convert the values in $var to a node
I tried text{$var} but that is not giving accurate results.
How do I convert the array of values into nodes?
(b) Use the following--
for $t in $var
Here, how do I create and use a counter within the for loop, so that the count value can be output along with each value of $t?
You can use the at keyword inside the for clause:
let $var := tokenize('Mohan Prakash, Ramesh Saini', ', ')
for $t at $pos in $var
return concat($t, ',', $pos)
This returns the two strings Mohan Prakash,1 and Ramesh Saini,2.
This XQuery (which also happens to be an XPath 2.0 expression):
for $text in 'Mohan Prakash, Ramesh Saini',
$i in 1 to count(tokenize($text, ','))
return concat(tokenize($text, ',')[$i], ',', $i, '
')
produces the wanted, correct result:
Mohan Prakash,1
Ramesh Saini,2
I want to create a counter in xquery. My initial attempt looked like the following:
let $count := 0
for $prod in $collection
let $count := $count + 1
return
<counter>{$count }</counter>
Expected result:
<counter>1</counter>
<counter>2</counter>
<counter>3</counter>
Actual result:
<counter>1</counter>
<counter>1</counter>
<counter>1</counter>
The $count variable either failing to update or being reset. Why can't I reassign an existing variable? What would be a better way to get the desired result?
Try using 'at':
for $d at $p in $collection
return
element counter { $p }
This will give you the position of each '$d'. If you want to use this together with the order by clause, this won't work since the position is based on the initial order, not on the sort result. To overcome this, just save the sorted result of the FLWOR expression in a variable, and use the at clause in a second FLWOR that just iterates over the first, sorted result.
let $sortResult := for $item in $collection
order by $item/id
return $item
for $sortItem at $position in $sortResult
return <item position="{$position}"> ... </item>
As #Ranon said, all XQuery values are immutable, so you can't update a variable. But if you you really need an updateable number (shouldn't be too often), you can use recursion:
declare function local:loop($seq, $count) {
if(empty($seq)) then ()
else
let $prod := $seq[1],
$count := $count + 1
return (
<count>{ $count }</count>,
local:loop($seq[position() > 1], $count)
)
};
local:loop($collection, 0)
This behaves exactly as you intended with your example.
In XQuery 3.0 a more general version of this function is even defined in the standard library: fn:fold-right($f, $zero, $seq)
That said, in your example you should definitely use at $count as shown by #tohuwawohu.
Immutable variables
XQuery is a functional programming language, which involves amongst others immutable variables, so you cannot change the value of a variable. On the other hand, a powerful collection of functions is available to you, which solves lots of daily programming problems.
let $count := 0
for $prod in $collection]
let $count := $count + 1
return
<counter>{$count }</counter>
let $count in line 1 defines this variable in all scope, which are all following lines in this case. let $count in line 3 defines a new $count which is 0+1, valid in all following lines within this code block - which isn't defined. So you indeed increment $count three times by one, but discard the result immediatly.
BaseX' query info shows the optimized version of this query which is
for $prod in $collection
return element { "counter" } { 1 }
The solution
To get the total number of elements in $collection, you can just use
return count($collection)
For a list of XQuery functions, you could have a look at the XQuery part of functx which contains both a list of XQuery functions and also some other helpful functions which can be included as a module.
Specific to MarkLogic you can also use xdmp:set. But this breaks functional language assumptions, so use it conservatively.
http://docs.marklogic.com/5.0doc/docapp.xqy#display.xqy?fname=http://pubs/5.0doc/apidoc/ExsltBuiltins.xml&category=Extension&function=xdmp:set
For an example of xdmp:set in real-world code, the search parser https://github.com/mblakele/xqysp/blob/master/src/xqysp.xqy might be helpful.
All the solution above are valid but I would like to mention that you can use the XQuery Scripting extension to set variable values:
variable $count := 0;
for $prod in (1 to 10)
return {
$count := $count + 1;
<counter>{$count}</counter>
}
You can try this example live at http://www.zorba-xquery.com/html/demo#twh+3sJfRpHhZR8pHhOdsmqOTvQ=
Use xdmp:set instead of the below query
let $count := 0
for $prod in (1 to 4)
return ( xdmp:set($count,number($count+1)) ,<counter>{$count }</counter>
I think you are looking for something like:
XQUERY:
for $x in (1 to 10)
return
<counter>{$x}</counter>
OUTPUT:
<counter>1</counter>
<counter>2</counter>
<counter>3</counter>
<counter>4</counter>
<counter>5</counter>
<counter>6</counter>
<counter>7</counter>
<counter>8</counter>
<counter>9</counter>
<counter>10</counter>