Reputation: 17336
I have the following dictionary aka MAP in Groovy.
list = [
[
name:ProductA-manifest-file.json,
path:ProductA,
properties: [
[
key:release,
value:RC1.0
],
[ key:PIPELINE_VERSION,
value:1.0.0.11
]
],
repo:some-generic-repo-local,
],
[
name:ProductA-manifest-file.json,
path:ProductA,
properties: [
[
key:release,
value:RC1.0
],
[ key:PIPELINE_VERSION,
value:1.0.0.75
]
],
repo:some-generic-repo-local,
],
[
name:ProductA-manifest-file.json,
path:ProductA,
properties: [
[
key:release,
value:RC1.0
],
[ key:PIPELINE_VERSION,
value:1.0.0.1104
]
],
repo:some-generic-repo-local,
],
[
more similar entries here containing
],
[
more similar entries here
]
]
I'm trying to sort this map acc. to properties's key = PIPELINE_VERSION's value which is in the format of x.x.x.x i.e. 4 digit set case.
I tried the following command but it's not giving me the entry which contains 1.0.0.1104 as PIPELINE_VERSION. It's giving me 1.0.0.75 (which seems like some kind of string type sort.
// Sort the list entries acc. to pipeline version
def sortedList = list.sort { it.properties.PIPELINE_VERSION.value }
println "###### sortedList" + sortedList
println "\n^^^^\n"
println sortedList.last() // this should return me the entry which contains 1.0.0.1104 but I'm getting 1.0.0.75
}
Also tried using .toInteger() as def sortedList = list.sort { it.properties.PIPELINE_VERSION.toInteger().value }
but that didn't work giving an error.
17:07:22 Caught: groovy.lang.MissingMethodException: No signature of method: java.util.ArrayList.toInteger() is applicable for argument types: () values: []
17:07:22 Possible solutions: toUnique(), toUnique()
17:07:22 groovy.lang.MissingMethodException: No signature of method: java.util.ArrayList.toInteger() is applicable for argument types: () values: []
17:07:22 Possible solutions: toUnique(), toUnique()
Tried:list.sort {it.value.tokenize('.').last()}
that didn't do either.
Smaller example will be:
map = ['a':'1.0.0.11', d:'1.0.0.85', 'b':'1.0.0.1104', 'c':"1.0.0.75"]
println " before sorting : " + map
//map = map.sort {it.value } // this doesn't work if the value is not a pure number format aka x.x.x. format ok lets try the following
map = map.sort {it.value.tokenize('.').last()} // cool that didn't work either
println " after sorting : " + map
Questions:
Upvotes: 1
Views: 1410
Reputation: 50265
Below should work (assuming the format X.X.X.X always has X as a number)
def sortClosure = { a, b ->
// Extract the pattern
def extract = {
it.properties.find { it.key == 'PIPELINE_VERSION' }?.value?.tokenize(/./)
}
// Transpose the numbers to compare
// gives [[1,1], [0,0], [0,0], [11, 1104]] for example
def transposed = [extract(a), extract(b)].transpose()
// Then compare the first occurrence of non-zero value (-1 or 1)
def compareInt = transposed.collect {
it[0].toInteger() <=> it[1].toInteger()
}.find()
compareInt ?: 0
}
list.sort(sortClosure)
Upvotes: 1
Reputation: 24498
Given this:
def map = ['a':'1.0.0.11', d:'1.0.0.85', 'b':'1.0.0.1104', 'c':"1.0.0.75"]
map = map.sort { a, b ->
compareVersion(a.value, b.value)
}
the goal becomes to write a compareVersion
function that satisfies these (incomplete) tests:
assert 0 == compareVersion('1.0.0.0', '1.0.0.0')
assert 1 == compareVersion('1.1.0.0', '1.0.0.0')
assert -1 == compareVersion('1.1.0.0', '1.2.0.0')
assert 1 == compareVersion('1.1.3.0', '1.1.2.0')
assert 1 == compareVersion('1.1.4.1104', '1.1.4.11')
Here is one implementation. It's not the shortest but is quite "Groovy" in style:
//
// e.g. a = '1.0.0.11', b = '1.0.0.85'
//
def compareVersion = { a, b ->
// e.g. [1, 0, 0, 11]
def listA = a.tokenize('.').collect { it as int }
// e.g. [1, 0, 0, 85]
def listB = b.tokenize('.').collect { it as int }
// e.g. [0, 0, 0, -1]
def compareList = [listA, listB].transpose().collect { it[0] <=> it[1] }
// return first non-zero value in compareList, or 0 if there are none
compareList.inject(0) { result, item ->
(result) ?: item
}
}
Output of the original map, and sorted:
$ groovy Q.groovy
before sorting : [a:1.0.0.11, d:1.0.0.85, b:1.0.0.1104, c:1.0.0.75]
after sorting : [a:1.0.0.11, c:1.0.0.75, d:1.0.0.85, b:1.0.0.1104]
Upvotes: 0
Reputation: 28599
def versions = ['a':'1.0.0.11', d:'1.0.0.85', 'b':'1.0.0.1104', 'c':"1.0.0.75"]
//sort:
def sorted = versions.sort{ (it.value=~/\d+|\D+/).findAll() }
result:
[a:1.0.0.11, c:1.0.0.75, d:1.0.0.85, b:1.0.0.1104]
Upvotes: 0
Reputation: 17336
This one-liner solution worked.
For the smaller example!
def versions = ['a':'1.0.0.11', d:'1.0.0.85', 'b':'1.0.0.1104', 'c':"1.0.0.75"]
map = map.sort {it.value.tokenize('.').last().toInteger() }
OK, found the shenzi(one-liner) solution for the complex structure (hint from dmahapatro's answer): i.e. a map > containing array > containing another map for PIPELINE_VERSION.
println "\n\n before sorting : " + list
list = list.sort {it.properties.find { it.key == 'PIPELINE_VERSION' }?.value?.tokenize('.').last().toInteger() }
println " after sorting : " + list
println "\n\n The last entry which contains the sorted shenzi is: " + map.last()
NOTE: The above solution and other answers so far, will only if the PIPELINE first 3 digit sets are 1.0.0 i.e. it's only deciding the highest number based on the 4th digit set (.last()). It'd be fun to use a similar one-liner to find highest PIPELINE_VERSION which actually covers all 4 or N no. of digit sets.
Upvotes: 0