This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 19158 - [XT30] Getting all values from a map
Summary: [XT30] Getting all values from a map
Status: CLOSED FIXED
Alias: None
Product: XPath / XQuery / XSLT
Classification: Unclassified
Component: XSLT 3.0 (show other bugs)
Version: Working drafts
Hardware: All All
: P2 normal
Target Milestone: ---
Assignee: Michael Kay
QA Contact: Mailing list for public feedback on specs from XSL and XML Query WGs
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-10-01 09:53 UTC by Jakub Maly
Modified: 2014-05-15 14:00 UTC (History)
0 users

See Also:


Attachments

Description Jakub Maly 2012-10-01 09:53:45 UTC
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).
Comment 1 Michael Kay 2012-10-01 16:53:43 UTC
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).
Comment 2 Jakub Maly 2012-10-01 17:32:45 UTC
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.
Comment 3 Jakub Maly 2012-10-08 20:43:57 UTC
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)?
Comment 4 Michael Kay 2012-10-08 22:30:43 UTC
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.
Comment 5 Michael Kay 2013-02-12 10:23:38 UTC
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.
Comment 6 Michael Kay 2013-05-02 14:16:02 UTC
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.