Groovy: Different behaviour observed using the eachWithIndex method - groovy

I was doing a Groovy tutorial online there and after playing around with the code I observed some behaviour that I can't understand.
First I created a Map object like this:
def devMap = [:]
devMap = ['name':'Frankie', 'framework':'Grails', 'language':'Groovy']
devMap.put('lastName','Hollywood')
Then I called eachWithIndex to print out the values like so:
devMap.eachWithIndex { println "$it.key: $it.value"}
Which printed this to the console:
name: Frankie
framework: Grails
language: Groovy
lastName: Hollywood
But when I printed to the console from the eachWithIndex method like this using the arrow operator:
devMap.eachWithIndex { it, i -> println "$i: $it" }
The following got printed to the console:
0: name=Frankie
1: framework=Grails
2: language=Groovy
3: lastName=Hollywood
So what I can't understand is why the indexes got printed with the second statement and why there are = signs but no : signs between the key-value pairs?
Thanks.

When you use the no-arg version of eachWithIndex, it is the current entry in the Map. That means that it.key and it.value return what you expect.
When you use the two-arg version of eachWithIndex, again, it is the current entry in the Map and i is the current index. You're printing i, the index, and then since you are only printing it, you are getting the result of it.toString(), which formats the map entry as "${it.key}=${it.value}"

Your second example is equivalent to:
devMap.eachWithIndex { it, index -> println "$index: ${it.toString()}" }
where this shows that the toString() implementation uses the = syntax:
devMap.each { println it.toString() }
Note that this is closer to your goal (as I interpret it):
devMap.eachWithIndex { it, index -> println "$index: ${it.key}: ${it.value}" }

Related

How to access a variable outside of a loop in Groovy

Beginner alert! I have a simple Groovy script that aims to break an argument list into key-value pairs, stored in an associative array (HashMap?). The code works fine until the point where it splits the parameters, but when it tries to put the results back into the array, it throws an exception, stating it cannot access a null element.
I suppose the reason for this is that it can't access the variable that was declared outside the loop.
Here's the script:
def input = "https://weyland-yutani.corp/engineering/bio?param1=1&param2=2"
def params = [:] // wanna store key-value pairs here
if (input.split('\\?').size() >= 2) {
def p = input.split('\\?').last() // get the param string
p.split('\\&').each { // cut the string into an argument list
def keyval = it.split('=') // cut the argument into a key-value pair
println keyval // <-- prints "[param1, 1]", looks okay
params[keyval[0]].put(keyval[1]) // ERROR: "Cannot invoke method put() on null object"
//params[keyval[0]].add(keyval[1]) // ERROR, same sh**
}
}
Error message:
Caught: java.lang.NullPointerException: Cannot invoke method put() on null object
java.lang.NullPointerException: Cannot invoke method put() on null object
at jdoodle$_run_closure1.doCall(jdoodle.groovy:10)
[...]
As it was stated in this article, the way you declare a variable can affect it's scope, but none of my tries succeeded.
Could you give me an advice what am I missing?
The following code:
def input = "https://weyland-yutani.corp/engineering/bio?param1=1&param2=2"
def params = input.tokenize('?').last().tokenize('&').collectEntries { keyval ->
keyval.tokenize('=')
}
println params.getClass()
println params
demonstrates one way of doing this. When run, this prints:
─➤ groovy solution.groovy
class java.util.LinkedHashMap
[param1:1, param2:2]
─➤
As an alternative, if you are ok with using an external library, you could use a url parsing class. This example from HttpBuilder (which is a tad outdated at this point, there are probably others out there):
#Grab('org.codehaus.groovy.modules.http-builder:http-builder:0.7.2')
import groovyx.net.http.URIBuilder
def input = "https://weyland-yutani.corp/engineering/bio?param1=1&param2=2"
def uri = new URIBuilder(input)
println uri.query.getClass()
println uri.query
which, when run, prints:
─➤ groovy solution2.groovy
class java.util.HashMap
[param1:1, param2:2]
─➤
As another example, this time using google http client library class GenericUrl:
#Grab('com.google.http-client:google-http-client:1.39.1')
import com.google.api.client.http.GenericUrl
def input = "https://weyland-yutani.corp/engineering/bio?param1=1&param2=2"
def url = new GenericUrl(input)
println (url instanceof Map)
url.each { k, v ->
println "$k -> $v"
}
println "value of param1: ${url.param1}"
which, when executed prints:
─➤ groovy solution3.groovy
true
param1 -> [1]
param2 -> [2]
value of param1: [1]
─➤
It should be noted that google does the right thing here. When asking for the value of a parameter, the answer should really be a list. This is because you can say ?a=1&a=2&a=3 which from what I understand should be interpreted not as replacing the value of a, but rather as a being a list of values 1, 2, 3.
So when you ask the google library for the value of a param, you get back a list which in this case happens to be one param long.
There is no code that is ever putting anything in params so when you do params[keyval[0]] that will always evaluate to null, so params[keyval[0]].put(keyval[1]) can't work because you are invoking .put on a null reference.

Groovy remove null elements from a map

I am getting a map in my method from another server and I have some null values, I wanted to remove those ones, because I am struggling with those values in the following process:
My map looks something like:
I had done the next code, but without satisfactory results:
map.values().removeAll(Collections.singleton(null))
Any ideas?
Thanks
Edit
The Groovy way, is to filter the entries you want:
def map = [a:42, b:null]
def cleanMap = map.findAll{ it.value!=null }
println cleanMap
// => [a:42]
Previous answer:
Seems to work with Jdk8/Groovy 2.5, but not for OP
To remove all elements with a value with null, remove on the map directly:
def map = [a:42, b:null]
map.removeAll{ it.value == null }
println map
// => [a:42]

Find a value in a collection and assign top an variable using groovy

Hello Groovy Experts,
I am using the below command to get all the ODI Dataservers.
def PSchema=DServer.getPhysicalSchemas();
When I print the PSchema variable I getting the following values.
[oracle.odi.domain.topology.OdiPhysicalSchema ABC.X1, oracle.odi.domain.topology.OdiPhysicalSchema ABC.X2]
What I am trying to achieve here I will be passing X1 or X2 during runtime...
And then I want to validate this value with the PSchema result and the print the following value:
oracle.odi.domain.topology.OdiPhysicalSchema ABC.X2
I tried using the following options:
def PSchema44 = PSchema11.findIndexValues { it =~ /(X1)/ }
def pl=PSchema11.collect{if(it.contains ('X1)){return it}}
I tried for loop to check whether values are getting printed properly ..result is fine:
for (item in PSchema11 )
{
println item
}
Assuming 'X1' and 'X2' are the names for the physical schemas, you should be able to do something like this:
def phys = "X1"
def pSchemas = dServer.getPhysicalSchemas()
def schema = pSchemas.find{it.schemaName == phys}
also I guess you are new to Groovy, I suggest you read up on syntax and naming conventions. For example, variable names should always start with a lower case letter

prefix println statements in groovy

I'd like to achieve the following goals using groovy:
Prepend (prefix) all println statements in groovy with the current time
I want the continue printing those println statements to print on standard out (console in my case).
I want to do it at one generic place, so that I don't have to individually modify all of these println statements.
Is there anything (like groovy aspect, log4j console appender etc.) that I could use to have all println statements prepend the time?
Metaprogramming. You could have PrintStream.metaClass.invokeMethod intercept the call to the println method and add the date to the passed in string.
Something like this, adapted from example code in Subramaniam's "Programming Groovy":
System.out.metaClass.invokeMethod = {String name, args ->
def validMethod = System.out.metaClass.getMetaMethod(name, args)
if (! validMethod)
{
return System.out.metaClass.invokeMissingMethod(delegate,name,args)
}
if ( validMethod.name == 'println')
{
args[0] = "${(new Date()).toString()} : ${args[0]}".toString()
}
validMethod.invoke(delegate,args)
}

What function is meant to format/substitute {0} {1} parameters in an string in Grails/Groovy?

I'm just getting started with Groovy/Grails
I noticed the error messages you get when you validate a form look like this:
Property [{0}] of class [{1}] cannot be blank
For example this code to dump the errors to the console
s.errors.allErrors.each
{
println it.defaultMessage
}
Now, it.arguments contains the arguments that need to be filled in here.
The problem is, I can't find any method in the Grails or Groovy documentation that formats strings based on positional parameters like {0}, {1} and substitutes values from an array
I need something like python's %
What is the proper way to format these error strings so the parameters get substituted properly?
These markers are actually replaced using the standard java.text.MessageFormat APIs. If you display the messages using Grail's g:message tag, it will fill in the gaps if you pass a suitable args="..." attribute:
<g:message code="mymessagecode" args="${['size', 'org.example.Something']}"/>
Under certain circumstances (within GSP pages and from controllers IIRC) you cann call the tag like a function:
g.message(code:'mymessagecode',args: ['size', 'org.example.Something'])
Note, that the value to supply as message code is only a symbolic string constant. The actual translation (the message text with the "gaps" in it) will be read by the framework using Spring's reloadable resource bundles.
If all you actually have is a translation text, you can call the message formatting APIs directly. See for example:
import java.text.MessageFormat
...
args = ["english"].toArray()
println(MessageFormat.format("Translation into {0}", args))
// Or - as the method is variadic:
println(MessageFormat.format("Translation into {0}", "english"))
Look what Groovy can do for you, using a little bit of meta-programming.
MessagesBundle_en_US.properties:
greetings = Hello {0}.
inquiry = {0}: How are you {1}?
farewell = Goodbye.
ResourceBundleWithSugar.groovy:
import java.text.MessageFormat
class ResourceBundleUtils {
def propertyMissing(String name) { this.getString(name) }
def methodMissing(String name, args) {
MessageFormat.format(this.getString(name), args)
}
}
ResourceBundle.metaClass.mixin ResourceBundleUtils
def msg = ResourceBundle.getBundle("MessagesBundle", new Locale("en","US"));
println msg.greetings("Serge")
println msg.inquiry("Serge","Mary")
println msg.farewell // You can use also: msg.['farewell'] msg."farewell" or msg.getString("farewell")
Output:
Hello Serge.
Serge: How are you Mary?
Goodbye.

Resources