Fravadona
Fravadona

Reputation: 17098

XPath: How to sort a map?

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?

Upvotes: 1

Views: 200

Answers (2)

Fravadona
Fravadona

Reputation: 17098

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) })
(:
    $map => map:keys() => sort((), $map) => reverse()
:)

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 })

Upvotes: 1

Michael Kay
Michael Kay

Reputation: 163352

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])}))

Upvotes: 2

Related Questions