XQuery average value of attribute - xquery

I have the following XML input
<foo>
<bar a=1 b=25/>
<bar a=2 b=30/>
<bar a=1 b=35/>
<bar a=2 b=40/>
<foo>
and I need to get the following result
<foo>
<bar a=1>average of b = 30</bar>
<bar a=2>average of b = 35</bar>
<foo>
I have hard time to do distinct value of a.
I have
<foo>
{for $e in doc(XXX.xml)/foo/bar
return
<bar a="distinct-values(data($e/#a))">
{for $c in distinct-values(data($e/#a))
return avg(data($e/#b))}
</bar>}
</foo>
What am I missing?

If your XQuery processor supports XQuery 3.0 you can use the group by construct, which is exactly designed for such a use case and, in my opinion, much nicer to read:
<foo>{
for $el in doc(XXX.xml)/foo/bar
let $a := $el/#a
group by $a
return
<bar a="{$a}">average of b = {avg($el/#b)}
</bar>
}
</foo>
It groups each bar element by the a attribute and computes (and outputs) the average of the b attribute.

I hope this is what you are looking for.
let $doc := <foo>
<bar a="1" b="25"/>
<bar a="2" b="30"/>
<bar a="1" b="35"/>
<bar a="2" b="40"/>
</foo>
let $result := <foo>
{for $a in fn:distinct-values($doc//bar/#a)
for $b in avg($doc//bar[#a=$a]/#b)
return
<bar a="{$a}">
averge of b = {$b}
</bar>
}
</foo>
return $result
Proper for loop will do.

Related

Unable to understand this xquery statement

I am new to xquery and unable to understand what does it means :
$bottles=getallBottles()
$cups=getallCups()
<containers>
{
($bottles,$cups) //this line i am unable to get
}
<containers>
The comma forms a sequence. Presumably $bottles is a sequence of zero-to-many items and $cups is a sequence of zero-to-many items. The comma forms a sequence of all of the items in $bottles and all of the items in $cups.
For example:
let $x := (1, 2, 3)
let $y := ('a', 'b', 'c')
return ($x,$y)
yields:
1 2 3 a b c
In the above example, the parentheses are necessary so that forming the sequence of $x, $y takes precedence over return and the entire constructed sequence is returned.
In an example similar to the original question, parentheses are unnecessary because precedence is not ambiguous:
let $x := <a><x>5</x><x>6</x></a>
let $y := <b><y>1</y><y>2</y></b>
return <container>{$x, $y}</container>
yields:
<container><a><x>5</x><x>6</x></a><b><y>1</y><y>2</y></b></container>

XQuery difference between same function different implementation

Return the number of cycles:
let $bd := doc("document")
return count ( for $c in $bd//cycle
where $c[#id]
return $c
)
Every cycle has an ID, not important here but it is a must to specify it.
What is the difference between the above use of count and the below use of count?
let $bd := doc("document")
let $c := $bd//cycle[#id]
return count($c)
I dont know the difference between these 2 XQueries return same result but following the same pattern the next 2 queries should work but the 2nd one doesnt... Here they are:
The total of hours of modules which is above 100.
*Working query*
let $bd:=doc("document")
return sum (
for $m in $bd//module[#id]
where $m/hours>100
return $m/hours
)
*Not working query*
let $bd := doc("document")
for $c in $bd//module[#id]
where $c/hours>100
return sum($c/hours)
Id like to know why following the same "pattern" the second query is not working.
The output of the not working query is this one:
160 160 256 224 192 160
Its not the result i need, I want the sum of all them.
The first two expressions are functionally equivalent. The difference is the use of FLWOR vs. XPath to select your sequence.
In the second example, you are calling sum() on each item of the sequence ($c/hours), instead of on the sequence itself:
let $bd := doc("document")
return sum(
for $c in $bd//module[#id]
where $c/hours>100
return $c/hours)
You could also use XPath:
let $bd := doc("document")
let $c := $bd//module[#id][hours>100]
return sum($c/hours)
Or similarly assign the result of the FLWOR to a variable and sum that:
let $bd := doc("document")
let $c :=
for $m in $bd//module[#id]
where $m/hours>100
return $m/hours
return sum($c)

How to avoid "invalid lexical value" while adding empty nodes

let $a := <product>
<p1>100</p1>
<p2>100</p2>
<p3/>
</product>
for $i in $a
return $i/p1 + $i/p2 + $i/p3
Why do i get invalid lexical value here when i expect the sum to be displayed?
Your last line return $i/p1 + $i/p2 + $i/p3 is evaluated as return xs:double($i/p1) + xs:double($i/p2) + xs:double($i/p3). This works for p1 and p2, but not p3:
xs:double($i/p3) = xs:double(<p3/>)
= xs:double(xs:untypedAtomic('')) (: atomization :)
= xs:double('')
Since + returns the empty sequence () if one of its arguments is the empty sequence, your approach would not have worked either way. You can use fn:sum($sequence) instead, summing over the text nodes inside the elements:
let $a :=
<product>
<p1>100</p1>
<p2>100</p2>
<p3/>
</product>
for $i in $a
return sum(($i/p1/text(), $i/p2/text(), $i/p3/text()))
The last line can even be shortened to:
return sum($i/(p1, p2, p3)/text())

PowerShell Math Problem?

function other3($x, $y)
{
$tmp = $x + $y
return $tmp
}
$x = 5
$y = 10
$a = other3($x, $y)
Write-Host $a
Keeps returning 5 10 when it should be returning 15, what's the deal?
To call other3 with two parameters, drop the parenthesis "()" e.g.
$a = other3 $x $y
The way you're currently calling it, actually passes one parameter, an array with two elements, i.e. 5 and 10. The second parameter is empty (probably defaults to null), meaning the addition does nothing and you simply return the $x parameter.
You're passing a list (5,10) to the parameter $x and $null to $y.
When the function adds $null to the list, you just get the list back.
Adding some write-host statements to the function should make this clear:
function other3($x, $y)
{
$tmp = $x + $y
write-host "`x=($x)"
write-host "`y=($y)"
return $tmp
}
$x = 5
$y = 10
$a = other3($x, $y)
Write-Host $a

Manipulating lists in OCaml

I am having issues manipulating deeply nested lists in OCaml in the below context.
class foo (pIn:int)=
object (self)
val p = pIn
val even = if (pIn mod 2) = 0 then true else (false)
method doIt = "doIt"
method isEven = even
method getP = p
end;;
let rec createListOfElements howMany = (
Random.self_init ();
if howMany > 1 then ((new foo (Random.int 10))::(createListOfElements (howMany - 1)))
else ([(new foo (Random.int 10))]) );;
let myList = createListOfElements 5;;
let rec process1 param =
if param <= 10 then
let f = new foo param in (
if f#isEven then (myList <- List.append myList (createListOfElements f#getP));
Printf.printf "%s\n" f#doIt;
process1 (param+1) )
in process1 0;;
The error I get is, "Unbound instance variable myList". How do I go about assigning the result of "List.append myList (createListOfElements f#getP) to myList in this context?
Thanks!
Edited function:
let myList = ref (createListOfElements 5);;
let rec process1 param =
if param <= 10 then
let f = new foo param in (
if f#isEven then (myList <- !myList # (createListOfElements f#getP));
Printf.printf "%s\n" f#doIt;
process1 (param+1) )
in process1 0;;
You have to use references to break persistence --since functional programming uses persistent data. Use the ref keyword in the declaration of myList:
let myList = ref (createListOfElements 5)
To dereference the list use !, so the line in question becomes
if f#isEven then
myList := !myList # f#getP;
I suggest you use an accumulator as it's in the spirit of the functional-programming style, like this:
let rec process1 lst = function
| x when x <= 10 ->
let f = new foo x in
if f#isEven then
process1 (lst # (createListOfElements f#getP)) (param+1)
else
process1 lst (param+1)
| _ -> lst
EDIT:
I didn't compile my code and didn't notice that you are using the wrong symbol to change the value of the reference. The correct symbol is, :=. See my change above. I strongly suggest you avoid references, though, and go the accumulator route.

Resources