1

I'm using a map for counting the number occurrences of, for example, each possible value of the attr attribute of the elem nodes:

<root>
    <elem attr="a"/>
    <elem attr="b"/>
    <elem attr="b"/>
    <elem         />
    <elem attr="a"/>
    <elem attr="c"/>
    <elem attr="b"/>
</root>
fold-left(
    //elem/@attr,
    map{},
    function($m,$a) {map:put($m, $a, sum((1, $m($a))))}
)

Resulting map:

{
  "a": 2,
  "b": 3,
  "c": 1
}

Now, using this map, I would like to sort the integer values in descending order and emit their associated key. The expected "output" would be:

b
a
c

How can I do it?

Fravadona
  • 13,917
  • 1
  • 23
  • 35

2 Answers2

2

I can't see why you would want a map for this. It's essentially a grouping-and-sorting problem, therefore much easier in XSLT or XQuery, but if it has to be pure XPath, you can use

let $in := . 
return (distinct-values(//@attr) 
        => sort((), function($a){-count($in//@attr[.=$a])}))
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
1

If you store the map in a variable then it's possible to call fn:sort on the keys while using the associated values as "sort keys":

let
    $map := map{ "a": 2, "b": 3, "c": 1 }
return
    $map => map:keys() => sort((), function($key) { -$map($key) })
b
a
c

ASIDE

You can also define a map:sort function for maps, which would return a sequence of keys:

function(
    $map       as map(*),
    $collation as xs:string?,
    $valuate   as function(xs:anyAtomicType, item()*) as xs:anyAtomicType*
) as xs:anyAtomicType*
{
    sort(
        map:keys($map),
        $collation,
        function($key) { $valuate($key, $map($key)) }
    )
}
let $map:sort := function (...) {...}
return
    map{ "a": 2, "b": 3, "c": 1 }
    => $map:sort((), function($k,$v) { -$v })
Fravadona
  • 13,917
  • 1
  • 23
  • 35