3
votes

A little difficult to explain the problem in a title, so here is a specific xml file example and what the code should return:

<objects>
    <object type='Brick' to='Up' />
    <object type='Cross' to='Down' />
    <object type='Brick' to='Left' />
    <object type='Brick' to='Up' />
    <object type='Circle' to='Right' />
    <object type='Circle' to='Right' />
</objects>

So I have 3 object types: Brich, Circle, and Cross, as well as 3 to's, Up, Down, Left, and Right. I want to use xquery to get something like:

<objects>
    <object type="Brick">
        <where to="Up" count="2" />
        <where to="Down" count="0" />
        <where to="Left" count="1" />
        <where to="Right" count="2" />
    </object>
    <object type="Cross">
     .
     .
     .
</objects> 

basically, for each type, I want to get sub elements for right, down, left, and up with the number of times they appear for that object type. I know that since there are restrictions I can just hard-code every single count and have a bunch of let statements, but I'm hoping somebody could suggest a better way of doing this.

2
Can you use XQuery 3 to group your elements? - Martin Honnen
I'm trying to do this in Xquery 1.0 - Assate Scharbo

2 Answers

3
votes

Here is a fully dynamic version (no hard-coding), distinct-values(...) is your friend in XQuery 1.0:

<objects>{
  let $directions := distinct-values(//@to)
  for $type in distinct-values(//object/@type)
  return <object type="{$type}">{
    for $to in $directions
    return <where to="{$to}" count="{count(//object[@type = $type and @to = $to])}"/>
  }</object>
}</objects>
0
votes

If the object types and directions are fairly static and you have lots of objects, then you're probably better off listing the known values, instead of using distinct-values, which can be slow on large sequences.

let $object-types := ('Brick', 'Circle', 'Cross')
let $directions := ('Up', 'Down', 'Left', 'Right')
for $t in $object-types
return element object {
  attribute type { $t },
  for $d in $directions
  let $count := count($objects/object[@type = $t][@to = $d])
  return element where {
    attribute to { $d },
    attribute count { $count }
  }
}

But if you couldn't know the input values ahead of time you could build those values dynamically like:

let $object-types := distinct-values($objects/object/@type)
...