How can I insert nodes in a GPathResult? - groovy

It would seem that GPathResult returns a list of nodes which is the same for each iteration. How can I insert nodes from one iteration and find them in the next, as shown in the example below?
def messageNodes = [] as HashSet
def inputRoot = new XmlSlurper().parse(xmlFile)
inputRoot.testsuite.list().each { suiteNode ->
suiteNode.children().list().each { caseNode ->
caseNode.children().list().each { messageNode ->
messageNodes << messageNode
}
}
}
inputRoot.testsuite.list().each { suiteNode ->
suiteNode.children().list().each { caseNode ->
caseNode.children().list().each { messageNode ->
assert messageNodes.contains(message)
}
}
}

That code isn't inserting nodes... But as it stands (trying to find nodes are the same between scans), it won't work either due to the way XmlSlurper works...
When you call children() on a GPathResult, you end up with an iterator that returns new instances of NodeChild. Because these are new instances, they cannot be compared to the last time you saw them (as their hashcode will be different)
If you change your code to use XmlParser like so:
def messageNodes = []
def inputRoot = new XmlParser().parse(xmlFile)
inputRoot.testsuite.'*'.'*'.each { messageNode ->
messageNodes << messageNode
}
inputRoot.testsuite.'*'.'*'.each { messageNode ->
println messageNode.hashCode()
assert messageNodes.contains(messageNode)
}
I believe it works as you'd expect (I had to guess at your xml format though, as you didn't show us what it was in the question)
As an addition, you can change the lines
inputRoot.testsuite.'*'.'*'.each { messageNode ->
messageNodes << messageNode
}
to
messageNodes.addAll( inputRoot.testsuite.'*'.'*' )
for the same functionality...
It should also be noted that XmlSlurper doesn't store internal state in a navigable way, so if you add nodes, you cannot find them unless you write the xml out and read it back in. If this is the sort of thing you're aiming for, XmlParser is probably also the better route

Related

Kotlin - How to get 5(or selected numbers) random Char from charRange '0..'z'

Did you know to get 5(or selected Number) random char from a Range '0..'z' ??
In "fun putCharTogehter" must call with specific numbers and must put these Char to new String with + Operator and then return to "fun main" with complete strong that is have size 5 or selected
In concept i know how to do but yeah only in concept.
Wish output Example: 38dj(
fun main() {
println(putCharTogehter(5))
}
fun putCharTogehter(stringSize: Int): String {
var charRange = ('0'..'z').random()
return charRange.toString()
}
You can do this by first creating array of chars and then converting it to a string:
fun putCharTogehter(stringSize: Int): String {
return CharArray(stringSize) {
('0'..'z').random()
}.concatToString()
}
Just another option:
fun putCharTogether(stringSize: Int): String = buildString {
repeat(stringSize) {
append(('0'..'z').random())
}
}
Another way:
fun putCharTogether(stringSize: Int): String {
return (1..stringSize).joinToString("") { ('0'..'z').random().toString() }
}
OR
fun putCharTogether(stringSize: Int): String {
return (1..stringSize).map { ('0'..'z').random() }.joinToString("")
}
Another approach:
val charRange = ('0'..'z')
val randomChars = generateSequence { charRange.random() }
randomChars.take(5).joinToString("").run(::println)
You could make it shorter, but the point is you can put together a sequence that's basically a neverending source of random chars, and just take from that as you need them. And you can easily put together a pipeline that, say, drops certain chars you need to exclude in a given situation, or whatever other processing you need. Depends on the situation but it can be a nice approach for composing things

Getting "calling" list size in list closure

I need to get the "calling" list size in a Groovy list closure, e.g.:
def foo = [1,2,3,4,5]
def bar = foo.findAll {
someCondition(it)
}.collect {
processElement(it, <self>.size())
}
where <self> is the list resulting from filtering foo with findAll.
Of course, one can save the intermediate result and get its size, but is it possible to do without it?
The best I can currently think of is:
def bar = foo.findAll { someCondition(it) }
.with { list ->
list.collect { processElement(it, list.size()) }
}
But this just uses with instead of an intermediate result.
Or, you could use the delegate of a Closure:
def foo = [1,2,3,4,5]
def collector = { it -> processElement(it, delegate.size()) }
(collector.delegate = foo.findAll { someCondition(it) }).collect collector
But this is just using the delegate as an intermediate result ;-)

Closure with conditional logging

I have a function like this:
private downloadAllFiles() {
sftpRetriever.listFiles().findAll {
filter.isResponse(it) || filter.isResponseTurned(it)
}.each { String fileName ->
log.info 'Downloading file: {}', fileName
sftpRetriever.downloadFile(fileName)
log.info 'File downloaded'
removeRemoteFile(fileName)
}
}
I am looking for a simple way of modyfing this closure inside of that function so if the size() of findAll is 0 it will simply log 'No more files to download' and .each won't be executed. Is there any simple way to make it in single closure? It is really simply task if I divide it in several parts, but trying to learn closures here and improve my expressiveness :) Thank you in advance for your help.
Take a look at creature below :) It works due the fact that each returns the collection on which it's invoked (+ elvis operator and quite nice Groovy's truth evaluation):
def printContents(Collection collection) {
collection.each {
println it
} ?: println('Collection is empty')
}
printContents([1,2,'hello'])
printContents([])
I don't like this syntax but it's the shorter version which came to my mind.
You can also use metaprogramming to add the method provided by Steinar. It must be added to metaClass before first use but you'll avoid an effort to make extension module:
Collection.metaClass.doIfEmpty { Closure ifEmptyClosure ->
if (delegate.empty) {
ifEmptyClosure()
}
return delegate
}
def printContents(Collection collection) {
collection.doIfEmpty {
println "Collection is empty"
}.each {
println it
}
}
printContents([1,2,'hello'])
printContents([])
One rather generic and reusable option is to extend Collection using an extension module. This is surprisingly easy to do and is even recognized in IDE's (at least in IntelliJ) so you get code completion, etc.
For example, write an the extension class for collections which will perform the closure if the collection is empty. In addtion, it should always return the collection to allow further chaining:
package stackoverflow
class CollectionExtension {
static <T extends Collection> T doIfEmpty(T self, Closure closure) {
if (self.empty) {
closure()
}
return self
}
}
You will also need to tell groovy that this file is an extension module. Add a property file as a resource on the classpath: META-INF/services/org.codehaus.groovy.runtime.ExtensionModule (note: this name and location is mandatory for extension modules, i.e. you cannot change it).
moduleName=stackoverflow-module
moduleVersion=1.0
extensionClasses=stackoverflow.CollectionExtension
Finally a simple test script to show how this can be used:
def printContents(Collection collection) {
collection.doIfEmpty {
println "Collection is empty"
}.each {
println it
}
}
printContents([1,2,'hello'])
printContents([])
Output:
1
2
hello
Collection is empty
You may try the following piece of code:
def l1 = [1,2,3,4]
def l2 = [5,6,7,8]
def m(list) {
list.findAll { it < 5}.with { l ->
size > 0 ?
l.each { e ->
println e
}
:
println('Zero elements found')
}
}
m(l1)
m(l2)
No better idea at the moment.

Map with default value created in a closure

I want to store objects in a map (called result). The objects are created or updated from SQL rows.
For each row I read I access the map as follows:
def result = [:]
sql.eachRow('SELECT something') { row->
{
// check if the Entry is already existing
def theEntry = result[row.KEY]
if (theEntry == null) {
// create the entry
theEntry = new Entry(row.VALUE1, row.VALUE2)
// put the entry in the result map
result[row.KEY] = theEntry
}
// use the Entry (create or update the next hierarchie elements)
}
I want to minimize the code for checking and updating the map. How can this be done?
I know the function map.get(key, defaultValue), but I will not use it, because it is to expensive to create an instance on each iteration even if I don't need it.
What I would like to have is a get function with a closure for providing the default value. In this case I would have lazy evaluation.
Update
The solution dmahapatro provided is exactly what I want. Following an example of the usage.
// simulate the result from the select
def select = [[a:1, b:2, c:3], [a:1, b:5, c:6], [a:2, b:2, c:4], [a:2, b:3, c:5]]
// a sample class for building an object hierarchie
class Master {
int a
List<Detail> subs = []
String toString() { "Master(a:$a, subs:$subs)" }
}
// a sample class for building an object hierarchie
class Detail {
int b
int c
String toString() { "Detail(b:$b, c:$c)" }
}
// the goal is to build a tree from the SQL result with Master and Detail entries
// and store it in this map
def result = [:]
// iterate over the select, row is visible inside the closure
select.each { row ->
// provide a wrapper with a default value in a closure and get the key
// if it is not available then the closure is executed to create the object
// and put it in the result map -> much compacter than in my question
def theResult = result.withDefault {
new Master(a: row.a)
}.get(row.a)
// process the further columns
theResult.subs.add new Detail(b: row.b, c: row.c )
}
// result should be [
// 1:Master(a:1, subs:[Detail(b:2, c:3), Detail(b:5, c:6)]),
// 2:Master(a:2, subs:[Detail(b:2, c:4), Detail(b:3, c:5)])]
println result
What I learned from this sample:
withDefault returns a wrapper, so for manipulating the map use the wrapper and not the original map
row variable is visible in the closure!
create the wrapper for the map in each iteration again, since row var changed
You asked for it, Groovy has it for you. :)
def map = [:]
def decoratedMap = map.withDefault{
new Entry()
}
It works the same way you would expect it to work lazily. Have a look at withDefault API for a detailed explanation.

Groovy, collect objects following a property while not null?

Is there already a way in groovy to collect objects following a property while not null ?
Object.metaClass {
collectWhileNotNull = { Closure follow ->
def result = []
def previous = null
for (def current = delegate; !current.is(previous) && (current != null); current = follow(current)){
result << current
previous = current
}
return result
}
}
It is useful for recursive data structure.
An example of usage for a groovy.util.Node :
Closure getAncestors = { Node n -> n.collectWhileNotNull{ it.parent() }.tail().reverse() }
You can use a Generator class (this is also the name of the necessary pattern) from cookbook.

Resources