Union of maps?
Wednesday, 15 July 2015 · 13 messages · last reply 20 Jul 2015
MG
Hi Graydon, have a look here https://github.com/xokomola/fold/blob/dbb79c60a7356e32a0994e581ad4f7f5377ddc72/webapp/fold-common/common.xqm#L125
hope it helps
--Marc
Quoted text · Graydon Saunders · 13 lines
> On 15 jul. 2015, at 20:01, Graydon Saunders <graydonish at gmail.com> wrote: > > Hi -- > > In BaseX 8.2.1, I have a bunch of maps; the maps may have some overlap of key values. I would like to merge/find the union of all of these maps while retaining all the values associated with each key in the resulting map-of-all-maps. (Combined map? Any word can be wrong, here! :) > > map:merge() doesn't do this; http://docs.basex.org/wiki/Map_Module#map:merge says > "The associated value for each such key is taken from the last map in the input sequence $input that contains an entry with this key." and testing confirms this. > > Is there a straightforward way to do this that I'm missing? > > Thanks! > Graydon
MG
Marc van Grootel15 Jul 2015 21:58 #
Looking at that code again I think it should be rewritten to use
map:for-each it's too convoluted, but it probably gives you an idea of how to proceed. Here's a test that shows how it works.
https://github.com/xokomola/fold/blob/dbb79c60a7356e32a0994e581ad4f7f5377ddc72/test/fold-common/test-common.xqm#L166
--MarcQuoted text · Marc van Grootel · 21 lines
> On 15 jul. 2015, at 21:51, Marc van Grootel <marc.van.grootel at gmail.com> wrote: > > Hi Graydon, have a look here https://github.com/xokomola/fold/blob/dbb79c60a7356e32a0994e581ad4f7f5377ddc72/webapp/fold-common/common.xqm#L125 > > hope it helps > > --Marc > >> On 15 jul. 2015, at 20:01, Graydon Saunders <graydonish at gmail.com> wrote: >> >> Hi -- >> >> In BaseX 8.2.1, I have a bunch of maps; the maps may have some overlap of key values. I would like to merge/find the union of all of these maps while retaining all the values associated with each key in the resulting map-of-all-maps. (Combined map? Any word can be wrong, here! :) >> >> map:merge() doesn't do this; http://docs.basex.org/wiki/Map_Module#map:merge says >> "The associated value for each such key is taken from the last map in the input sequence $input that contains an entry with this key." and testing confirms this. >> >> Is there a straightforward way to do this that I'm missing? >> >> Thanks! >> Graydon
GS
Graydon Saunders15 Jul 2015 22:01 #
Hi --
In BaseX 8.2.1, I have a bunch of maps; the maps may have some overlap of key values. I would like to merge/find the union of all of these maps while retaining all the values associated with each key in the resulting map-of-all-maps. (Combined map? Any word can be wrong, here! :)
map:merge() doesn't do this;
http://docs.basex.org/wiki/Map_Module#map:merge says
"The associated value for each such key is taken from the last map in the input sequence $input that contains an entry with this key." and testing confirms this.
Is there a straightforward way to do this that I'm missing?
Thanks!
GraydonGS
Hi Marc --
That looks very helpful.
(Just as soon as I get over feeling like a cabbage for forgetting about fold-left()!)
thanks!
Graydon
Quoted text · Marc van Grootel · 30 lines
On Wed, Jul 15, 2015 at 3:51 PM, Marc van Grootel < marc.van.grootel at gmail.com> wrote: > Hi Graydon, have a look here > https://github.com/xokomola/fold/blob/dbb79c60a7356e32a0994e581ad4f7f5377ddc72/webapp/fold-common/common.xqm#L125 > > hope it helps > > --Marc > > On 15 jul. 2015, at 20:01, Graydon Saunders <graydonish at gmail.com> wrote: > > Hi -- > > In BaseX 8.2.1, I have a bunch of maps; the maps may have some overlap of > key values. I would like to merge/find the union of all of these maps > while retaining all the values associated with each key in the resulting > map-of-all-maps. (Combined map? Any word can be wrong, here! :) > > map:merge() doesn't do this; > http://docs.basex.org/wiki/Map_Module#map:merge says > "The associated value for each such key is taken from the last map in the > input sequence $input that contains an entry with this key." and testing > confirms this. > > Is there a straightforward way to do this that I'm missing? > > Thanks! > Graydon > >
RS
Quoted text · Rob Stapper · 45 lines
Van: Rob Stapper [mailto:r.stapper at lijbrandt.nl] Verzonden: donderdag 16 juli 2015 8:48 Aan: 'Graydon Saunders' Onderwerp: RE: [basex-talk] Union of maps? Hoi Graydon, Here’s one of my map-extensions. This one I use for merging maps. It’s also an example of how to create your own high-order-functions. Hopes is it helps. Cheers, Rob Stapper declare function ext.map:merge ( $sourceMap as map(*) , $targetMap as map(*) , $updateFunction as function(*) ) as map(*) { fold-left( map:keys( $sourceMap) , $targetMap , function( $targetMap , $key ) { map:put( $targetMap , $key , $updateFunction( map:get( $sourceMap, $key) , map:get( $targetMap, $key) ) ) } ) } ; Van: basex-talk-bounces at mailman.uni-konstanz.de [mailto:basex-talk-bounces at mailman.uni-konstanz.de] Namens Graydon Saunders Verzonden: woensdag 15 juli 2015 20:01 Aan: BaseX Onderwerp: [basex-talk] Union of maps? Hi -- In BaseX 8.2.1, I have a bunch of maps; the maps may have some overlap of key values. I would like to merge/find the union of all of these maps while retaining all the values associated with each key in the resulting map-of-all-maps. (Combined map? Any word can be wrong, here! :) map:merge() doesn't do this; http://docs.basex.org/wiki/Map_Module#map:merge says "The associated value for each such key is taken from the last map in the input sequence $input that contains an entry with this key." and testing confirms this. Is there a straightforward way to do this that I'm missing? Thanks! Graydon --- Dit e-mailbericht is gecontroleerd op virussen met Avast antivirussoftware. https://www.avast.com/antivirus
RS
Quoted text · Rob Stapper · 74 lines
Van: Rob Stapper [mailto:r.stapper at lijbrandt.nl] Verzonden: donderdag 16 juli 2015 9:08 Aan: 'Graydon Saunders' Onderwerp: RE: [basex-talk] Union of maps? Just as Marc: after looking at the code I found it needed tweeking ;-) Here’s a tweeked version. Succes, Rob declare function XQR.map:merge ( $sourceMap as map(*) , $targetMap as map(*) , $updateFunction as function(*) ) as map(*) { fold-left( map:keys( $sourceMap) , $targetMap , function( $targetMap , $key ) { let $go := function( $sourceEntry ) { item:exists( map:get( $targetMap , $key ) , function( $targetEntry ) { map:put( $targetMap , $key , $updateFunction( $sourceEntry , $targetEntry ) ) } , map:put( $targetMap , $key , $sourceEntry ) ) } return $go( map:get( $sourceMap , $key ) ) } ) } ; Plus bonus: declare %basex:inline function item:exists ( $item , $existProcessor , $emptyValue ) { if ( exists( $item)) then $existProcessor( $item) else $emptyValue } ; Van: basex-talk-bounces at mailman.uni-konstanz.de [mailto:basex-talk-bounces at mailman.uni-konstanz.de] Namens Graydon Saunders Verzonden: woensdag 15 juli 2015 20:01 Aan: BaseX Onderwerp: [basex-talk] Union of maps? Hi -- In BaseX 8.2.1, I have a bunch of maps; the maps may have some overlap of key values. I would like to merge/find the union of all of these maps while retaining all the values associated with each key in the resulting map-of-all-maps. (Combined map? Any word can be wrong, here! :) map:merge() doesn't do this; http://docs.basex.org/wiki/Map_Module#map:merge says "The associated value for each such key is taken from the last map in the input sequence $input that contains an entry with this key." and testing confirms this. Is there a straightforward way to do this that I'm missing? Thanks! Graydon --- Dit e-mailbericht is gecontroleerd op virussen met Avast antivirussoftware. https://www.avast.com/antivirus
RS
Or, retweeked and made for multiple maps (@ Marc thanx for the inspiration)
declare function XQR.map:merge ( $mapSet as map(*)*
, $mergeFunction as function(*)
) as map(*)
{ fold-left( $mapSet
, map {}
, function( $targetMap
, $sourceMap
)
{
map:merge(( $targetMap {
map:entry( $key , $mergeFunction(
map:get( $targetMap, $key)
, map:get( $sourceMap, $key)
)
)
}
)
))
}
)
} ;
Use:
xqr.map:merge( ( map { 'a' : ( 1,2,3,4)
, 'b' : ( 4,5,6,7)
}
, map { 'b' : ( 6,7,8,9)
, '7' : ( 'x', 'y', 'z')
}
, map { 'b' : ( 9,10,12)
, '7' : ( 'x', 'y', 'p')
}
)
, function( $data1
, $data2
) {
distinct-values(( $data1, $data2)) }
) ! map:serialize( .)
You’re welcome,
Rob
Quoted text · basex-talk-bounces at mailman.uni-konstanz.de · 14 lines
Van: basex-talk-bounces at mailman.uni-konstanz.de [mailto:basex-talk-bounces at mailman.uni-konstanz.de] Namens Graydon Saunders Verzonden: woensdag 15 juli 2015 20:01 Aan: BaseX Onderwerp: [basex-talk] Union of maps? Hi -- In BaseX 8.2.1, I have a bunch of maps; the maps may have some overlap of key values. I would like to merge/find the union of all of these maps while retaining all the values associated with each key in the resulting map-of-all-maps. (Combined map? Any word can be wrong, here! :) map:merge() doesn't do this; http://docs.basex.org/wiki/Map_Module#map:merge says "The associated value for each such key is taken from the last map in the input sequence $input that contains an entry with this key." and testing confirms this. Is there a straightforward way to do this that I'm missing? Thanks! Graydon --- Dit e-mailbericht is gecontroleerd op virussen met Avast antivirussoftware. https://www.avast.com/antivirus
CG
Hi Graydon,
here is one more aproach for (it's not so flexible as the functional approach, though):
declare function local:merge($maps as map(*)*) as map(*) {
map:merge(
for $key in distinct-values($maps ! map:keys(.))
return map { $key: ($maps ! .($key)) }
)
};
local:merge((
map { 'A': 1, 'B': 2 },
map { 'A': 3, 'C': 4 }
))Cheers,
Christian
Quoted text · Graydon Saunders · 18 lines
On Wed, Jul 15, 2015 at 8:01 PM, Graydon Saunders <graydonish at gmail.com> wrote: > Hi -- > > In BaseX 8.2.1, I have a bunch of maps; the maps may have some overlap of > key values. I would like to merge/find the union of all of these maps while > retaining all the values associated with each key in the resulting > map-of-all-maps. (Combined map? Any word can be wrong, here! :) > > map:merge() doesn't do this; > http://docs.basex.org/wiki/Map_Module#map:merge says > "The associated value for each such key is taken from the last map in the > input sequence $input that contains an entry with this key." and testing > confirms this. > > Is there a straightforward way to do this that I'm missing? > > Thanks! > Graydon
GS
Wow.
Thanks, Rob! That about solves the problem.
Quoted text · Rob Stapper · 159 lines
On Fri, Jul 17, 2015 at 4:01 AM, Rob Stapper <r.stapper at lijbrandt.nl> wrote: > Or, retweeked and made for multiple maps (@ Marc thanx for the inspiration) > > > > declare function XQR.map:merge > > ( $mapSet as map(*)* > > , $mergeFunction as function(*) > > ) as map(*) > > { > > fold-left( $mapSet > > , map {} > > , function( $targetMap > > , $sourceMap > > ) > > { > > map:merge(( $targetMap > > , $sourceMap > > , for-each( map:keys( $targetMap)[ > . = map:keys( $sourceMap)] > > , function( $key > > ) > > { > > map:entry( > $key > > , > $mergeFunction( map:get( $targetMap, $key) > > > , map:get( $sourceMap, $key) > > > ) > > ) > > } > > ) > > )) > > } > > ) > > } ; > > > > Use: > > > > xqr.map:merge( ( map { 'a' : ( 1,2,3,4) > > , 'b' : ( 4,5,6,7) > > } > > , map { 'b' : ( 6,7,8,9) > > , '7' : ( 'x', 'y', 'z') > > } > > , map { 'b' : ( 9,10,12) > > , '7' : ( 'x', 'y', 'p') > > } > > ) > > , function( $data1 > > , $data2 > > ) > > { > > distinct-values(( $data1, $data2)) > > } > > ) ! map:serialize( .) > > > > You’re welcome, > > Rob > > > > *Van:* basex-talk-bounces at mailman.uni-konstanz.de [mailto: > basex-talk-bounces at mailman.uni-konstanz.de] *Namens *Graydon Saunders > *Verzonden:* woensdag 15 juli 2015 20:01 > *Aan:* BaseX > *Onderwerp:* [basex-talk] Union of maps? > > > > Hi -- > > > > In BaseX 8.2.1, I have a bunch of maps; the maps may have some overlap of > key values. I would like to merge/find the union of all of these maps > while retaining all the values associated with each key in the resulting > map-of-all-maps. (Combined map? Any word can be wrong, here! :) > > > > map:merge() doesn't do this; > http://docs.basex.org/wiki/Map_Module#map:merge says > > "The associated value for each such key is taken from the last map in the > input sequence $input that contains an entry with this key." and testing > confirms this. > > > > Is there a straightforward way to do this that I'm missing? > > > > Thanks! > > Graydon > > > ------------------------------ > [image: Avast logo] <https://www.avast.com/antivirus> > > Dit e-mailbericht is gecontroleerd op virussen met Avast > antivirussoftware. > www.avast.com <https://www.avast.com/antivirus> > >
GS
Thank you!
Are these easy enough that a map:union() function operating on a sequence of maps isn't something to ask for in the next spec iteration?
-- Graydon
Quoted text · Christian Grün · 44 lines
On Fri, Jul 17, 2015 at 4:18 AM, Christian Grün <christian.gruen at gmail.com> wrote: > Hi Graydon, > > here is one more aproach for (it's not so flexible as the functional > approach, though): > > declare function local:merge($maps as map(*)*) as map(*) { > map:merge( > for $key in distinct-values($maps ! map:keys(.)) > return map { $key: ($maps ! .($key)) } > ) > }; > > local:merge(( > map { 'A': 1, 'B': 2 }, > map { 'A': 3, 'C': 4 } > )) > > Cheers, > Christian > > > On Wed, Jul 15, 2015 at 8:01 PM, Graydon Saunders <graydonish at gmail.com> > wrote: > > Hi -- > > > > In BaseX 8.2.1, I have a bunch of maps; the maps may have some overlap of > > key values. I would like to merge/find the union of all of these maps > while > > retaining all the values associated with each key in the resulting > > map-of-all-maps. (Combined map? Any word can be wrong, here! :) > > > > map:merge() doesn't do this; > > http://docs.basex.org/wiki/Map_Module#map:merge says > > "The associated value for each such key is taken from the last map in the > > input sequence $input that contains an entry with this key." and testing > > confirms this. > > > > Is there a straightforward way to do this that I'm missing? > > > > Thanks! > > Graydon >
CG
Christian Grün18 Jul 2015 14:19 #
Quoted text · Graydon Saunders · 2 lines
> Are these easy enough that a map:union() function operating on a sequence of > maps isn't something to ask for in the next spec iteration?
Maybe yes. Feel free to propose it on the W3 issue tracker [1]. The talk at x-query.com mailing list may be another place to discuss this.
Best,
Christian
[1] https://www.w3.org/Bugs/Public/describecomponents.cgi?product=XPath%20%2F%20XQuery%20%2F%20XSLT
Quoted text · Christian Grün · 48 lines
> On Fri, Jul 17, 2015 at 4:18 AM, Christian Grün <christian.gruen at gmail.com> > wrote: >> >> Hi Graydon, >> >> here is one more aproach for (it's not so flexible as the functional >> approach, though): >> >> declare function local:merge($maps as map(*)*) as map(*) { >> map:merge( >> for $key in distinct-values($maps ! map:keys(.)) >> return map { $key: ($maps ! .($key)) } >> ) >> }; >> >> local:merge(( >> map { 'A': 1, 'B': 2 }, >> map { 'A': 3, 'C': 4 } >> )) >> >> Cheers, >> Christian >> >> >> On Wed, Jul 15, 2015 at 8:01 PM, Graydon Saunders <graydonish at gmail.com> >> wrote: >> > Hi -- >> > >> > In BaseX 8.2.1, I have a bunch of maps; the maps may have some overlap >> > of >> > key values. I would like to merge/find the union of all of these maps >> > while >> > retaining all the values associated with each key in the resulting >> > map-of-all-maps. (Combined map? Any word can be wrong, here! :) >> > >> > map:merge() doesn't do this; >> > http://docs.basex.org/wiki/Map_Module#map:merge says >> > "The associated value for each such key is taken from the last map in >> > the >> > input sequence $input that contains an entry with this key." and testing >> > confirms this. >> > >> > Is there a straightforward way to do this that I'm missing? >> > >> > Thanks! >> > Graydon > >
CG
Hi Graydon,
The solutions look similar indeed. Here is an more verbose writing, I hope it's helpful?
map:merge(
(: select distinct keys :)
for $key in distinct-values(
for $map in $maps return map:keys($map)
)
(: create new map entry with all values :)
return map {
$key :
for $map in $maps return $map($key)
}
)I recommend you to have a closer look at our Wiki article on XQuery 3.1 [1] and, possibly, XQuery 3.0 [2]. We tried to summarize the new language features in a simple and concise manner.
Cheers,
Christian
[1] http://docs.basex.org/wiki/XQuery_3.1
[2] http://docs.basex.org/wiki/XQuery_3.0
Quoted text · Graydon Saunders · 89 lines
> return map:merge( > for $k in distinct-values($maps!map:keys(.)) > return map:entry($k, $maps?($k)) > ) > > Your version looks like: > map:merge( > for $key in distinct-values($maps ! map:keys(.)) > return map { $key: ($maps ! .($key)) } > ) > > I feel like I ought to update the docs with one or both but I don't > understand the syntax in the return statement of either! > > Any chance of getting either or both expanded for the easily-bewildered? > > On Sat, Jul 18, 2015 at 8:19 AM, Christian Grün <christian.gruen at gmail.com> > wrote: >> >> > Are these easy enough that a map:union() function operating on a >> > sequence of >> > maps isn't something to ask for in the next spec iteration? >> >> Maybe yes. Feel free to propose it on the W3 issue tracker [1]. The >> talk at x-query.com mailing list may be another place to discuss this. >> >> Best, >> Christian >> >> [1] >> https://www.w3.org/Bugs/Public/describecomponents.cgi?product=XPath%20%2F%20XQuery%20%2F%20XSLT >> >> >> >> > On Fri, Jul 17, 2015 at 4:18 AM, Christian Grün >> > <christian.gruen at gmail.com> >> > wrote: >> >> >> >> Hi Graydon, >> >> >> >> here is one more aproach for (it's not so flexible as the functional >> >> approach, though): >> >> >> >> declare function local:merge($maps as map(*)*) as map(*) { >> >> map:merge( >> >> for $key in distinct-values($maps ! map:keys(.)) >> >> return map { $key: ($maps ! .($key)) } >> >> ) >> >> }; >> >> >> >> local:merge(( >> >> map { 'A': 1, 'B': 2 }, >> >> map { 'A': 3, 'C': 4 } >> >> )) >> >> >> >> Cheers, >> >> Christian >> >> >> >> >> >> On Wed, Jul 15, 2015 at 8:01 PM, Graydon Saunders >> >> <graydonish at gmail.com> >> >> wrote: >> >> > Hi -- >> >> > >> >> > In BaseX 8.2.1, I have a bunch of maps; the maps may have some >> >> > overlap >> >> > of >> >> > key values. I would like to merge/find the union of all of these >> >> > maps >> >> > while >> >> > retaining all the values associated with each key in the resulting >> >> > map-of-all-maps. (Combined map? Any word can be wrong, here! :) >> >> > >> >> > map:merge() doesn't do this; >> >> > http://docs.basex.org/wiki/Map_Module#map:merge says >> >> > "The associated value for each such key is taken from the last map in >> >> > the >> >> > input sequence $input that contains an entry with this key." and >> >> > testing >> >> > confirms this. >> >> > >> >> > Is there a straightforward way to do this that I'm missing? >> >> > >> >> > Thanks! >> >> > Graydon >> > >> > > >
GS
talk at x-query.com says "too late for 3.1 (for a two-liner)" which is fair enough.
The Michael Kay version of the 2-liner looks like:
return map:merge(
for $k in distinct-values($maps!map:keys(.))
return map:entry($k, $maps?($k))
)Your version looks like:
map:merge(
for $key in distinct-values($maps ! map:keys(.))
return map { $key: ($maps ! .($key)) }
)I feel like I ought to update the docs with one or both but I don't understand the syntax in the return statement of either!
Any chance of getting either or both expanded for the easily-bewildered?
Quoted text · Christian Grün · 69 lines
On Sat, Jul 18, 2015 at 8:19 AM, Christian Grün <christian.gruen at gmail.com> wrote: > > Are these easy enough that a map:union() function operating on a > sequence of > > maps isn't something to ask for in the next spec iteration? > > Maybe yes. Feel free to propose it on the W3 issue tracker [1]. The > talk at x-query.com mailing list may be another place to discuss this. > > Best, > Christian > > [1] > https://www.w3.org/Bugs/Public/describecomponents.cgi?product=XPath%20%2F%20XQuery%20%2F%20XSLT > > > > > On Fri, Jul 17, 2015 at 4:18 AM, Christian Grün < > christian.gruen at gmail.com> > > wrote: > >> > >> Hi Graydon, > >> > >> here is one more aproach for (it's not so flexible as the functional > >> approach, though): > >> > >> declare function local:merge($maps as map(*)*) as map(*) { > >> map:merge( > >> for $key in distinct-values($maps ! map:keys(.)) > >> return map { $key: ($maps ! .($key)) } > >> ) > >> }; > >> > >> local:merge(( > >> map { 'A': 1, 'B': 2 }, > >> map { 'A': 3, 'C': 4 } > >> )) > >> > >> Cheers, > >> Christian > >> > >> > >> On Wed, Jul 15, 2015 at 8:01 PM, Graydon Saunders <graydonish at gmail.com > > > >> wrote: > >> > Hi -- > >> > > >> > In BaseX 8.2.1, I have a bunch of maps; the maps may have some overlap > >> > of > >> > key values. I would like to merge/find the union of all of these maps > >> > while > >> > retaining all the values associated with each key in the resulting > >> > map-of-all-maps. (Combined map? Any word can be wrong, here! :) > >> > > >> > map:merge() doesn't do this; > >> > http://docs.basex.org/wiki/Map_Module#map:merge says > >> > "The associated value for each such key is taken from the last map in > >> > the > >> > input sequence $input that contains an entry with this key." and > testing > >> > confirms this. > >> > > >> > Is there a straightforward way to do this that I'm missing? > >> > > >> > Thanks! > >> > Graydon > > > > >
Written for BaseX 8.2. BaseX may have changed since: please check the current documentation.
⚡Generated with XQuery