Create windows of results per alphabet - xquery

My XML file is here.
I would like to get the results of all movie titles by alphabet in the following manner:
<window>
<title>Alpha</title>
<title>Abel</title>
<window>
<title><babylon</title>
...
<window>
...
<window>
...
I am using tumbling feature in the following code:
for tumbling window $w in db:open("movies","movies.xml")/movies/movie/title
start at $s when fn:true()
only end at $e when not starts-with($e,substring($s, 1, 1))
return <window>{ $w/../title }</window>
Basically, I am trying to extract first character of each words and grouping them together.
However, It says incomplete FLWOR expression.

A group by would be more appropriate here:
for $title in //title
order by $title
group by $g := substring($title, 1,1)
return element { $g } {
$title
}
when you really want to use tumbling you could use the start and next variables to create groups:
for tumbling window $w in //title
start $first when true()
end next $next when substring($first, 1,1) != substring($next,1,1)
return <window>{ $w }</window>

Related

(Invalid_argument "index out of bounds") while encoding a word to a Morse code

Bonjour.I am struggling with the function code_word. The purpose of this function is to take a message of type string and the Morse alphabet itself on the input, and it should return an encoded word of the same type on the output. For instance:
code_mot: string->(char * string)list -> string
code_word "SOS" alphabet;;
-: string list = ["... --- ..."]
PS: Each encoded letter of the word have to be separated by the space.
I created a function (as demonstrated below), however each time I pass it an argument, I always get an error:
Exception: (Invalid_argument "index out of bounds")
To be honest I have tried all possible combinations to solve this problem. Nonetheless, I didn't find a suitable solution.Due to this I would like to ask for help: What is the source of an error and how can I fix it ?
let alphabet =
[ ('A', "._" ); ('B', "_..." ); ('C', "_._.") ;
('D', "_.." ); ('E', "." ); ('F', ".._." );
('G', "__." ); ('H', "...." );( 'I', ".." );
('J', ".___" ); ('K', "_._" ); ('L', "._.." );
('M', "__" ); ('N', "_." ); ('O', "___" );
('P', ".__."); ( 'Q', "__._" );( 'R', "._.") ;
('S', "..." ); ('T', "_" ); ('U', ".._" );
('V', "..._" ); ('W', ".__" ); ('X', "_.._") ;
('Y', "_.__" ); ('Z', "__.." )
];;
let code_word s alpha =
let rec cw_aux s i alpha word =
match alpha with
| [] -> word
| (cha, l)::r ->
if s.[i] = cha then
cw_aux s (i+1) alpha (l^" "^word)
else
cw_aux s (i+1) r word
in
cw_aux s 0 alpha " " ;
Your code needs to iterate over two things: the incoming string, and the list of morse codes. But you have only one termination condition, the end of the morse code list. In other words, you're not checking anywhere for when you reach the end of the string. Thus your recursive calls with (i + 1) will eventually reach past the end of the string and cause your error.
It seems to me that you also have the problem that you have merged your two searches. It's not true that you want to move along to the next character in the string when you don't find a code for the current char of the string.
In other words, this call looks wrong to me:
cw_aux s (i+1) r word
It's not the case that you want to move along to the next character of the incoming string here. You want to find the correct code for the current character first.
This is a great place to use a Map to map characters to Morse codes. Then it's just a matter of mapping each character in your string to that character's binding in the map. By letting library functions handle looping over the string, the index error cannot happen.
You could just use List.assoc with your list, but that's O(n) while lookups in a map are O(log n). For simple purposes, there's likely little practical difference, but for larger strings, it will matter.
module CMap = Map.Make (Char)
Then, using your list of tuples:
let alphabet =
[ ('A', "._" ); ('B', "_..." ); ('C', "_._.");
('D', "_.." ); ('E', "." ); ('F', ".._." );
('G', "__." ); ('H', "...." );( 'I', ".." );
('J', ".___" ); ('K', "_._" ); ('L', "._.." );
('M', "__" ); ('N', "_." ); ('O', "___" );
('P', ".__."); ( 'Q', "__._" );( 'R', "._.");
('S', "..." ); ('T', "_" ); ('U', ".._" );
('V', "..._" ); ('W', ".__" ); ('X', "_.._");
('Y', "_.__" ); ('Z', "__.." )
]
We can create a map from the mappings you've created with a bit of help from the Seq (sequence) module.
let map = alphabet |> List.to_seq |> CMap.of_seq
(Note that the |> operator allows us to write the above, rather than CMap.of_seq (List.to_seq alphabet). This becomes more helpful in the following code.)
Now, really we just need to map an input string to the Morse code for each character. To do that we'll turn a string int a sequence, map, and then convert to a list.
"Hello"
|> String.to_seq
|> Seq.map Char.uppercase_ascii
|> Seq.map (fun ch -> CMap.find ch map)
|> List.of_seq
Result:
["...."; "."; "._.."; "._.."; "___"]
You would now simply need to join these together into a single string.
You might still received a Not_found exception if you try to encode a string which includes characters for which there are not Morse code mappings. You'd either want to handle this exeception, or add more Morse code bindings to your map.

Symfony Invalid parameter format, : given

I need create multiply search by years. From request I get string like 2017,2018 and then I want get Questions which createdAt, between from start year and end year. I have query builder with part, and I'am not understand why I have this error
if ($paramFetcher->get('years')) {
$orXSearch = $qb->expr()->orX();
$yearData = trim($paramFetcher->get('years'));
foreach (explode(',', $yearData) as $key => $id) {
if (!$id) {
continue;
}
$orXSearch
->add($qb->expr()->between('q.createdAt', ':'.$key.'dateFrom', ':'.$key.'dateTo'));
$date = $this->additionalFunction->validateDateTime($id, 'Y');
$first = clone $date;
$first->setDate($date->format('Y'), 1, 1);
$first->setTime(0, 0, 0);
$last = clone $date;
$last->setDate($date->format('Y'), 12, 31);
$last->setTime(23, 59 , 59);
$qb
->setParameter($key.'dateFrom', $first->format('Y-m-d H:i:s'))
->setParameter($key.'dateTo', $last->format('Y-m-d H:i:s'));
}
$qb->andWhere($orXSearch);
}
error:
symfony Invalid parameter format, : given, but :<name> or ?<num> expected.
In your foreach loop, you’re looping over the result of an explode operation which yields a numeric array, i.e. $key will always have a numeric value.
Hence, your parameter placeholder is colon + number + string, i.e. :1dateFrom. This is not allowed. Either you reference a string value with a colon + string placeholder (:foo), or you reference a numeric value with a question mark + number value (?1).
Your problem is easy to solve: Simply add any letter between the colon and the number, and you’re good:
->add($qb->expr()->between(
'q.createdAt',
':x'.$key.'dateFrom',
':x'.$key.'dateTo'
));

XQuery with if condition in for loop

I have written xquery to return results in normal way.
let $results := //data:data
return
<result>
{
for $i in $results
return
<documentInformation>
<id>{data($i/DATA:ID)}</id>
<status>{data($i/#status)}</status>
<title>{data($i/data:title)}</title>
<displayName>{data($i/DATA:DISPLAYNAME)}</displayName>
</documentInformation>
}
</result>
Now, I have to filter out the results in for loop with some condition like
(pseudo logic)
if id = 'abc' and status ="closed"
then skip the row
else add row.
I have tried several ways. but could not run the query..
Try this:
<result>
{
for $i in //data:data
where fn:not($i/DATA:ID = 'abc' and $i/#status = "closed")
return
<documentInformation>
<id>{data($i/DATA:ID)}</id>
<status>{data($i/#status)}</status>
<title>{data($i/data:title)}</title>
<displayName>{data($i/DATA:DISPLAYNAME)}</displayName>
</documentInformation>
}
</result>
Note that the XPath //data:data may have a lot of work to do, but that's a separate matter.
You Can also use if condition instead of where
<result>
{
for $i in //data:data
return
if($i/DATA:ID != 'abc' and $i/#status != "closed")
then
(
<documentInformation>
<id>{data($i/DATA:ID)}</id>
<status>{data($i/#status)}</status>
<title>{data($i/data:title)}</title>
<displayName>{data($i/DATA:DISPLAYNAME)}</displayName>
</documentInformation>
)
else ()
}
</result>

wrapping child node content in xquery

I have what I hope is a really simple question, but I'm a novice at xquery and I can't make this work:
I have the following bit of xml:
<collation>1<num>12</num> 2<num>12</num> ||
I<num>8</num>-V<num>8</num>, 1 flyleaf</collation>
That I need to transform so that becomes the content of a new node, like so:
<note displayLabel="Collation: Notes">1(12) 2(12) || I(8)-V(8), 1 flyleaf<note>
I am using the following xquery code to attempt to do this:
<note displayLabel="Collation:Notes">{for $t in doc("collation.xml")//collation,
$h in distinct-values($t)
return
????
</note>
The problem is that I can either display all of the content (so without the parentheses) using data($t), or I can display just the things I want to be in parentheses (the information in the tags) using data($t/num) but I can't figure out how to display both with the items in tags wrapped in parentheses. I'm sure it's a really simple answer but I can't find it.
This is a good job for recursion:
declare function local:render(
$n as node()
) as node()?
{
typeswitch($n)
case element(num) return text{concat('(', $n, ')')}
case element(collation) return
<note displayLabel="Collation: Notes">{
for $n in $n/node()
return local:render($n)
}</note>
case element() return element { node-name($n) } {
for $n in $n/node()
return local:render($n)
}
default return $n
};
local:render(
<collation>1<num>12</num> 2<num>12</num> || I<num>8</num>-V<num>8</num>, 1 flyleaf</collation>)
=>
<note displayLabel="Collation: Notes">1(12) 2(12) || I(8)-V(8), 1 flyleaf</note>

How to convert text inside a node into child node using xquery update?

I have a xml document like
<root>
<first>
First Level
<second>
second level
<third>
Third Level
</third>
</second>
<second2>
another second level
</second2>
</first>
</root>
How to convert this document with all nodes, that means if a node contains text and child node convert text into a child node (let's say childtext) using xquery-update
<root>
<first>
<childtext>First Level</childtext>
<second>
<childtext>second level</childtext>
<third>
Third Level
</third>
</second>
<second2>
another second level
</second2>
</first>
</root>
And here is what I tried:
let $a :=
<root>
<first>
First Level
<second>
second level
<third>
Third Level
</third>
</second>
<second2>
another second level
</second2>
</first>
</root>
return
copy $i := $a
modify (
for $x in $i/descendant-or-self::*
return (
if($x/text() and exists($x/*)) then (
insert node <childtext>
{$x/text()}
</childtext> as first into $x
(: here should be some code to delete the text only:)
) else ()
)
)
return $i
I could not delete the text which has sibling node.
As you want to replace an element, you should simply use the replace construct, instead of inserting the new element and deleting the old one. Seems much simpler to me:
copy $i := $a
modify (
for $x in $i/descendant-or-self::*[exists(*)]/text()
return replace node $x with <childtext>{$x}</childtext>
)
return $i
The modified solution is from #dirkk here:
copy $i := $a
modify (
for $x in $i/descendant-or-self::*
return (
if($x/text() and exists($x/*)) then (
if(count($x/text())=1) then (
replace node $x/text() with <child> {$x/text()}</child>
) else (
for $j at $pos in 1 to count($x/text())
return
replace node $x/text()[$pos] with <child>{$x/text()[$pos]}</child>
)
) else ()
)
)
return $i
Thank you Once again.

Resources