Sort list of version numbers groovy - groovy

I have a list of version numbers like,
Versions = [0.0.10, 0.0.11, 0.0.13, 0.0.14, 0.0.15, 0.0.16, 0.0.17, 0.0.18, 0.0.19, 0.0.20, 0.0.21, 0.0.22, 0.0.23, 0.0.24, 0.0.25, 0.0.26, 0.0.27, 0.0.28, 0.0.29, 0.0.3, 0.0.30, 0.0.33, 0.0.34, 0.0.35, 0.0.36, 0.0.37, 0.0.38, 0.0.39, 0.0.4, 0.0.41, 0.0.42, 0.0.43, 0.0.44, 0.0.45, 0.0.46, 0.0.47, 0.0.48, 0.0.49, 0.0.5, 0.0.5-delivery.5, 0.0.50, 0.0.51, 0.0.52, 0.0.53, 0.0.54, 0.0.55, 0.0.56, 0.0.57, 0.0.58, 0.0.59, 0.0.6, 0.0.60, 0.0.61, 0.0.62, 0.0.63, 0.0.64, 0.0.7, 0.0.8, 0.0.9]'
And i need to get the last version (0.0.64), Versions.sort() && Collections.max(Versions) doesn't work for me.
So I developed this function blow
def mostRecentVersion(def versions) {
def lastversion = "0.0.0"
for (def items : versions) {
def version = items.tokenize('-')[0]
def ver = version.tokenize('.')
def lastver = lastversion.tokenize('.')
if (lastver[0].toInteger() < ver[0].toInteger() ){
lastversion = version
}else if(lastver[0].toInteger() == ver[0].toInteger()) {
if (lastver[1].toInteger() < ver[1].toInteger() ){
lastversion = version
}else if(lastver[1].toInteger() == ver[1].toInteger()){
if (lastver[2].toInteger() < ver[2].toInteger() ){
lastversion = version
}
}
}
}
return lastversion }
i'm asking if there is something better,
Thank you for help :)

the idea:
build map with sortable key and original version value, then sort map by keys, then get only values
to create sortable key for each value
split version to digits & not-digit strings array
prepend to each part 0 to have minimum length 3 (assume each number not longer then 3 digits)
join array to string
so, for 0.11.222-dev ->
1. [ '0', '.', '11', '222', '-dev' ]
2. [ '000', '00.', '011', '222', '-dev' ]
3. '00000.011222-dev'
the code
def mostRecentVersion(versions){
return versions.collectEntries{
[(it=~/\d+|\D+/).findAll().collect{it.padLeft(3,'0')}.join(),it]
}.sort().values()[-1]
}
//test cases:
def fullVersions = ['0.0.10', '0.0.11', '0.0.13', '0.0.14', '0.0.15', '0.0.16',
'0.0.17', '0.0.18', '0.0.19', '0.0.20', '0.0.21', '0.0.22', '0.0.23', '0.0.24',
'0.0.25', '0.0.26', '0.0.27', '0.0.28', '0.0.29', '0.0.3', '0.0.30', '0.0.33',
'0.0.34', '0.0.35', '0.0.36', '0.0.37', '0.0.38', '0.0.39', '0.0.4', '0.0.41',
'0.0.42', '0.0.43', '0.0.44', '0.0.45', '0.0.46', '0.0.47', '0.0.48', '0.0.49',
'0.0.5', '0.0.5-delivery.5', '0.0.50', '0.0.51', '0.0.52', '0.0.53', '0.0.54',
'0.0.55', '0.0.56', '0.0.57', '0.0.58', '0.0.59', '0.0.6', '0.0.60', '0.0.61',
'0.0.62', '0.0.63', '0.0.64', '0.0.7', '0.0.8', '0.0.9']
assert mostRecentVersion(fullVersions) == '0.0.64'
assert mostRecentVersion(['0.0.5-delivery.5', '0.0.3', '0.0.5']) == '0.0.5-delivery.5'
assert mostRecentVersion(['0.0.5.5', '0.0.5-delivery.5', '0.0.5']) == '0.0.5.5'

I believe this will work... it also keeps the original version strings around, incase 0.5.5-devel.5 is the latest... It relies on the fact that Groovy will use a LinkedHashMap for the sorted map, so the order will be preserved :-)
def mostRecentVersion(def versions) {
versions.collectEntries {
[it, it.split(/\./).collect { (it =~ /([0-9]+).*/)[0][1] }*.toInteger()]
}.sort { a, b ->
[a.value, b.value].transpose().findResult { x, y -> x <=> y ?: null } ?:
a.value.size() <=> b.value.size() ?:
a.key <=> b.key
}.keySet()[-1]
}
def fullVersions = ['0.0.10', '0.0.11', '0.0.13', '0.0.14', '0.0.15', '0.0.16', '0.0.17', '0.0.18', '0.0.19', '0.0.20', '0.0.21', '0.0.22', '0.0.23', '0.0.24', '0.0.25', '0.0.26', '0.0.27', '0.0.28', '0.0.29', '0.0.3', '0.0.30', '0.0.33', '0.0.34', '0.0.35', '0.0.36', '0.0.37', '0.0.38', '0.0.39', '0.0.4', '0.0.41', '0.0.42', '0.0.43', '0.0.44', '0.0.45', '0.0.46', '0.0.47', '0.0.48', '0.0.49', '0.0.5', '0.0.5-delivery.5', '0.0.50', '0.0.51', '0.0.52', '0.0.53', '0.0.54', '0.0.55', '0.0.56', '0.0.57', '0.0.58', '0.0.59', '0.0.6', '0.0.60', '0.0.61', '0.0.62', '0.0.63', '0.0.64', '0.0.7', '0.0.8', '0.0.9']
assert mostRecentVersion(fullVersions) == '0.0.64'
assert mostRecentVersion(['0.0.5-delivery.5', '0.0.3', '0.0.5']) == '0.0.5-delivery.5'
assert mostRecentVersion(['0.0.5.5', '0.0.5-delivery.5', '0.0.5']) == '0.0.5.5'
Edit:
Made a change so that 0.5.5.5 > 0.5.5-devel.5

Related

Groovy how to compare two floats

I need to check an array and get all the values if the os value is above 13.0 . So my script is like :
for(String item: jsonObj.devices) {
if (item.os >= 13.0) {
name = item.name
os = item.os
println name
println os
}
}
But it's not listing any values. The item.os have values like : 9, 14.5.6, 13.0.1, 12.1
Could you please let me know where I am wrong here.

How to check all files in a directory except one file in Groovy language

I am trying to search a word in every file in a directory but I want to exclude my logfile.
My code is something like this
user input: search test C:\Users\Desktop\test\Groovy
My code
import static groovy.io.FileType.FILES
import java.text.SimpleDateFormat
def terminal_log = new File("terminal.log")
def terminal_log_path = terminal_log.getName()
def fas = ""
def file2_path = ""
def cmd = System.console().readLine 'Enter command: '
String[] csplice = cmd.split(" ");
if(csplice.length == 3){
def first_parameter = csplice[0]
def second_parameter = csplice[1]
def third_parameter = csplice[2]
if(first_parameter == "search"){
def file = new File(third_parameter)
if(file.exists()){
if(file.isDirectory()){
file.eachFile(FILES) { f ->
fas = "/"+f+"/"
File file2 = new File(fas)
file2_path = file2.getName()
if(!file2_path == terminal_log_path){
file2.eachLine{ line ->
if(line.contains(second_parameter)){
println "This file contains this word"
}
}
}
}
}else{
println "Not a directory"
}
}else{
println "Not exists"
}
}else{
println "Invalid command"
}
}else{
println "Invalid command"
}
This block here is not working
if(!file2_path == terminal_log_path){
Is there any documentation that I can read to exclude a specific file while checking every files in a directory?
Many thanks
EDIT:
the directory of the user input has the logfile (terminal.log)
terminal.log exists
It should be:
if (file2_path != terminal_log_path) {
...
}
or
if (!(file2_path == terminal_log_path)) {
...
}
E.g. you can run the following code to see the result of applying the "Not" operator to a string in Groovy:
def file2_path = "/i/am/path/"
println (!file2_path) // prints false as file2_path is not an empty string
For more info, you can refer to the official Groovy doc on that topic:
The "not" operator is represented with an exclamation mark (!) and inverts the result of the underlying boolean expression. In particular, it is possible to combine the not operator with the Groovy truth:
assert (!true) == false
assert (!'foo') == false
assert (!'') == true

how to create list of dictionary in this code?

I have some names and scores as follows
input = {
'Maths': dict(Mohsen=19, Sadegh=18, Hafez=15),
'Physics': dict(Sadegh=16, Hafez=17, Mohsen=17),
'Chemistry': dict(Hafez=13),
'Literature': dict(Sadegh=14),
'Biology': dict(Mohsen=16, Sadegh=10),
}
if a person don't have any lesson its score consider zero also get avrege of scores's person and sort final list by averge and i want to get an output like this.
answer = [
dict(Name='Sadegh', Literature=14, Chemistry=0, Maths=18, Physics=16, Biology=10, Average=11.6),
dict(Name='Mohsen', Maths=19, Physics=17, Chemistry=0, Biology=16, Literature=0, Average=10.4),
dict(Name='Hafez', Chemistry=13, Biology=0, Physics=17, Literature=0, Maths=15, Average=9),
]
how to do it?
Essentially, you have a dictionary, where the information is arranged based on subjects, where for each subject, you have student marks. You want to collection all information related to each student in separate dictionaries.
One of the approaches which can try, is as below:
Try converting the data which you have into student specific data and then you can calculate the Average of the Marks of all subjects for that student. There is a sample code below.
Please do note that, this is just a sample and you should be trying
out a solution by yourself. There are many alternate ways of doing it and you should explore them by yourself.
The below code works with Python 2.7
from __future__ import division
def convert_subject_data_to_student_data(subject_dict):
student_dict = {}
for k, v in subject_dict.items():
for k1, v1 in v.items():
if k1 not in student_dict:
student_dict[k1] = {k:v1}
else:
student_dict[k1][k] = v1
student_list = []
for k,v in student_dict.items():
st_dict = {}
st_dict['Name'] = k
st_dict['Average'] = sum(v.itervalues()) / len(v.keys())
st_dict.update(v)
student_list.append(st_dict)
print student_list
if __name__ == "__main__":
subject_dict = {
'Maths': dict(Mohsen=19, Sadegh=18, Hafez=15),
'Physics': dict(Sadegh=16, Hafez=17, Mohsen=17),
'Chemistry': dict(Hafez=13),
'Literature': dict(Sadegh=14),
'Biology': dict(Mohsen=16, Sadegh=10),
}
convert_subject_data_to_student_data(subject_dict)
sample_input = {
'Maths': dict(Mohsen=19, Sadegh=18, Hafez=15),
'Physics': dict(Sadegh=16, Hafez=17, Mohsen=17),
'Chemistry': dict(Hafez=13),
'Literature': dict(Sadegh=14),
'Biology': dict(Mohsen=16, Sadegh=10),
}
def foo(lessons):
result = {}
for lesson in lessons:
for user in lessons[lesson]:#dictionary
if result.get(user):
#print(result.get(user))
result.get(user).setdefault(lesson, lessons[lesson].get(user,0))
else:
result.setdefault(user, dict(name=user))
result.get(user).setdefault(lesson,lessons[lesson].get(user,0))
#return list(result.values())
return result.values()
#if name == '__main__':
print(foo(sample_input))

How to retrieve all instances of an attribute from an xml

I have a piece of code below where it looks for an attribute known as 'ExtranetContract'within the xml and performs an assertion to check that the attribute does exist and that its value equals 'true'.
def DailyContracts = xml.'soap:Body'
.TestResponse
.TestContractType
.DailyContracts
def ContractType = DailyContracts.findAll { it.#Type == "C"}
def ExtranetContract = ContractType.DailyContract[0].#ExtranetContract
assert ExtranetContract.size() > 0 && ExtranetContract.every { it == 'true' }
Notice the line: def ExtranetContract = ContractType.DailyContract[0].#ExtranetContract. The [0] represents the first instance of DailyContract(where the contact type equals 'C'.
I am not sure if it will work if I have two or more extranet contracts under contract type 'C' (I have't got an example to test myself but I have created a dummy xml to show an example on where the above line of code may not work effectively in checking all extranet contracts attributes
<Contracts>
<DailyContracts Type="S">
<DailyContract Type="TEST" Code="xxx">
<Name>Extranet</Name>
</DailyContract>
</DailyContracts>
<DailyContracts Type="C">
<DailyContract Type="TEST" Code="xxx" ExtranetContract="true">
<Name>Test Hotel 1</Name>
</DailyContract>
<DailyContract Type="TEST" Code="xxx" ExtranetContract="true">
<Name>Test Hotel 2</Name>
</DailyContract>
</DailyContracts>
</Contracts>
My question is simply how can I replace the [0] so that it checks all extranet contracts? I tried replacing [0] with .findAll and 'collect' but no luck.
You do it as below:
//Pass the response as String to parseText method
def xml = new XmlSlurper().parseText(response)
//Get the Daily Contracts of type C
def contracts = xml.'**'.findAll { it.name() == 'DailyContracts' && it.#Type == 'C'}
//Get the ExtranetContract values
def eCon = contracts.DailyContract*.#ExtranetContract
assert eCon instanceof List
assert !(false in eCon)
You can try it quickly online Demo
The following line:
def ExtranetContract = ContractType.DailyContract[0].#ExtranetContract
gives you an instance of FilteredNodeChildren. Since this class is an instance of Iterable you can use spread operator:
import groovy.util.XmlSlurper
def input = '''<Contracts>
<DailyContracts Type="S">
<DailyContract Type="FIT" Code="xxx" Start="2017-08-15" Days="7">
<Name>Extranet</Name>
</DailyContract>
</DailyContracts>
<DailyContracts Type="C">
<DailyContract Type="FIT" Code="xxx" Start="2017-08-15" Days="7" ExtranetContract="true">
<Name>Melia Costa Del Sol Extranet</Name>
</DailyContract>
<DailyContract Type="FIT" Code="xxx" Start="2017-08-15" Days="7" ExtranetContract="true">
<Name>Melia Costa Del Sol Extranet 2</Name>
</DailyContract>
</DailyContracts>
</Contracts>'''
def slurped = new XmlSlurper().parseText(input)
def ContractType = slurped.DailyContracts.findAll { it.#Type == "C"}
def ExtranetContract = ContractType.DailyContract*.#ExtranetContract
assert ExtranetContract.size() == 2 && ExtranetContract.every { it == 'true' }

Optimize Groovy code

I am fairly new to the Groovy/Grails arena. I recently modified some code to add the following block.
result.processed.each{
def queueEntry = QueueEntry.findById(it.id)<<<START ADD>>>
Set dates = new HashSet<Long>()
def children = QueueEntry.findAllByParent(queueEntry)
for(QueueEntry qe : children){
def f = new GregorianCalendar()
f.setTimeInMillis(DateUtils.getClearedTime(qe.entryTimestamp))
def l = new GregorianCalendar()
l.setTimeInMillis(DateUtils.getClearedTime(qe.exitTimestamp))
while(f < l){
if(f.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY && f.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY){//only add weekdays
dates.add(f.time.time)
}
def xx = new GregorianCalendar()
xx.setTimeInMillis(f.time.next().time)
f = xx
}
dates.add(l.time.time)
} <<<STOP ADD>>>
Set outsideDays = it.numberOfDaysOutsideCVB
Set days = DateUtils.businessDaysBetweenDates(it.entryTimestamp, it.exitTimestamp)
days.removeAll(outsideDays)
days.removeAll(dates)
turnTimes << days.size()
}
The application is now crawling. I am obviously doing something wrong. When this is run for small data-sets it will complete slowly. On larger sets it doesn't finish. Prior to this change it was completing.
You can start with the lines of the below changes.
// Proposed
import static java.util.Calendar.*
// Query for all the children upfront instead of hitting
// database twice on each iteration.
// You can also avoid N + 1 situation if children is fetched eagerly
def allChildren = QueueEntry.where {
id in (result.processed*.id as List<Long>)
}.children.list()
def turnTimes = result.processed.collect { entry ->
allChildren.findAll { it.parent.id == entry.id }.collect { child ->
Set dates = []
new Date( child.entryTimeStamp ).upto( new Date( child.exitTimestamp ) ) {
if ( !( it[DAY_OF_WEEK] in [ SUNDAY, SATURDAY ] ) ) {
dates << it.time
}
}
Set outsideDays = child.numberOfDaysOutsideCVB
Set days =
DateUtils.businessDaysBetweenDates(
child.entryTimestamp,
child.exitTimestamp
)
( days - outsideDays - dates )?.size() ?: 0
}
}
Assumptions:
QueueEntry hasMany children
entryTimestamp/exitTimestamp is java.sql.Timestamp
entryTimestamp/exitTimestamp are not null and entryTimestamp comes before exitTimestamp.
As Burt suggested this question best fits in codereview.stackexchange.com

Resources