Union of maps?

Wednesday, 15 July 2015 · 13 messages · last reply 20 Jul 2015
MG
Marc van Grootelin reply to Graydon Saunders, 15 Jul 2015, 22:0115 Jul 2015 21:51 #
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 --Marc
Quoted 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! Graydon
GS
Graydon Saundersin reply to Marc van Grootel, 15 Jul 2015, 21:5116 Jul 2015 06:04 #
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
Rob Stapperin reply to Graydon Saunders, 15 Jul 2015, 22:0116 Jul 2015 09:11 #
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
Rob Stapperin reply to Graydon Saunders, 15 Jul 2015, 22:0116 Jul 2015 09:11 #
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
Rob Stapperin reply to Graydon Saunders, 15 Jul 2015, 22:0117 Jul 2015 10:01 #
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
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
Christian Grünin reply to Graydon Saunders, 15 Jul 2015, 22:0117 Jul 2015 10:18 #
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
Graydon Saundersin reply to Rob Stapper, 17 Jul 2015, 10:0118 Jul 2015 08:49 #
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
Graydon Saundersin reply to Christian Grün, 17 Jul 2015, 10:1818 Jul 2015 08:50 #
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
Christian Grünin reply to Graydon Saunders, 20 Jul 2015, 17:5920 Jul 2015 17:50 #
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
Graydon Saundersin reply to Christian Grün, 18 Jul 2015, 14:1920 Jul 2015 17:59 #
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 > > > > >

⚡Generated with XQuery