How to parse a string of version numbers in Groovy? - groovy

I have a string containing a version number like major.minor.patch.build but I only want to keep major.minor.patch where each number can have 1-3 digits.
How can I do this in groovy?
Examples for a result:
1.20.30.44 -> 1.20.30
or
1.21.1.1 -> 1.21.1

This will work for provided examples.
def majorMinorPatchBuild = '1.20.30.44'
def majorMinorPatch
if (majorMinorPatchBuild.toList().count('.') < 3) {
majorMinorPatch = majorMinorPatchBuild
} else {
majorMinorPatch = majorMinorPatchBuild.split('\\.')[0..-2].join('.')
}
assert majorMinorPatch == '1.20.30'
Note, you'll have to implement some error handling if the input data will have some format deviations.
Test on more data:
[
'1.2.3',
'1.2.3.4',
'11.22.33.44',
'111.222.333.444'
].each {
def majorMinorPatchBuild = it
def majorMinorPatch
if (majorMinorPatchBuild.toList().count('.') < 3) {
majorMinorPatch = majorMinorPatchBuild
} else {
majorMinorPatch = majorMinorPatchBuild.split('\\.')[0..-2].join('.')
}
println("major.minor.patch: '$it' -> '$majorMinorPatch'")
}
Output:
major.minor.patch: '1.2.3' -> '1.2.3'
major.minor.patch: '1.2.3.4' -> '1.2.3'
major.minor.patch: '11.22.33.44' -> '11.22.33'
major.minor.patch: '111.222.333.444' -> '111.222.333'

That should be essentially one-liners in Groovy. To start with 2:
String listBased = '1.20.30.44'.split( /\./ ).take 3 join '.'
String regexBased = '1.20.30.44'.replaceFirst( /(.+)\.[^.]+/, '$1' )
assert listBased == '1.20.30'
assert regexBased == '1.20.30'

This expression returns the substring of an versionString before the last dot - and so results exactly what you have specified in your question:
versionString = '1.20.30.44'
println versionString[0..versionString.lastIndexOf('.')-1]
// => 1.20.30

Related

In Groovy -How to fill empty fields in version with number zero?

I have a version like below and I want to add zero (0) in versionB after 2 decimal. How can I achieve this in groovy?
versionA=1.12.14
versionB=1.11
Expected OutPut:-
versionA=1.12.14
VersionB=1.11.0
Solution
The term you are are looking for is Semantic Versioning ( semver ). This is not the prettiest solution but it will work
//def semver = "1"
def semver = "1.13"
def split = semver.split("\\.");
if(split.size() == 1) {
semver+=".0.0"
} else if(split.size()==2) {
semver+=".0"
}
println semver
In your example you have the variables typed as numbers but they must be strings
A generic variant for version strings of variable lengths:
String getFormattedVersion( String raw, int maxPositions = 3 ){
def parts = raw.split( /\./ )
(0..<maxPositions).collect{ it < parts.size() ? parts[ it ] : '0' }.join '.'
}
assert '1.2.3' == getFormattedVersion( '1.2.3' )
assert '1.2.0' == getFormattedVersion( '1.2' )
assert '1.0.0' == getFormattedVersion( '1' )
assert '1.2.3.0.0' == getFormattedVersion( '1.2.3', 5 )

Groovy String to CSV

I have a string input of the structure like:
[{name=John, dob=1970-07-27 00:00:00.0, score=81},
{name=Jane, dob=1970-07-28 00:00:00.0, score=77}]
I am trying to convert it to a CSV output. So expected output:
"name", "dob", "score"
"John", "1970-07-27 00:00:00.0", 81
"Jane", "1970-07-28 00:00:00.0", 77
So far I have tried something like
def js = "[{name=John, dob=1970-07-27 00:00:00.0, score=81}, {name=Jane, dob=1970-07-28 00:00:00.0, score=77}]"
def js = new JsonSlurper().parseText(s)
def columns = js*.keySet().flatten().unique()
def encode = { e -> e == null ? '' : e instanceof String ? /"$e"/ : "$e" }
// Print all the column names
println columns.collect { c -> encode( c ) }.join( ',' )
// Then create all the rows
println data.infile.collect { row ->
// A row at a time
columns.collect { colName -> encode( row[ colName ] ) }.join( ',' )
}.join( '\n' )
This fails with groovy.json.JsonException: expecting '}' or ',' but got current char 'n'
Defining var js without quotes fails with expecting '}', found ','
Thanks in advance
I wrote this simple parser which will go splitting lines. If your input changes (i.e., comma or = is used in any other place), you will need a more complex parser:
input = '''[{name=John, dob=1970-07-27 00:00:00.0, score=81},
{name=Jane, dob=1970-07-28 00:00:00.0, score=77},
{name=Test, dob=1980-01-01 00:00:00.0, score=90}]'''
maps = (input =~ /\{(.*)\}/)
.collect {
it[1]
.split(', ')
.collectEntries { entry ->
entry.split '=' with {
[it.first(), it.last()]
}
}
}
assert maps == [
[name:'John', dob:'1970-07-27 00:00:00.0', score:'81'],
[name:'Jane', dob:'1970-07-28 00:00:00.0', score:'77'],
[name:'Test', dob:'1980-01-01 00:00:00.0', score:'90']
]
You can try this code snippet
import groovy.json.JsonSlurper
def s = '[{"name":"John", "dob":"1970-07-27 00:00:00.0", "score":81},{"name":"Jane", "dob":"1970-07-28 00:00:00.0", "score":77}]'
def js = new JsonSlurper().parseText(s)
def columns = js*.keySet().flatten().unique()
def encode = { e -> e == null ? '' : e instanceof String ? /"$e"/ : "$e" }
println columns.collect { c -> encode(c) }.join(',')
js.each {
println it.values().join(",")
} ​
I used Groovy JsonSlurper to convert String to Map, For this you need to give proper json String.

Performance issue with Groovy lists / maps

I have this code which compares two lists and find differences, so far so good, it works fine for small lists. Now Im testing with huge lists.
which contains both more than 300000 maps. It takes more than 5 hours to process it. is that normal? How can I reduce the procssing time?
def list1 = [
[cuInfo:"T12",service:"3",startDate:"14-01-16 13:22",appId:"G12355"],
[cuInfo:"T13",service:"3",startDate:"12-02-16 13:00",appId:"G12356"],
[cuInfo:"T14",service:"9",startDate:"10-01-16 11:20",appId:"G12300"],
[cuInfo:"T15",service:"10",startDate:"26-02-16 10:20",appId:"G12999"]
]
​
def list2 = [
[name:"testname1",cuInfo:"T12",service:"3",startDate:"14-02-16 10:00",appId:"G12351"],
[name:"testname1",cuInfo:"T13",service:"3",startDate:"14-01-16 13:00",appId:"G12352"],
[name:"testname1",cuInfo:"T16",service:"3",startDate:"14-01-16 13:00",appId:"G12353"],
[name:"testname2",cuInfo:"T14",service:"9",startDate:"10-01-16 11:20",appId:"G12301"],
[name:"testname3",cuInfo:"T15",service:"10",startDate:"26-02-16 10:20",appId:"G12999"],
[name:"testname3",cuInfo:"T18",service:"10",startDate:"26-02-16 10:20",appId:"G12999"]
]
def m1 = [:]
def m2 = [:]
def rows = list1.collect { me ->
[me, list2.find { it.cuInfo == me.cuInfo && it.service == me.service }]
}.findAll {
it[1]
}.findAll {
/*
* This is where the differences are identified.
* The 'name' attribute is excluded from the comparison,
* by including only the desired attributes.
*/
it[0] != it[1].subMap(['cuInfo', 'service', 'startDate', 'appId'])
}.collect {
/*
* At this point the list only contains the row pairs
* which are different. This step identifies which columns
* are different using asterisks.
*/
(m1, m2) = it
m1.keySet().each { key ->
if(m1[key] != m2[key]) {
m1[key] = "*${m1[key]}*"
m2[key] = "*${m2[key]}*"
}
}
[m1, m2]
}.collect {
[it[0].values(), it[1].values()].flatten() as String[]
}
Maybe this will help a little. I didn't have time to test but your code has a lot of collects and find alls that can cause performance issues
def results = []
list1.each{ lst1 ->
def list1WithDifferences = []
def list2WithDifferences = []
def add = false
def match = list2.find{ lst2 -> lst2.cuInfo == lst1.cuInfo && lst2.service == lst1.service }
match.each{k, v ->
if(k != 'name'){
if(v != lst1[k]){
add = true
list1WithDifferences << "*${lst1[k]}*"
list2WithDifferences << "*${v}*"
}else{
list1WithDifferences << v
list2WithDifferences << v
}
}else{
list2WithDifferences << v
}
}
if(add){
results << list1WithDifferences + list2WithDifferences
}
}
println(results)

How I can find the contents of inner nesting?

Lets say I have a string like this :
string = [+++[>>[--]]]abced
Now I want a someway to return a list that has: [[--],[>>],[+++]]. That is the contents of the deepest [ nesting followed by other nesting. I came up with this solution like this :
def string = "[+++[>>[--]]]"
loop = []
temp = []
string.each {
bool = false
if(it == "["){
temp = []
bool = true
}
else if( it != "]")
temp << it
if(bool)
loop << temp
}
println loop.reverse()
But this indeed takes the abced string after the last ] and put into the result!. But what I want is only [[--],[>>],[+++]]
Are there any groovy way of solving this?
You can use this, if you wouldn't mind using recursion
def sub(s , list){
if(!s.contains('[') && !s.contains('['))
return list
def clipped = s.substring(s.lastIndexOf('[')+1, s.indexOf(']'))
list.add(clipped)
s = s - "[$clipped]"
sub(s , list)
}
Calling
sub('''[+++[>>[--]]]abced''' , [])
returns a list of all subportions enclosed between braces.
['--', '>>', '+++']
If your brackets are symmetrical, you could just introduce a counter variable that holds the depth of the bracket nesting. Only depth levels above 0 are allowed in the output:
def string = "[+++[>>[--]]]abc"
loop = []
temp = []
depth = 0;
string.each {
bool = false
if(it == "["){
temp = []
bool = true
depth++;
}
else if (it == "]"){
depth--;
}
else if (depth > 0){
temp << it
}
if(bool){
loop << temp
}
}
println loop.reverse()
class Main {
private static final def pattern = ~/([^\[]*)\[(.+?)\][^\]]*/
static void main(String[] args) {
def string = "[+++[>>[--]]]abced"
def result = match(string)
println result
}
static def match(String val) {
def matcher = pattern.matcher(val);
if (matcher.matches()) {
return matcher.group(1) ? match(matcher.group(2)) + matcher.group(1) : match(matcher.group(2))
}
[val]
}
}
System.out
[--, >>, +++]
The capturing of the first group in the regex pattern could probably be improved. Right now the first group is any character that is not [ and if there are nothing in front of the first [ then the first group will contain an empty string.

Groovy: indexes of substrings?

How do I find the indexes of all the occurances of a substring in a large string -
(so basically ,and extension of the "indexOf" function) . Any ideas?
Current situation:
def text = " --- --- bcd -- bcd ---"
def sub = "bcd"
text.indexOf(sub)
// = 9
I want something like:
def text = " --- --- bcd -- bcd ---"
def sub = "bcd"
text.indexesOf(sub)
// = [9,15]
Is there such a function? How should I implement it otherwise? (in a non trivial way)
You could write a new addition to the String metaClass like so:
String.metaClass.indexesOf = { match ->
def ret = []
def idx = -1
while( ( idx = delegate.indexOf( match, idx + 1 ) ) > -1 ) {
ret << idx
}
ret
}
def text = " --- --- bcd -- bcd ---"
def sub = "bcd"
text.indexesOf(sub)
There is nothing I know of that exists in groovy currently that gets you this for free though
This is a relatively easy approach:
String.metaClass.indexesOf = { arg ->
def result = []
int index = delegate.indexOf(arg)
while (index != -1) {
result.add(index);
index = delegate.indexOf(arg, index+1);
}
return result;
}
Note that this will find overlapping instances (i.e. "fooo".indexesOf("oo") will return [1, 2]). If you don't want this, replace index+1 with index+arg.length().

Resources