This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.
To get all values from a map as a single sequence I write for $k in map:keys($mymap) return $mymap($k) which is long and also probably ineffective (O(NlogN)?) I propose to add a function map:values($mymap) which would return all values from a map as a sequence. Unlike the code above, it could be implemented in O(N). Also, when I have a map entry (a map containing one item) in variable $entry, I can write map:keys($entry) to get the key of the entry. To get the value, I must write $entry(map:keys($entry)), which is a bit convoluted and could be replaced by map:values($entry).
A problem with map:values() is that in the general case it would return a sequence of sequences, which we can't represent (except as a map...). Flattening it wouldn't produce anything useful. The function could be useful in the special case where the values are all singletons, but I'm not sure we really want to specify it to work in that case and fail in other cases. Another approach is to define map:entries which returns a sequence of "pairs", each pair being a map with two entries, accessible via the keys "key" and "value". The reason we didn't put anything like this in the spec is that we weren't comfortable with any of the designs. Some members wanted to add "tuples" to the model, but we had a lack of consensus as to what they should look like (in particular, whether the components of a tuple should be named or numbered).
Oh, I forgot about the nested sequences again... There may be no nice solution, but at least some solution should be available. When I write <xsl:for-each select="$mymap"> then "." returns a 1-item map in each entry, which is near to the "map:entries" solution. However, I still have to write (.)(map:keys(.)) to get the value.
How about using higher-order functions to solve this. Let's say there is map:process HOF and it uses an accumulator to store intermediate results, the accumulator is initialized using the last parameter and the value of the accumulator is returned after the last entry was processed. For returning all values as one (flattened) sequence: map:process($mymap, function($key,$value,$acc,()) { $acc, $value }) When someone wants to handle values, which can be sequences or maps, (s)he can use more sophisticated function and accumulator structure. My only concern is whether calling $acc, $value would be effective (linear)?
I think the idea of a map:process() calling a user-supplied function with two arguments - key and value - makes good sense; effectively a mapping function applied to the entries in a map. I can't see a good use case for a "fold" (or accumulator) capability here, since the order of processing would be undefined, and aggregation across the entries in a map isn't an especially common use case. The flattened set of values could be achieved simply with map:process($map, function($k, $v){$v}) Conversion to an element structure would look like this: <xsl:function name="f:pair"> <xsl:param name="key"/> <xsl:param name="value"/> <pair key="{$key}" value="{$value}"/> </xsl:function> map:process($map, f:pair#2) Feels good.
The WG is proposing to add a map:fold function that takes as argument a callback function which is called once for each entry in the map, with three arguments: an accumulator value, and the key and value from the map entry. Noted that this could be used to implement various other functions including ones we already have such as map:keys() and new functions such as map:values() which returns the flattened values. Also noted that in principle map:fold could be implemented in terms of fn:fold-left applied to the map:keys() sequence. Use cases include grouping the entries in the map.
We decided to accept your suggestion of using a higher-order function. We looked at the possibility of a fold()-like function, but decided that a simple mapping function would do the job: map:for-each-entry(map, function(key, value) => item()*) There's been a decision to rename fn:map as fn:for-each, so this fits in. There's also some discussion going on about the order of parameters for fn:map() and fn:map-pairs and we'll look at aligning with the final outcome on those. Pleae close if this resolution is acceptable.