Discussion:
[xquery-talk] Manipulating Maps In Non-Trivial Ways
Eliot Kimber
2015-07-19 15:40:22 UTC
Permalink
I don't know why I'm having such a hard time figuring out how to operate
on maps but I just can't get my head around it (I suspect procedural brain
damage but it could just be advancing age). I suspect others will or are
having the same challenge.

My challenge of the day is:

I have an existing map where each entry is a sequence mapped to a string
key, e.g.:

let $m1 := map{ 'key1' : (), 'key2' : () }

I then have a node and a set of key values. For each key I need to find
the entry with that key and add the node to the entry, e.g.

let $newKeys := ('key1', 'key3')
let $obj := <added/>

Let $m2 := for $key in $newKeys
(: Find sequence for key,
Add obj to sequence,
Update map entry for new key
:)

I think the right answer is to use map:merge like so:

let $m2 :=
map:merge(
$m1,
lor $key in $newKeys
Map {$key, ($m1($key), $obj))}
)

Is my analysis correct? Is there a better way to do this sort of map
updating?

Thanks,

Eliot
----
Eliot Kimber, Owner
Contrext, LLC
http://contrext.com



_______________________________________________
***@x-query.com
http://x-query.com/mailman/listinfo/talk
Eliot Kimber
2015-07-19 16:08:16 UTC
Permalink
Just realized that map:merge() takes a *sequence* of maps, so this is
wrong:

map:merge($m1, $m2)

But this is correct:

map:merge(($m1, $m2))

That clear in the spec examples (and of course in the function signature)
but it still surprised me because I wasn't paying close attention and
assumed the signature was (map1, map2). So my examples below need an
addition set of parens around the values with map:merge() calls.

Cheers,

E.

----
Eliot Kimber, Owner
Contrext, LLC
http://contrext.com
Post by Eliot Kimber
I don't know why I'm having such a hard time figuring out how to operate
on maps but I just can't get my head around it (I suspect procedural brain
damage but it could just be advancing age). I suspect others will or are
having the same challenge.
I have an existing map where each entry is a sequence mapped to a string
let $m1 := map{ 'key1' : (), 'key2' : () }
I then have a node and a set of key values. For each key I need to find
the entry with that key and add the node to the entry, e.g.
let $newKeys := ('key1', 'key3')
let $obj := <added/>
Let $m2 := for $key in $newKeys
(: Find sequence for key,
Add obj to sequence,
Update map entry for new key
:)
let $m2 :=
map:merge(
$m1,
lor $key in $newKeys
Map {$key, ($m1($key), $obj))}
)
Is my analysis correct? Is there a better way to do this sort of map
updating?
Thanks,
Eliot
----
Eliot Kimber, Owner
Contrext, LLC
http://contrext.com
_______________________________________________
http://x-query.com/mailman/listinfo/talk
_______________________________________________
***@x-query.com
http://x-query.com/mailman/listinfo/talk
Michael Kay
2015-07-19 17:38:05 UTC
Permalink
A fold can sometimes be useful for this kind of operation:

$newKeys => fold-left($m1, function($key, $map) { $map => map:put($key, ($map($key), $obj) })

Also, appending an item to the value for a key is a sufficiently common operation it’s worth having a function for it:

declare function f:map-add($map, $key, $value) as map(*) {
map:put($map, $key, ($map($key), $value))
}

and then the fold becomes

$newKeys => fold-left($m1, function($key, $map) {$map => f:add($key, $obj)})

But it takes a while before folds become intuitive. map:merge works equally well.

Michael Kay
Saxonica
Post by Eliot Kimber
I don't know why I'm having such a hard time figuring out how to operate
on maps but I just can't get my head around it (I suspect procedural brain
damage but it could just be advancing age). I suspect others will or are
having the same challenge.
I have an existing map where each entry is a sequence mapped to a string
let $m1 := map{ 'key1' : (), 'key2' : () }
I then have a node and a set of key values. For each key I need to find
the entry with that key and add the node to the entry, e.g.
let $newKeys := ('key1', 'key3')
let $obj := <added/>
Let $m2 := for $key in $newKeys
(: Find sequence for key,
Add obj to sequence,
Update map entry for new key
:)
let $m2 :=
map:merge(
$m1,
lor $key in $newKeys
Map {$key, ($m1($key), $obj))}
)
Is my analysis correct? Is there a better way to do this sort of map
updating?
Thanks,
Eliot
----
Eliot Kimber, Owner
Contrext, LLC
http://contrext.com
_______________________________________________
http://x-query.com/mailman/listinfo/talk
_______________________________________________
***@x-query.com
http://x-query.com/mailman/listi
Eliot Kimber
2015-07-19 21:55:28 UTC
Permalink
Yes, the fold operations are still a bit too clever for my taste--I prefer
to make my business a logic a little more verbose to clearer, at least to
me--I don't want to have to think too deeply when I'm programming :-)

Cheers,

E.
----
Eliot Kimber, Owner
Contrext, LLC
http://contrext.com
Post by Michael Kay
$newKeys => fold-left($m1, function($key, $map) { $map => map:put($key, ($map($key), $obj) })
Also, appending an item to the value for a key is a sufficiently common
declare function f:map-add($map, $key, $value) as map(*) {
map:put($map, $key, ($map($key), $value))
}
and then the fold becomes
$newKeys => fold-left($m1, function($key, $map) {$map => f:add($key, $obj)})
But it takes a while before folds become intuitive. map:merge works equally well.
Michael Kay
Saxonica
Post by Eliot Kimber
I don't know why I'm having such a hard time figuring out how to operate
on maps but I just can't get my head around it (I suspect procedural brain
damage but it could just be advancing age). I suspect others will or are
having the same challenge.
I have an existing map where each entry is a sequence mapped to a string
let $m1 := map{ 'key1' : (), 'key2' : () }
I then have a node and a set of key values. For each key I need to find
the entry with that key and add the node to the entry, e.g.
let $newKeys := ('key1', 'key3')
let $obj := <added/>
Let $m2 := for $key in $newKeys
(: Find sequence for key,
Add obj to sequence,
Update map entry for new key
:)
let $m2 :=
map:merge(
$m1,
lor $key in $newKeys
Map {$key, ($m1($key), $obj))}
)
Is my analysis correct? Is there a better way to do this sort of map
updating?
Thanks,
Eliot
----
Eliot Kimber, Owner
Contrext, LLC
http://contrext.com
_______________________________________________
http://x-query.com/mailman/listinfo/talk
_______________________________________________
***@x-query.com
http://x-query.com/mailman/listinfo/talk
Michael Kay
2015-07-20 18:35:07 UTC
Permalink
Post by Eliot Kimber
Yes, the fold operations are still a bit too clever for my taste--I prefer
to make my business a logic a little more verbose to clearer, at least to
me--I don't want to have to think too deeply when I'm programming :-)
$newKeys => fold-left($m1, function($key, $map) { $map => map:put($key,
($map($key), $obj) })
becomes

<xsl:iterate select=“$newKeys”>
<xsl:param name=“map” select=“$m1”/>
<xsl:on-completion select=“$map”/>
<xsl:next-iteration>
<xsl:with-param name=“map” select=“map:put($map, ., ($map(.), $obj))”/>
</xsl:next-iteration>
</xsl:iterate>

Michael Kay
Saxonica


_______________________________________________
***@x-query.com
http://x-query.com/mailman/listinfo/talk
Eliot Kimber
2015-07-20 18:44:21 UTC
Permalink
Hmm, I'll have to think about that one.

In the context of XQuery I don't see how it's different from a FOR
expression inside map:merge(). In XSLT I can see the value because you
might be applying templates or something else you can't do directly in an
XPath expression.

Cheers,

Eliot
----
Eliot Kimber, Owner
Contrext, LLC
http://contrext.com
Post by Eliot Kimber
Yes, the fold operations are still a bit too clever for my taste--I prefer
to make my business a logic a little more verbose to clearer, at least to
me--I don't want to have to think too deeply when I'm programming :-)
Although I say it myself, I think the xsl:iterate instruction in XSLT
3.0, which is essentially syntactic sugar over a fold operation,
Post by Eliot Kimber
$newKeys => fold-left($m1, function($key, $map) { $map => map:put($key,
($map($key), $obj) })
becomes
<xsl:iterate select=³$newKeys²>
<xsl:param name=³map² select=³$m1²/>
<xsl:on-completion select=³$map²/>
<xsl:next-iteration>
<xsl:with-param name=³map² select=³map:put($map, ., ($map(.),
$obj))²/>
</xsl:next-iteration>
</xsl:iterate>
Michael Kay
Saxonica
_______________________________________________
***@x-query.com
http://x-query.com/mailman/listinfo/talk

Continue reading on narkive:
Loading...