Sort array by filename in groovy - sorting

I'm trying to sort a list of jars by their filenames:
def jars = ['app-5.0.0.jar', 'app-5.1.1.jar', 'app-5.2.0-9.jar', 'app-5.2.0-10.jar', 'app-5.2.0.jar', 'app-5.1.0.jar']
jars = jars.sort().reverse()
println jars
The result is:
[app-5.2.0.jar, app-5.2.0-9.jar, app-5.2.0-10.jar, app-5.1.1.jar, app-5.1.0.jar, app-5.0.0.jar]
However, I'm more interested in the natural (and probably more intuitive) sorting to receive this sorted list:
[app-5.2.0-10.jar, app-5.2.0-9.jar, app-5.2.0.jar, app-5.1.1.jar, app-5.1.0.jar, app-5.0.0.jar]
Is there a way to achieve this?
this is my current algorithm for sorting but it's too verbose in my opinion. However, it really does what I'm looking for. Each part of the version (major, minor, maintenance, build) is evaluated independently:
jars = jars.sort { a, b ->
File fileA = new File(a)
File fileB = new File(b)
def partsA = fileA.name.findAll(/\d+/)
def partsB = fileB.name.findAll(/\d+/)
if (partsA[0] == null) partsA[0] = "0"
if (partsB[0] == null) partsB[0] = "0"
if (partsA[0].toInteger() < partsB[0].toInteger()) {
println "${partsA[0]} < ${partsB[0]}"
return -1
} else if (partsA[0].toInteger() > partsB[0].toInteger()) {
println "${partsA[0]} > ${partsB[0]}"
return 1
} else {
if (partsA[1] == null) partsA[1] = "0"
if (partsB[1] == null) partsB[1] = "0"
if (partsA[1].toInteger() < partsB[1].toInteger()) {
println "${partsA[1]} < ${partsB[1]}"
return -1
} else if (partsA[1].toInteger() > partsB[1].toInteger()) {
println "${partsA[1]} > ${partsB[1]}"
return 1
} else {
if (partsA[2] == null) partsA[2] = "0"
if (partsB[2] == null) partsB[2] = "0"
if (partsA[2].toInteger() < partsB[2].toInteger()) {
println "${partsA[2]} < ${partsB[2]}"
return -1
} else if (partsA[2].toInteger() > partsB[2].toInteger()) {
println "${partsA[2]} > ${partsB[2]}"
return 1
} else {
if (partsA[3] == null) partsA[3] = "0"
if (partsB[3] == null) partsB[3] = "0"
if (partsA[3].toInteger() < partsB[3].toInteger()) {
println "${partsA[3]} < ${partsB[3]}"
return -1
} else if (partsA[3].toInteger() > partsB[3].toInteger()) {
println "${partsA[3]} > ${partsB[3]}"
return 1
} else {
println "${partsA[3]} = ${partsB[3]}"
return 0
}
}
}
}
}

Had to try this:
def jars = ['app-5.0.0.jar', 'app-5.1.1.jar', 'app-5.2.0-9.jar', 'app-5.2.0-10.jar', 'app-5.2.0.jar', 'app-5.1.0.jar', 'app-1.0.jar', 'app-0.10.jar']
jars = jars.sort{ -it.findAll( /\d+/ ).join().toInteger() }
println jars
Gets:
[app-5.2.0-10.jar, app-5.2.0-9.jar, app-5.2.0.jar, app-5.1.1.jar, app-5.1.0.jar, app-5.0.0.jar, app-1.0.jar, app-0.10.jar]
Or more thorough version that handles large patch versions:
def jars = ['app-5.0.0.jar', 'app-5.1.1.jar', 'app-5.2.0-9.jar', 'app-5.2.0-10.jar', 'app-5.2.0.jar', 'app-5.1.0.jar', 'app-5.1.1-172.jar']
jars.sort{ a, b ->
def aList = a.findAll(/\d+/)
def bList = b.findAll(/\d+/)
for ( int i = 0 ; i < aList.size() ; i++ ) {
def aVal = aList[i] ? aList[i].toInteger() : 0
def bVal = bList[i] ? bList[i].toInteger() : 0
if ( aVal <=> bVal ) { // only return if non-zero i.e. not equal
return aVal <=> bVal
}
}
bList.size() > aList.size() ? -1 : 0 // all facets match up to now, if b has additional parts it must be later version
}
println jars.reverse()
Gets:
[app-5.2.0-10.jar, app-5.2.0-9.jar, app-5.2.0.jar, app-5.1.1-172.jar, app-5.1.1.jar, app-5.1.0.jar, app-5.0.0.jar]

How about something like this:
def jars = ['app-5.0.0.jar', 'app-5.1.1.jar', 'app-5.2.0-9.jar', 'app-5.2.0-10.jar', 'app-5.2.0.jar', 'app-5.1.0.jar', 'app-5.1.1-172.jar']
// it is probably sufficient to just choose a "high enough" number
// (e.g. 10) instead of resolving max digits.
def maxDigits = jars*.findAll(/\d+/).flatten()*.size().max()
// sort the strings consisting of left-padded version numbers
// e.g. sorting string for 'app-5.1.1-172.jar' is ' 5 1 1172'
jars.sort{ it.findAll(/\d+/)*.padLeft(maxDigits).join() }
println 'max digits: ' + maxDigits
println jars.reverse()
Output:
max digits: 3
[app-5.2.0-10.jar, app-5.2.0-9.jar, app-5.2.0.jar, app-5.1.1-172.jar, app-5.1.1.jar, app-5.1.0.jar, app-5.0.0.jar]

Related

GO concatenate string with int

I’ve the following code which needs to get int value and add it to a string with string suffix. E.g.
At start I'm getting this
"fds data "
After the if statement it should like this
"fds data 10 M"
This is the code:
ltrCfg := "fds data "
if len(cfg.ltrSharedDicts) > 0 {
ltrCfg += strconv.Itoa(cfg.ltrSharedDicts["c_data"])
ltrCfg += "M"
} else {
ltrCfg += "10M"
}
out = append(out, ltrCfg)
ltrCert := “fds data "
if len(cfg.ltrSharedDicts) > 0 {
ltrCert += strconv.Itoa(cfg.ltrSharedDicts["d_data"])
ltrCert += "M"
} else {
ltrCert += “20M"
}
out = append(out, ltrCert)
The code is working but I wonder for the first fork of the if statement
if len(cfg.ltrSharedDicts) > 0 {
ltrCfg += strconv.Itoa(cfg.ltrSharedDicts["c_data"])
ltrCfg += "M"
Is there a better way to achieve it?
For readability, I would write:
cd, ok := cfg.ltrSharedDicts["c_data"]
if !ok {
cd = 10
}
out = append(out, fmt.Sprintf("fds data %dM", cd))

Scala/functional/without libs - check if string permutation of other

How could you check to see if one string is a permutation of another using scala/functional programming with out complex pre-built functions like sorted()?
I'm a Python dev and what I think trips me up the most is that you can't just iterate through a dictionary of character counts comparing to another dictionary of character counts, then just exit when there isn't a match, you can't just call break.
Assume this is the starting point, based on your description:
val a = "aaacddba"
val b = "aabaacdd"
def counts(s: String) = s.groupBy(identity).mapValues(_.size)
val aCounts = counts(a)
val bCounts = counts(b)
This is the simplest way:
aCounts == bCounts // true
This is precisely what you described:
def isPerm(aCounts: Map[Char,Int], bCounts: Map[Char,Int]): Boolean = {
if (aCounts.size != bCounts.size)
return false
for ((k,v) <- aCounts) {
if (bCounts.getOrElse(k, 0) != v)
return false
}
return true
}
This is your method, but more scala-ish. (It also breaks as soon as a mismatch is found, because of how foreach is implemented):
(aCounts.size == bCounts.size) &&
aCounts.forall { case (k,v) => bCounts.getOrElse(k, 0) == v }
(Also, Scala does have break.)
Also, also: you should read the answer to this question.
Another option using recursive function, which will also 'break' immediately once mismatch is detected:
import scala.annotation.tailrec
#tailrec
def isPerm1(a: String, b: String): Boolean = {
if (a.length == b.length) {
a.headOption match {
case Some(c) =>
val i = b.indexOf(c)
if (i >= 0) {
isPerm1(a.tail, b.substring(0, i) + b.substring(i + 1))
} else {
false
}
case None => true
}
} else {
false
}
}
Out of my own curiosity I also create two more versions which use char counts map for matching:
def isPerm2(a: String, b: String): Boolean = {
val cntsA = a.groupBy(identity).mapValues(_.size)
val cntsB = b.groupBy(identity).mapValues(_.size)
cntsA == cntsB
}
and
def isPerm3(a: String, b: String): Boolean = {
val cntsA = a.groupBy(identity).mapValues(_.size)
val cntsB = b.groupBy(identity).mapValues(_.size)
(cntsA == cntsB) && cntsA.forall { case (k, v) => cntsB.getOrElse(k, 0) == v }
}
and roughly compare their performance by:
def time[R](block: => R): R = {
val t0 = System.nanoTime()
val result = block // call-by-name
val t1 = System.nanoTime()
println("Elapsed time: " + (t1 - t0) + "ns")
result
}
// Match
time((1 to 10000).foreach(_ => isPerm1("apple"*100,"elppa"*100)))
time((1 to 10000).foreach(_ => isPerm2("apple"*100,"elppa"*100)))
time((1 to 10000).foreach(_ => isPerm3("apple"*100,"elppa"*100)))
// Mismatch
time((1 to 10000).foreach(_ => isPerm1("xpple"*100,"elppa"*100)))
time((1 to 10000).foreach(_ => isPerm2("xpple"*100,"elppa"*100)))
time((1 to 10000).foreach(_ => isPerm3("xpple"*100,"elppa"*100)))
and the result is:
Match cases
isPerm1 = 2337999406ns
isPerm2 = 383375133ns
isPerm3 = 382514833ns
Mismatch cases
isPerm1 = 29573489ns
isPerm2 = 381622225ns
isPerm3 = 417863227ns
As can be expected, the char counts map speeds up positive cases but can slow down negative cases (overhead on building the char counts map).

go error % not allowed

I have written a program for Mumax with a go syntax but I don't understant my error. Here the script where the error appears :
n:=0
Dtr0:=5*1e-12
Dtd0 :=300*1e-12
Dtf0:=5*1e-12
Dtz0:=20000*1e-12
tr0:=Dtr0
td0:=Dtd0+tr0
tf0:=Dtf0+td0
tz0:=Dtz0+tf0
TT:=tz0
n=t/TT
tr:=tr0+(n*TT)
td:=td0+(n*TT)
tf:=tf0+(n*TT)
tz:=tz0+(n*TT)
if (n % 2 == 0) {
if (n<1 && t<tr) {
a:=(t/tr)
} else if (n>=1 && t>=tz0+((n-1)*TT) && t<tr) {
a:=1/(tr-(tz0+((n-1)*TT)))*(t-(tz0+((n-1)*TT)))
} else if (t>=tr && t<=td) {
a:=1
} else if (t>td && t<=tf) {
a:=(-1/(tf-td))*(t-td)+1
} else if (t>tf && t<tz) {
a:=0
}
}
if (int(n)%2==1) {
if (n<1 && t<tr) {
a:=-(t/tr)
} else if (n>=1.0 && t>=tz0+((n-1)*TT) && t<tr) {
a:=-(1/(tr-(tz0+((n-1)*TT)))*(t-(tz0+((n-1)*TT))))
} else if (t>=tr && t<=td) {
a:=-1
} else if (t>td && t<=tf) {
a:=-((-1/(tf-td))*(t-td)+1)
} else if (t>tf && t<tz) {
a:=0
}
}
And the error message is : line 37: if (n % 2 == 0) {: not allowed: %
Thank's a lot
There are two problems here:
n must be a float because TT must be a float, because that's ultimately a function of two floats. This conflicts with the n := 0 default int definition at the top.
the modulus operator on floats is undefined (see this playground for what happens when you try).
This means you have a very weird Go implementation or we're not seeing everything.
In any case, either you have to either coerce n to an int (as you do in your second if) or use math.Mod somehow.

Version increment using gradle task

I want to increase the version number of my project from 1.0.0. to 1.0.1 automatically whenever a new build is made through bash command. I only need to increase path number and others i will be increasing manually during manual build.
i want to change
this :
version=1.0.0
to
This:
version=1.0.1
using gradle task.
any help that how can i do this .
Is there any way to update this using regex or using substring function.
Here is an example task:
version='1.0.0' //version we need to change
task increment<<{
def v=buildFile.getText().find(version) //get this build file's text and extract the version value
String minor=v.substring(v.lastIndexOf('.')+1) //get last digit
int m=minor.toInteger()+1 //increment
String major=v.substring(0,v.length()-1) //get the beginning
//println m
String s=buildFile.getText().replaceFirst("version='$version'","version='"+major+m+"'")
//println s
buildFile.setText(s) //replace the build file's text
}
Run this task several times and you should see the version change.
A variant:
version='1.0.0'
task incrementVersion<<{
String minor=version.substring(version.lastIndexOf('.')+1)
int m=minor.toInteger()+1
String major=version.substring(0,version.length()-1)
String s=buildFile.getText().replaceFirst("version='$version'","version='"+major+m+"'")
buildFile.setText(s)
}
Here's a custom task for version bumps in Gradle (Android project):
class Version {
private int major
private int minor
private int patch
private int code
Version(int code, String version) {
this.code = code
def (major, minor, patch) = version.tokenize('.')
this.major = major.toInteger()
this.minor = minor.toInteger()
this.patch = patch.toInteger()
}
#SuppressWarnings("unused")
void bumpMajor() {
major += 1
minor = 0
patch = 0
code += 1
}
#SuppressWarnings("unused")
void bumpMinor() {
minor += 1
patch = 0
code += 1
}
#SuppressWarnings("unused")
void bumpPatch() {
patch += 1
code += 1
}
String getName() { "$major.$minor.$patch" }
int getCode() { code }
}
tasks.addRule("Pattern: bump<TYPE>Version") { String taskName ->
if (taskName.matches("bump(Major|Minor|Patch)Version")) {
task(taskName) {
doLast {
String type = (taskName - 'bump' - 'Version')
println "Bumping ${type.toLowerCase()} version…"
int oldVersionCode = android.defaultConfig.versionCode
String oldVersionName = android.defaultConfig.versionName
version = new Version(oldVersionCode, oldVersionName)
version."bump$type"()
String newVersionName = version.getName()
String newVersionCode = version.getCode()
println "$oldVersionName ($oldVersionCode) → $newVersionName ($newVersionCode)"
def updated = buildFile.getText()
updated = updated.replaceFirst("versionName '$oldVersionName'", "versionName '$newVersionName'")
updated = updated.replaceFirst("versionCode $oldVersionCode", "versionCode $newVersionCode")
buildFile.setText(updated)
}
}
}
}
See this Kanji learning Android app for completeness.
Prerequisites
Following format required (note the single quotes):
android {
defaultConfig {
versionCode 3
versionName '0.3.13'
}
}
Usage
$ ./gradlew bumpPatchVersion
> Task :app:bumpPatchVersion
Bumping patch version…
0.3.13 (3) → 0.3.14 (4)
BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
$ ./gradlew bumpMinorVersion
> Task :app:bumpMinorVersion
Bumping minor version…
0.3.14 (4) → 0.4.0 (5)
BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
$ ./gradlew bumpMajorVersion
> Task :app:bumpMajorVersion
Bumping major version…
0.4.0 (5) → 1.0.0 (6)
BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
You could also use split with a increment-matrix, that could be changed depending on the amount of changes:
def version = '1.0.0'
def incstep = '0.0.1'.split(/\./).collect{it.toInteger()}
def indexedVersionList = version.split(/\./).toList().withIndex()
def updatedVersionList = indexedVersionList.collect{num, idx -> num.toInteger()+incstep[idx]}
def updatedVersion = updatedVersionList.join(".")
This is how I did it with Kotlin DSL (build.gradle.kts):
tasks.create("incrementVersion") {
group = "my tasks"
description = "Increments the version in this build file everywhere it is used."
fun generateVersion(): String {
val updateMode = properties["mode"] ?: "minor" // By default, update the minor
val (oldMajor, oldMinor, oldPatch) = version.split(".").map(String::toInt)
var (newMajor, newMinor, newPatch) = arrayOf(oldMajor, oldMinor, 0)
when (updateMode) {
"major" -> newMajor = (oldMajor + 1).also { newMinor = 0 }
"minor" -> newMinor = oldMinor + 1
else -> newPatch = oldPatch + 1
}
return "$newMajor.$newMinor.$newPatch"
}
doLast {
val newVersion = properties["overrideVersion"] as String? ?: generateVersion()
val oldContent = buildFile.readText()
val newContent = oldContent.replace("""= "$version"""", """= "$newVersion"""")
buildFile.writeText(newContent)
}
}
Usage:
./gradlew incrementVersion [-P[mode=major|minor|patch]|[overrideVersion=x]]
Examples:
./gradlew incrementVersion -Pmode=minor
./gradlew incrementVersion -PoverrideVersion=2.5.11
That is given that you have something like this in your build script:
version = "1.2.3"
... and the patch part of the version is just a number (not containing letters like alpha, rc, etc.).
Below solution will not create an issue evern last number exceed from 9-10 and so on
version='1.0.11.1001'
task incrementrevsion{
def v = version
println v
String minor=v.substring(v.lastIndexOf('.')+1) //get last digit
int m=minor.toInteger()+1 //increment
println m
String major=v.substring(0,v.lastIndexOf(".")); //get the beginning
println major
String s=buildFile.getText().replaceFirst("version='$version'","version='"+major+ "." +m+"'")
//println s
buildFile.setText(s) //replace the build file's text
}
def patch = version.substring(version.lastIndexOf('.') + 1)
def p = patch.toInteger() + 1
def major = version.substring(0, version.length() - p.toString().length())
def s = buildFile.getText().replaceFirst("version = '$version'", "version = '" + major + p + "'")
buildFile.setText(s)
The only difference with Alexiy's answer that line 3 contains m.toString().length() as if minor version > 10, i.e 1.0.12 and you will use that approach it will change it to 1.0.113. We need to calculate the length of minor version instead of chopping off only 1 symbol.
And one more thing, usually the last number is called patch, minor is a middle one :)
My Solution where the version will be set by a Parameter.
version = '4.0.0' // I have whitespaces between the equals-sign
task setVersion << {
group = 'Versioning'
description = "Sets the version to the new number specified with -PnewVersion=\'x.x.x\'"
println version
if(project.hasProperty('newVersion')) {
println 'Set Project to new Version '+newVersion
String s=buildFile.getText().replaceFirst("version = '$version'","version = '"+newVersion+"'")
buildFile.setText(s)
}
}
Call Gradle Task with:
gradle setVersion -PnewVersion='5.1.1'
As for me work this solution.
You need add this code into build.gradle file:
version='1.0.1'
tasks.register("incrementVersion") {
doLast {
def ver = version
println ver
String lastNumber = ver.substring(ver.lastIndexOf('.') + 1)
int increment = lastNumber.toInteger() + 1
println increment
String firstNumber = ver.substring(0, ver.lastIndexOf("."))
println firstNumber
String result = buildFile.getText().replaceFirst("version='$version'","version='" + firstNumber + "." + increment + "'")
buildFile.setText(result)
}
}
I know I am posting this quite late, but the answers mentioned above work well upto '1.0.99'. After which it starts misbehaving.
If any one is still interested, I found a different approach.
task increment {
def v = buildFile.getText().find(version)
def (major, minor, patch) = v.tokenize('.')
int newPatch = patch.toInteger() + 1
String newVersion = major + "." + minor + "." + newPatch
String updatedVersion = buildFile.getText().replaceFirst("version='"+v+"'","version='"+newVersion+"'")
buildFile.setText(updatedVersion)
}
This is an example of same think but with KTS(Kotlin Script).
val newVersion: String? by project
tasks.register("bumpVersion") {
this.doFirst {
println("Old version $version")
val newVersion = takeIf { newVersion.isNullOrBlank() }?.let {
val versionArray = version.toString().split(".")
"${versionArray[0]}.${versionArray[1]}.${versionArray.last().toInt().plus(1)}"
} ?: newVersion
buildFile.readText().apply {
println("Bump to $newVersion")
val content = this.replaceFirst("version = \"$version\"", "version = \"$newVersion\"")
buildFile.writeText(content)
}
}
}
Increment automatic the patch or it is possible to send new version as property.
./gradlew bumpVersion -PnewVersion=0.2.0
Kotlin dsl:
tasks.create("incrementPatch") {
group = "version"
description = "Автоматически поднять патч версию в файле VERSION"
doFirst {
incrementVersion("patch")
}
}
tasks.create("incrementMinor") {
group = "version"
description = "Автоматически поднять минор версию в файле VERSION"
doFirst {
incrementVersion("minor")
}
}
tasks.create("incrementMajor") {
group = "version"
description = "Автоматически поднять мажор версию в файле VERSION"
doFirst {
incrementVersion("major")
}
}
tasks.named("compileKotlin") {
dependsOn(":incrementPatch")
}
fun incrementVersion(updateMode: String){
val versions = file("version").readText().trim()
println("read version = $versions")
val (oldMajor, oldMinor, oldPatch) = versions.substringBefore("-").split(".").map(String::toInt)
var (newMajor, newMinor, newPatch) = arrayOf(oldMajor, oldMinor, 0)
when (updateMode) {
"major" -> newMajor = (oldMajor + 1).also { newMinor = 0 }
"minor" -> newMinor = oldMinor + 1
else -> newPatch = oldPatch + 1
}
val newVersion ="$newMajor.$newMinor.$newPatch-SNAPSHOT"
println("new version = $newVersion")
file("version").writeText(newVersion)
}

Simple ranking algorithm in Groovy

I have a short groovy algorithm for assigning rankings to food based on their rating. This can be run in the groovy console. The code works perfectly, but I'm wondering if there is a more Groovy or functional way of writing the code. Thinking it would be nice to get rid of the previousItem and rank local variables if possible.
def food = [
[name:'Chocolate Brownie',rating:5.5, rank:null],
[name:'Fudge', rating:2.1, rank:null],
[name:'Pizza',rating:3.4, rank:null],
[name:'Icecream', rating:2.1, rank:null],
[name:'Cabbage', rating:1.4, rank:null]]
food.sort { -it.rating }
def previousItem = food[0]
def rank = 1
previousItem.rank = rank
food.each { item ->
if (item.rating == previousItem.rating) {
item.rank = previousItem.rank
} else {
item.rank = rank
}
previousItem = item
rank++
}
assert food[0].rank == 1
assert food[1].rank == 2
assert food[2].rank == 3
assert food[3].rank == 3 // Note same rating = same rank
assert food[4].rank == 5 // Note, 4 skipped as we have two at rank 3
Suggestions?
This is my solution:
def rank = 1
def groupedByRating = food.groupBy { -it.rating }
groupedByRating.sort().each { rating, items ->
items.each { it.rank = rank }
rank += items.size()
}
I didn't try it, but maybe this could work.
food.eachWithIndex { item, i ->
if( i>0 && food[i-1].rating == item.rating )
item.rank = food[i-1].rank
else
item.rank = i + 1
}
Here's another alternative that doesn't use "local defs" with the groovy inject method:
food.sort { -it.rating }.inject([index: 0]) { map, current ->
current.rank = (current.rating == map.lastRating ? map.lastRank : map.index + 1)
[ lastRating: current.rating, lastRank: current.rank, index: map.index + 1 ]
}

Resources