Discussion:
[groovy-user] List or regex in a map?
marc fawzi
2010-04-12 10:23:49 UTC
Permalink
(new to groovy)

Can I have a list/range, e.g. [a..z], or some regex pattern inside a map to
define the keys so that I don't have to spell them out when initializing?

Example: how do I replace XXXX with [a..z]

def someMap = [
XXXX:0,
1:0,
2:0,
3:0,
]

This way I don't have to specify the keys "a" thru "z" by hand

How do I initialize multiple keys to one value inside a map using a list of
keys?

Also, how do I initialize multiple keys to one value inside a map using a
regex pattern (for the keys)?

Thanks!

Marc
Tim Yates
2010-04-12 10:29:55 UTC
Permalink
For the first bit, you can do something like this:

[ 'a'..'z', 1, 2, 3 ].flatten().inject( [:] ) { map, val ->
map[ val ] = 0
map
}

I'll have a think about the second bit now...

Tim
Post by marc fawzi
(new to groovy)
Can I have a list/range, e.g. [a..z], or some regex pattern inside a map to
define the keys so that I don't have to spell them out when initializing?
Example: how do I replace XXXX with [a..z]
def someMap = [
XXXX:0,
1:0,
2:0,
3:0,
]
This way I don't have to specify the keys "a" thru "z" by hand
How do I initialize multiple keys to one value inside a map using a list of
keys?
Also, how do I initialize multiple keys to one value inside a map using a
regex pattern (for the keys)?
Thanks!
Marc
Tim Yates
2010-04-12 10:37:17 UTC
Permalink
Does this fit the bill?

There may be a better way of doing it...

def map = [ 'a'..'z', 1, 2, 3 ].flatten().inject( [:] ) { map, val ->
map[ val ] = 0
map
}

// This will do the replacement. It alters the original map
def replaceByRegexp( Map map, regexp, value ) {
map.each { k, v ->
// If the key matches the regexp, then set the map entry to our value
if( k ==~ regexp ) map[ k ] = value
}
}

// Replace keys a, c anf f
replaceByRegexp( map, /[acf]/, 20 )

Tim
Post by marc fawzi
(new to groovy)
Can I have a list/range, e.g. [a..z], or some regex pattern inside a map to
define the keys so that I don't have to spell them out when initializing?
Example: how do I replace XXXX with [a..z]
def someMap = [
XXXX:0,
1:0,
2:0,
3:0,
]
This way I don't have to specify the keys "a" thru "z" by hand
How do I initialize multiple keys to one value inside a map using a list of
keys?
Also, how do I initialize multiple keys to one value inside a map using a
regex pattern (for the keys)?
Thanks!
Marc
John Hartnup
2010-04-12 10:47:35 UTC
Permalink
Post by Tim Yates
def map = [ 'a'..'z', 1, 2, 3 ].flatten().inject( [:] ) { map, val ->
  map[ val ] = 0
  map
}
Don't forget the '*' notation for appending lists -- it's more
succinct than flatten(), as long as the reader knows about it.

[ 'a'..'z', 1, 2, 3 ].flatten()
vs
[ * 'a'..'z', 1, 2, 3 ]
--
"There is no way to peace; peace is the way"

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Tim Yates
2010-04-12 10:49:50 UTC
Permalink
Good call

I had forgotten about that operator :-/

Both it and the spread map operator seem to prefer living in the outskirts
of my memory ;-)

Tim
Post by John Hartnup
Post by Tim Yates
def map = [ 'a'..'z', 1, 2, 3 ].flatten().inject( [:] ) { map, val ->
map[ val ] = 0
map
}
Don't forget the '*' notation for appending lists -- it's more
succinct than flatten(), as long as the reader knows about it.
[ 'a'..'z', 1, 2, 3 ].flatten()
vs
[ * 'a'..'z', 1, 2, 3 ]
--
"There is no way to peace; peace is the way"
---------------------------------------------------------------------
http://xircles.codehaus.org/manage_email
marc fawzi
2010-04-12 16:08:00 UTC
Permalink
Wow!

This Groovy thing totally blows me away.

I'm just kicking the tires (practical stuff)

I'll post the final code here to provide context once I get it working!

Thanks !! :)
Post by Tim Yates
Good call
I had forgotten about that operator :-/
Both it and the spread map operator seem to prefer living in the outskirts
of my memory ;-)
Tim
Post by John Hartnup
Post by Tim Yates
def map = [ 'a'..'z', 1, 2, 3 ].flatten().inject( [:] ) { map, val ->
map[ val ] = 0
map
}
Don't forget the '*' notation for appending lists -- it's more
succinct than flatten(), as long as the reader knows about it.
[ 'a'..'z', 1, 2, 3 ].flatten()
vs
[ * 'a'..'z', 1, 2, 3 ]
--
"There is no way to peace; peace is the way"
---------------------------------------------------------------------
http://xircles.codehaus.org/manage_email
marc fawzi
2010-04-12 23:10:50 UTC
Permalink
Hey Tim,

This is really nice.

What I meant for the second part of my question:

Let's say that I want to define a map that has regex pattern as a key
initialized to 0, e.g.

def map = [ /pattern/ : '0' ]

Obviously, if the pattern matches any string (e.g: /.*/ ) then the map
can't be created because it would consume the spacetime continuum :) but I
guess my question was exactly about that, although I did found your
ReplaceByRegexp function is quite useful in the context of what I'm trying
to do)

Now, if it is possible to pre-define a map and not have it be created per se
(i.e. just a place holder that says this 'map' contains a regex pattern as
key so don't create it just yet) and then have the map be _created and
appended to or modified as you specify a literal key to update_ e.g.
"this_is_a_key_that_matches_the_regex_pattern" and I like to update its'
value from 0 to X. So as I go on specifying literal keys to update within
that pre-defined map the map is actually created (on first such update) and
appended to or modified on every following update.

I guess what I'm looking for may not be a map. I just need a key-value data
structure that I can pre-define using regex keys (set to e.g. 0) and then
create and update that key-value structure by specifying the literal key. So
for a key that is pre-defined as /[regex pattern for banana or bananas]/ I
want to be able to update the key "banana". In my case, the key-value data
structure would be more like a template where keys are not defined until
they're explicitly specified during being updated.

How do I go about doing this?

Thanks in advance for any tips or for helping me think about it differently.
:)

Being a n00b is a full time job.

Marc
Post by Tim Yates
Does this fit the bill?
There may be a better way of doing it...
def map = [ 'a'..'z', 1, 2, 3 ].flatten().inject( [:] ) { map, val ->
map[ val ] = 0
map
}
// This will do the replacement. It alters the original map
def replaceByRegexp( Map map, regexp, value ) {
map.each { k, v ->
// If the key matches the regexp, then set the map entry to our value
if( k ==~ regexp ) map[ k ] = value
}
}
// Replace keys a, c anf f
replaceByRegexp( map, /[acf]/, 20 )
Tim
Post by marc fawzi
(new to groovy)
Can I have a list/range, e.g. [a..z], or some regex pattern inside a map
to define the keys so that I don't have to spell them out when initializing?
Example: how do I replace XXXX with [a..z]
def someMap = [
XXXX:0,
1:0,
2:0,
3:0,
]
This way I don't have to specify the keys "a" thru "z" by hand
How do I initialize multiple keys to one value inside a map using a list
of keys?
Also, how do I initialize multiple keys to one value inside a map using a
regex pattern (for the keys)?
Thanks!
Marc
Tim Yates
2010-04-13 07:54:46 UTC
Permalink
Hi Marc,

You can use regular expressions as keys in a map like this;

class RegexpMap {
@Delegate Map map

def getAt( String key ) {
map.find { a ->
a.key.matcher( key ).matches()
}.value
}

def putAt( String key, Object value ) {
map.find { a ->
a.key.matcher( key ).matches()
}.value = value
}
}

def map = new RegexpMap( map:[ (~/[a-z]+/):0, (~/[0-9]+/):10, ] )

// Check that alpha keys are 0
assert map[ 'a' ] == 0
assert map[ 'b' ] == 0
// And numeric keys are 10
assert map[ '1' ] == 10
// Set 'a' to 20
map[ 'a' ] = 20
// Check 'a' is 20
assert map[ 'a' ] == 20
// But as you can see, all alpha values are now 20
assert map[ 'b' ] == 20

but this isn't really what you wanted. I don't believe it's possible to
unroll a regexp to get all possible combinations that would pass it (and if
there were, as you say, it could end up consuming all of space-time ;-)

Maybe using something like the class I posted above, but just using a getAt
override to return a default value if the property had never been set?

Something like:

import java.util.regex.Pattern

class FunkyMap {
Pattern initialPattern
def initialValue
@Delegate Map map = [:]

def getAt( String key ) {
map[ key ] ?: ( initialPattern.matcher( key ).matches() ? initialValue :
null )
}
}

def map = new FunkyMap( initialValue:10, initialPattern:(~/[a-z]+/) )
assert map[ 1 ] == null
assert map[ 'a' ] == 10

So that creates a class with a regexp and a default value. If the key is
null, and matches the regexp, then the default value is returned

Hope this helps...

Tim
Post by marc fawzi
Hey Tim,
This is really nice.
Let's say that I want to define a map that has regex pattern as a key
initialized to 0, e.g.
def map = [ /pattern/ : '0' ]
Obviously, if the pattern matches any string (e.g: /.*/ ) then the map
can't be created because it would consume the spacetime continuum :) but I
guess my question was exactly about that, although I did found your
ReplaceByRegexp function is quite useful in the context of what I'm trying
to do)
Now, if it is possible to pre-define a map and not have it be created per
se (i.e. just a place holder that says this 'map' contains a regex pattern
as key so don't create it just yet) and then have the map be _created and
appended to or modified as you specify a literal key to update_ e.g.
"this_is_a_key_that_matches_the_regex_pattern" and I like to update its'
value from 0 to X. So as I go on specifying literal keys to update within
that pre-defined map the map is actually created (on first such update) and
appended to or modified on every following update.
I guess what I'm looking for may not be a map. I just need a key-value data
structure that I can pre-define using regex keys (set to e.g. 0) and then
create and update that key-value structure by specifying the literal key. So
for a key that is pre-defined as /[regex pattern for banana or bananas]/ I
want to be able to update the key "banana". In my case, the key-value data
structure would be more like a template where keys are not defined until
they're explicitly specified during being updated.
How do I go about doing this?
Thanks in advance for any tips or for helping me think about it
differently. :)
Being a n00b is a full time job.
Marc
Post by Tim Yates
Does this fit the bill?
There may be a better way of doing it...
def map = [ 'a'..'z', 1, 2, 3 ].flatten().inject( [:] ) { map, val ->
map[ val ] = 0
map
}
// This will do the replacement. It alters the original map
def replaceByRegexp( Map map, regexp, value ) {
map.each { k, v ->
// If the key matches the regexp, then set the map entry to our value
if( k ==~ regexp ) map[ k ] = value
}
}
// Replace keys a, c anf f
replaceByRegexp( map, /[acf]/, 20 )
Tim
Post by marc fawzi
(new to groovy)
Can I have a list/range, e.g. [a..z], or some regex pattern inside a map
to define the keys so that I don't have to spell them out when initializing?
Example: how do I replace XXXX with [a..z]
def someMap = [
XXXX:0,
1:0,
2:0,
3:0,
]
This way I don't have to specify the keys "a" thru "z" by hand
How do I initialize multiple keys to one value inside a map using a list
of keys?
Also, how do I initialize multiple keys to one value inside a map using a
regex pattern (for the keys)?
Thanks!
Marc
John Hartnup
2010-04-13 07:58:42 UTC
Permalink
You could write your own class that implements the Map interface,
where get() does a regex match using the various patterns you've
stored.

One issue with this is that you'd probably not be able to implement a
comprehensive keySet() method.
--
"There is no way to peace; peace is the way"

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Tim Yates
2010-04-13 09:54:09 UTC
Permalink
Actually, I just found a class in Groovy that does what I did but much
cleaner:

// Ok, these are our defaults
def defaults = [
// for single lower case letter keys, return 'a default'
(~/[a-z]/):{ 'a default' },
// for single numeric (string) keys, convert to an integer, and add 10
(~/[0-9]/):{ ( it as Integer ) + 10 }
]

// And here is our closure that is run when a key is missing
def defaultClosure = { key ->
// Find a key for which the regexp matches the key
// And if one exists, execute the closure value passing
// our key as a parameter (returns null if no match is found)
defaults.find { it.key.matcher( key ).matches() }?.value( key )
}

// Create our MapWithDefaults as an empty map and our
// closure for when values are missing
def m = new MapWithDefault( [:], defaultClosure )

// do some testing
assert m[ '1' ] == 11
assert m[ 'a' ] == 'a default'
assert m.a == 'a default'
assert m.missing == null

// Set a value
m[ 'a' ] = 'wheee'

// and check it was set
assert m[ 'a' ] == 'wheee'
assert m.a == 'wheee'

I *believe* this is only since Groovy 1.7.2 as it fails on the web console,
but there's no @Since field in the source code for the MapWithDefault class,
so I can't be sure

Pretty nifty though!

Tim
Post by John Hartnup
You could write your own class that implements the Map interface,
where get() does a regex match using the various patterns you've
stored.
One issue with this is that you'd probably not be able to implement a
comprehensive keySet() method.
--
"There is no way to peace; peace is the way"
---------------------------------------------------------------------
http://xircles.codehaus.org/manage_email
John Hartnup
2010-04-13 10:04:28 UTC
Permalink
Post by Tim Yates
Actually, I just found a class in Groovy that does what I did but much
Oh, that *is* nifty.

I guess there are caveats to using getKeys() (or keySet() or each()
etc.) - where is this documented? Google can't find MapWithDefaults
--
"There is no way to peace; peace is the way"

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Tim Yates
2010-04-13 10:09:11 UTC
Permalink
The only thing I found about it was the javadoc page

http://groovy.codehaus.org/gapi/groovy/lang/MapWithDefault.html

And then I got the source from SVN to see how it was used

Never heard of it before seeing it in the docs, and as you say can't find
anything else out about it from any other source
Post by John Hartnup
Post by Tim Yates
Actually, I just found a class in Groovy that does what I did but much
Oh, that *is* nifty.
I guess there are caveats to using getKeys() (or keySet() or each()
etc.) - where is this documented? Google can't find MapWithDefaults
--
"There is no way to peace; peace is the way"
---------------------------------------------------------------------
http://xircles.codehaus.org/manage_email
Guillaume Laforge
2010-04-13 10:08:32 UTC
Permalink
You don't need to create the map that way, you can write:

def m = [:].withDefault { key -> key * 2 }
Post by Tim Yates
Actually, I just found a class in Groovy that does what I did but much
// Ok, these are our defaults
def defaults = [
  // for single lower case letter keys, return 'a default'
  (~/[a-z]/):{ 'a default' },
  // for single numeric (string) keys, convert to an integer, and add 10
  (~/[0-9]/):{ ( it as Integer ) + 10 }
]
// And here is our closure that is run when a key is missing
def defaultClosure = { key ->
  // Find a key for which the regexp matches the key
  // And if one exists, execute the closure value passing
  // our key as a parameter (returns null if no match is found)
  defaults.find { it.key.matcher( key ).matches() }?.value( key )
}
// Create our MapWithDefaults as an empty map and our
// closure for when values are missing
def m = new MapWithDefault( [:], defaultClosure )
// do some testing
assert m[ '1' ]  == 11
assert m[ 'a' ]  == 'a default'
assert m.a       == 'a default'
assert m.missing == null
// Set a value
m[ 'a' ] = 'wheee'
// and check it was set
assert m[ 'a' ] == 'wheee'
assert m.a      == 'wheee'
I *believe* this is only since Groovy 1.7.2 as it fails on the web console,
so I can't be sure
Pretty nifty though!
Tim
Post by John Hartnup
You could write your own class that implements the Map interface,
where get() does a regex match using the various patterns you've
stored.
One issue with this is that you'd probably not be able to implement a
comprehensive keySet() method.
--
"There is no way to peace; peace is the way"
---------------------------------------------------------------------
   http://xircles.codehaus.org/manage_email
--
Guillaume Laforge
Groovy Project Manager
Head of Groovy Development at SpringSource
http://www.springsource.com/g2one

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Tim Yates
2010-04-13 10:10:22 UTC
Permalink
Cool!

Is it new to 1.7.2?

On Tue, Apr 13, 2010 at 11:08 AM, Guillaume Laforge
Post by Guillaume Laforge
def m = [:].withDefault { key -> key * 2 }
Guillaume Laforge
2010-04-13 10:12:44 UTC
Permalink
Yup.
It was in the JIRA release notes, but I guess I should have
highlighted some of these novelties, like Hamlet did in his blog post:
http://hamletdarcy.blogspot.com/2010/04/groovy-172-three-features-worth-upgrade.html
Post by Tim Yates
Cool!
Is it new to 1.7.2?
Post by Guillaume Laforge
def m = [:].withDefault { key -> key * 2 }
--
Guillaume Laforge
Groovy Project Manager
Head of Groovy Development at SpringSource
http://www.springsource.com/g2one

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Tim Yates
2010-04-13 10:16:03 UTC
Permalink
That will teach me to skim-read the JIRA release notes ;-)

I'll pay more attention next time ;-)

On Tue, Apr 13, 2010 at 11:12 AM, Guillaume Laforge
Post by Guillaume Laforge
Yup.
It was in the JIRA release notes, but I guess I should have
http://hamletdarcy.blogspot.com/2010/04/groovy-172-three-features-worth-upgrade.html
Guillaume Laforge
2010-04-13 10:22:29 UTC
Permalink
I usually highlight key / important features, not necessarily such
more minor features and little improvements, but I guess I should, so
that everybody can really see the nice new things even a maintenance
release can bring!
Post by Tim Yates
That will teach me to skim-read the JIRA release notes ;-)
I'll pay more attention next time ;-)
Post by Guillaume Laforge
Yup.
It was in the JIRA release notes, but I guess I should have
http://hamletdarcy.blogspot.com/2010/04/groovy-172-three-features-worth-upgrade.html
--
Guillaume Laforge
Groovy Project Manager
Head of Groovy Development at SpringSource
http://www.springsource.com/g2one

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Tim Yates
2010-04-13 10:15:03 UTC
Permalink
So Marc, that whole top section (down to the testing comment) can be
replaced with:

def defaults = [
(~/[a-z]/):{ 'a default' },
(~/[0-9]/):{ ( it as Integer ) + 10 }
]
def m = [:].withDefault { key ->
defaults.find { it.key.matcher( key ).matches() }?.value( key )
}

Tim
Post by Tim Yates
Cool!
Is it new to 1.7.2?
Post by Guillaume Laforge
def m = [:].withDefault { key -> key * 2 }
marc fawzi
2010-04-13 17:29:49 UTC
Permalink
SWEET!
Post by Tim Yates
So Marc, that whole top section (down to the testing comment) can be
def defaults = [
(~/[a-z]/):{ 'a default' },
(~/[0-9]/):{ ( it as Integer ) + 10 }
]
def m = [:].withDefault { key ->
defaults.find { it.key.matcher( key ).matches() }?.value( key )
}
Tim
Post by Tim Yates
Cool!
Is it new to 1.7.2?
On Tue, Apr 13, 2010 at 11:08 AM, Guillaume Laforge <
Post by Guillaume Laforge
def m = [:].withDefault { key -> key * 2 }
John Hartnup
2010-04-13 10:24:06 UTC
Permalink
Post by Guillaume Laforge
def m = [:].withDefault { key -> key * 2 }
That is super cool!

Correct me if I'm wrong, but if the map lookup results in a miss,
causing the default closure to be called, the result is added to the
map?

def m = [:].withDefault {k -> k * 2}
m[1] = 3

assert m[1] == 3
assert m[2] == 4

assert m == [1:3, 2:4]

(actually that last assert doesn't work -- but "println m" displays
[1:3, 2:4]. Bug? Feature?)

So even if the result of the closure depends on state, MapWithDefault
will always return the same result for a given key, which is only
calculated the first time it is referenced.

(Of course it's good practice not to use a closure that depends on state!)
--
"There is no way to peace; peace is the way"

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Tim Yates
2010-04-13 10:32:32 UTC
Permalink
Yeah, the get method in the MapWithDefault class does:

public V get(Object key) {
if (!delegate.containsKey(key)) {
delegate.put((K)key, (V)initClosure.call(new Object[]{key}));
}
return delegate.get(key);
}

The issue could be that it doesn't declare a hashCode or equals method that
uses the delegate, and I can't see a way of accessing (or unwrapping) the
delegate map from the MapWithDefault class

But I always get a bit tied up when I start looking at the internals of
groovy, so could be wrong

Tim
Post by John Hartnup
Post by Guillaume Laforge
def m = [:].withDefault { key -> key * 2 }
That is super cool!
Correct me if I'm wrong, but if the map lookup results in a miss,
causing the default closure to be called, the result is added to the
map?
def m = [:].withDefault {k -> k * 2}
m[1] = 3
assert m[1] == 3
assert m[2] == 4
assert m == [1:3, 2:4]
(actually that last assert doesn't work -- but "println m" displays
[1:3, 2:4]. Bug? Feature?)
So even if the result of the closure depends on state, MapWithDefault
will always return the same result for a given key, which is only
calculated the first time it is referenced.
(Of course it's good practice not to use a closure that depends on state!)
--
"There is no way to peace; peace is the way"
---------------------------------------------------------------------
http://xircles.codehaus.org/manage_email
Paul King
2010-04-13 10:39:56 UTC
Permalink
It was added in 1.7.1 just before release. I think the Groovy WebConsole
is a snapshot just before this was added.

Doco is here:

http://groovy.codehaus.org/groovy-jdk/java/util/Map.html#withDefault(groovy.lang.Closure)

But the @since number was only added after the 1.7.2 release. I'll add the since number
on the actual class too - though that is normally hidden.

Regarding John's example:

def m = [:].withDefault {k -> k * 2}
m[1] = 3
assert m[1] == 3
assert m[2] == 4
assert [1:3, 2:4] == m

If you change the last line by swapping the order (see above) then it will work.
I suspect we need to change the equals method in MapWithDefaultto make it work
the other way around, so yes a Jira would be good for that.


Cheers, Paul.
Post by John Hartnup
def m = [:].withDefault { key -> key * 2 }
That is super cool!
Correct me if I'm wrong, but if the map lookup results in a miss,
causing the default closure to be called, the result is added to the
map?
def m = [:].withDefault {k -> k * 2}
m[1] = 3
assert m[1] == 3
assert m[2] == 4
assert m == [1:3, 2:4]
(actually that last assert doesn't work -- but "println m" displays
[1:3, 2:4]. Bug? Feature?)
So even if the result of the closure depends on state, MapWithDefault
will always return the same result for a given key, which is only
calculated the first time it is referenced.
(Of course it's good practice not to use a closure that depends on state!)
---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
John Hartnup
2010-04-13 10:49:44 UTC
Permalink
Post by Paul King
It was added in 1.7.1 just before release. I think the Groovy WebConsole
is a snapshot just before this was added.
http://groovy.codehaus.org/groovy-jdk/java/util/Map.html#withDefault(groovy.lang.Closure)
on the actual class too - though that is normally hidden.
def m = [:].withDefault {k -> k * 2}
m[1] = 3
assert m[1] == 3
assert m[2] == 4
assert [1:3, 2:4] == m
If you change the last line by swapping the order (see above) then it will work.
I suspect we need to change the equals method in MapWithDefaultto make it work
the other way around, so yes a Jira would be good for that.
... although perhaps they should not evaluate to equal?

def m1 = [:].withDefault { k -> k * 2 }
def m2 = [:].withDefault { k -> k + 4 }

m1[1] = 5
m2[1] = 5

assert m1[1] == 5
assert m2[1] == 5
assert m1[4] == 8
assert m2[4] == 8

assert [1:5, 4:8] == m1
assert [1:5, 4:8] == m2
assert m1 == m2 // ??? they have the same cached content, but in
general m1[x] != m2[x]

It's not clear to me what the semantics of equals() should be.
I guess, delegate.equals() && [default closures are functionally
equivalent] -- but that would be a real challenge to achieve.
--
"There is no way to peace; peace is the way"

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
John Hartnup
2010-04-13 10:52:39 UTC
Permalink
Also is it reasonable that using MapWithDefault we can have:

def m = [:].withDefault { it * 2}
assert m[1] == 2
assert [1:2] == m
assert m[10] == 20

You could reasonably argue that the second assertion is not *really* true.
--
"There is no way to peace; peace is the way"

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Tim Yates
2010-04-13 10:57:40 UTC
Permalink
Maybe some method of extracting the delegate map (then comparison can be
done on this if that is what is wanted), and an equals method that takes the
closure into account

Not sure how you can compare closures though... Unless they are the same
physical instance of a single closure

Tim
Post by John Hartnup
def m = [:].withDefault { it * 2}
assert m[1] == 2
assert [1:2] == m
assert m[10] == 20
You could reasonably argue that the second assertion is not *really* true.
--
"There is no way to peace; peace is the way"
---------------------------------------------------------------------
http://xircles.codehaus.org/manage_email
John Hartnup
2010-04-13 11:07:19 UTC
Permalink
Post by Tim Yates
Maybe some method of extracting the delegate map (then comparison can be
done on this if that is what is wanted), and an equals method that takes the
closure into account
Not sure how you can compare closures though...  Unless they are the same
physical instance of a single closure
Academic, since it's fixed to Paul's satisfaction.

But you could compare the AST for the closures.

Actually proving equivalence for different implementations is hard
computer science though. Probably impossible in the general case.

e.g.
{ factorial it } == { it * factorial it -1 }
--
"There is no way to peace; peace is the way"

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Paul King
2010-04-13 11:05:04 UTC
Permalink
In a strict sense, they are probably not exactly equal
but in Groovy's coerce similar things approach I would
think they would be regarded as equal.

Cheers, Paul.
Post by John Hartnup
Post by Paul King
It was added in 1.7.1 just before release. I think the Groovy WebConsole
is a snapshot just before this was added.
http://groovy.codehaus.org/groovy-jdk/java/util/Map.html#withDefault(groovy.lang.Closure)
on the actual class too - though that is normally hidden.
def m = [:].withDefault {k -> k * 2}
m[1] = 3
assert m[1] == 3
assert m[2] == 4
assert [1:3, 2:4] == m
If you change the last line by swapping the order (see above) then it will work.
I suspect we need to change the equals method in MapWithDefaultto make it work
the other way around, so yes a Jira would be good for that.
... although perhaps they should not evaluate to equal?
def m1 = [:].withDefault { k -> k * 2 }
def m2 = [:].withDefault { k -> k + 4 }
m1[1] = 5
m2[1] = 5
assert m1[1] == 5
assert m2[1] == 5
assert m1[4] == 8
assert m2[4] == 8
assert [1:5, 4:8] == m1
assert [1:5, 4:8] == m2
assert m1 == m2 // ??? they have the same cached content, but in
general m1[x] != m2[x]
It's not clear to me what the semantics of equals() should be.
I guess, delegate.equals()&& [default closures are functionally
equivalent] -- but that would be a real challenge to achieve.
---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Paul King
2010-04-13 10:59:28 UTC
Permalink
I created a Jira and it is now fixed:

GROOVY-4168: MapWithDefault doesn't have correct equals functionality

http://jira.codehaus.org/browse/GROOVY-4168

cheers, Paul.
Post by Paul King
It was added in 1.7.1 just before release. I think the Groovy WebConsole
is a snapshot just before this was added.
http://groovy.codehaus.org/groovy-jdk/java/util/Map.html#withDefault(groovy.lang.Closure)
on the actual class too - though that is normally hidden.
def m = [:].withDefault {k -> k * 2}
m[1] = 3
assert m[1] == 3
assert m[2] == 4
assert [1:3, 2:4] == m
If you change the last line by swapping the order (see above) then it will work.
I suspect we need to change the equals method in MapWithDefaultto make it work
the other way around, so yes a Jira would be good for that.
Cheers, Paul.
Post by John Hartnup
Post by Guillaume Laforge
def m = [:].withDefault { key -> key * 2 }
That is super cool!
Correct me if I'm wrong, but if the map lookup results in a miss,
causing the default closure to be called, the result is added to the
map?
def m = [:].withDefault {k -> k * 2}
m[1] = 3
assert m[1] == 3
assert m[2] == 4
assert m == [1:3, 2:4]
(actually that last assert doesn't work -- but "println m" displays
[1:3, 2:4]. Bug? Feature?)
So even if the result of the closure depends on state, MapWithDefault
will always return the same result for a given key, which is only
calculated the first time it is referenced.
(Of course it's good practice not to use a closure that depends on state!)
---------------------------------------------------------------------
http://xircles.codehaus.org/manage_email
---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
John Hartnup
2010-04-12 10:43:31 UTC
Permalink
I'm not sure I understand the requirement, but consider:
// initialize map[a] .. map[z] to 0
def map = [:]
​ ('a'..'z').each {
map[it] = 0
}

// Set map entries if key matches a regex
map.each { key, value ->
if(key =~ /j/) {
map[key] = 1
}
}

A bit loop-oriented and procedural? Mebbe, but it works.
Post by marc fawzi
(new to groovy)
Can I have a list/range, e.g. [a..z], or some regex pattern inside a map to
define the keys so that I don't have to spell them out when initializing?
Example:  how do I replace XXXX with [a..z]
def someMap = [
        XXXX:0,
        1:0,
        2:0,
        3:0,
    ]
This way I don't have to specify the keys "a" thru "z" by hand
How do I initialize multiple keys to one value inside a map using a list of
keys?
Also, how do I initialize multiple keys to one value inside a map using a
regex pattern (for the keys)?
Thanks!
Marc
--
"There is no way to peace; peace is the way"

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email
Loading...