Groovy iterating list variables - groovy

I am working on a script to collect field names (dialogPartyASelection_* && dialogPartyBSelection_*) and then compare the two values to see check if they match. The full list(selections)of fields is being broken down into 'groups' before comparison. I can break out certain parts (IE, comparing the two field values via iteration) and test successfully, however when bringing everything together the script doesn't seem to compare correctly. I may be approaching this/setting myself up to do this wrong, (have started to toy with creating a map, with party A as the key with party B as value).
Snippet of code:
// Test Variables
PartyBSelection_Propertieshamster = 'Accepted'
PartyBSelection_Propertieszembra = 'Agreed'
PartyBSelection_Propertiesdogs = 'Agreed'
PartyBSelection_Propertiescats = 'Decision taken'
PartyASelection_Propertieshamster = 'Accepted'
PartyASelection_Propertieszembra = 'Agreed'
PartyASelection_Propertiesdogs = 'Agreed'
PartyASelection_Propertiescats = 'Decision taken'
// example of selections(there are lots of entries for A/B party) = ['dialogPartyBSelection_Communication','dialogPartyASelection_Housing','dialogPartyASelection_Income','PartyASelection_Properties']
def selectedGroup = { s -> selections.findAll { it.contains s}} // for pulling groups of questions from list
def isAgreed = { a, b -> (a in ['Agreed', 'Decision taken','Accepted'] && b in ['Agreed', 'Decision taken','Accepted']) } // for comparing values
for(questions in selectedGroup("Properties")){
{k -> percentCompleteProperties += isAgreed("PartyASelection_${k}", "PartyBSelection_${k}")? 1 : 0}
println questions
println percentCompleteProperties
}
Current output:
PartyBSelection_Propertiescats
0
PartyBSelection_Propertieshamster
0
PartyBSelection_Propertiesdogs
0
PartyBSelection_Propertieshamster
0
PartyASelection_Propertiescats
0
PartyASelection_Propertieshamster
0
PartyASelection_Propertiesdogs
0
PartyASelection_Propertieshamster
0

This is sample code.
but i had changed test data etc.
please check whether this logic is correct for your case.
// Test Variables
def testVariables = [
'PartyBSelection_PropertiesAssets':'Accepted',
'PartyBSelection_PropertiesDebts': 'Agreed',
'PartyBSelection_PropertiesMoney': 'Agreed',
'PartyBSelection_PropertiesSpecialGoods':'Decision taken',
'PartyASelection_PropertiesAssets':'Accepted',
'PartyASelection_PropertiesDebts':'Agreed',
'PartyASelection_PropertiesMoney':'Agreed',
'PartyASelection_PropertiesSpecialGoods':'Decision taken'
]
List<String> selections= [
'PropertiesAssets',
'PropertiesDebts',
'PropertiesMoney',
'PropertiesSpecialGoods',
'PropertiesAssets',
'PropertiesDebts',
'PropertiesMoney',
'PropertiesSpecialGoods'
]
def selectedGroup = { s -> selections.findAll { it.contains s}}
List<String> okLabels = ['Agreed', 'Decision taken','Accepted']
def isAgreed = {a, b ->
(testVariables[a] in okLabels && testVariables[b] in okLabels)
}
List<Map<String, Integer>> result = selectedGroup("Properties").collect {String question ->
[(question) : isAgreed("PartyASelection_${question}", "PartyBSelection_${question}")? 1 : 0 ]
}
assert result == [
['PropertiesAssets':1],
['PropertiesDebts':1],
['PropertiesMoney':1],
['PropertiesSpecialGoods':1],
['PropertiesAssets':1],
['PropertiesDebts':1],
['PropertiesMoney':1],
['PropertiesSpecialGoods':1]
]
Other way
def r = [:]
def questionsCount = selectedGroup("Properties").size()
selectedGroup("Properties").eachWithIndex {String question, Integer i ->
println "question:${question}(${i+1}/${questionsCount})"
r.put(question,isAgreed("PartyASelection_${question}", "PartyBSelection_${question}")? 1 : 0 )
}
// This version includes all records in to the one map.
// Also, a record that is duplicated (as key) is overwritten.
r == [PropertiesAssets:1, PropertiesDebts:1, PropertiesMoney:1, PropertiesSpecialGoods:1]
then output:
question:PropertiesAssets(1/8)
question:PropertiesDebts(2/8)
question:PropertiesMoney(3/8)
question:PropertiesSpecialGoods(4/8)
question:PropertiesAssets(5/8)
question:PropertiesDebts(6/8)
question:PropertiesMoney(7/8)
question:PropertiesSpecialGoods(8/8)

Related

How to sort a list in descending order according to a map which is already sorted in descending order

I have a JSON array (list of maps) similar to:
def listOfMap = [[TESTCASE:1, METHOD:'CLICK', RESULT:'PASS'],
[TESTCASE:2, METHOD:'CLICK', RESULT:'FAIL'],
[TESTCASE:3, METHOD:'CLICK', RESULT:'FAIL'],
[TESTCASE:4, METHOD:'TYPETEXT', RESULT:'FAIL']]
I am grouping by the METHOD names and collecting the FAILURE % of each method
def percentage (map){
(map.FAIL ?: 0) / ((map.PASS ?: 0) + (map.FAIL ?: 0)) * 100
}
def result = listOfMap.groupBy{it.METHOD}
.collectEntries{[(it.key) : percentage(it.value.countBy{it.RESULT})]}
Now my output will be [CLICK : 66.6, TYPETEXT : 100]
To sort the above result in descending order of percentage,
def sortedResult = result.sort { a, b -> b.value <=> a.value }
Now my output will be [TYPETEXT : 100, CLICK : 66.6]
How can i get the FAIL count and PASS count of the METHOD linked to the above sorted order?
My output should be two separate lists (sorted in descending order of failure %)
passList = [0, 1] Note : [TYPETEXT passed 0 times, CLICK passed 1 time]
failList = [1, 2] Note : [TYPETEXT failed 1 time, CLICK failed 2 times]
Basically i am looking for this data to create a CSV report something like the below from the listofMap given:
Given this (from the original post):
def listOfMap = [[TESTCASE:1, METHOD:'CLICK', RESULT:'PASS'],
[TESTCASE:2, METHOD:'CLICK', RESULT:'FAIL'],
[TESTCASE:3, METHOD:'CLICK', RESULT:'FAIL'],
[TESTCASE:4, METHOD:'TYPETEXT', RESULT:'FAIL']]
Consider using Expando so that percentage is a field, but also passCount and failCount:
def percentage(passCount, failCount) {
failCount / (passCount + failCount) * 100
}
def result = listOfMap.groupBy{it.METHOD}.collectEntries{
def rec = new Expando()
def count = it.value.countBy{ it.RESULT }
rec."passCount" = count.'PASS' ?: 0
rec."failCount" = count.'FAIL' ?: 0
rec."percentage" = percentage(rec."passCount",
rec."failCount")
[(it.key) : rec]
}
def sortedResult = result.sort { a, b ->
b.value."percentage" <=> a.value."percentage"
}
sortedResult.each { println it }
The output matches the basic schema desired for the CSV:
$ groovy Example.groovy
TYPETEXT={failCount=1, percentage=100, passCount=0}
CLICK={failCount=2, percentage=66.6666666700, passCount=1}
You can do a multi-grouping to make your life easier:
def listOfMap = [[TESTCASE: 1, METHOD: 'CLICK', RESULT: 'PASS'],
[TESTCASE: 2, METHOD: 'CLICK', RESULT: 'FAIL'],
[TESTCASE: 3, METHOD: 'CLICK', RESULT: 'FAIL'],
[TESTCASE: 4, METHOD: 'TYPETEXT', RESULT: 'FAIL']]
def result = listOfMap.groupBy({ it.METHOD }, { it.RESULT })
.collectEntries { method, r ->
def passCount = r.PASS?.size() ?: 0
def failCount = r.FAIL?.size() ?: 0
[method, [passCount: passCount, failCount: failCount, failPercentage: (failCount / (passCount + failCount) * 100.0)]]
}.sort { -it.value.failPercentage }
You can then do:
result.values().failCount
or
result.values().passCount
to get the numbers you require in the question.
It's best to never try and keep multiple separate lists in the same order, much easier to store all the data together, and extract it after sorting the whole thing

Groovy: Get index of all occurences of sublist from arraylist

I am new to groovy and trying to find the indexes of all sublists in a list.
I am trying to use something like Collections.indexOfSubList like in java but it gives exception saying it applies on Lists and not ArrayLists.
So I am trying to define my own function. I am finding all the indices of all the elements in the smaller list existing in the longer list and then subtracting the indices of the result array. If it comes to 1 then I am considering that index to a sublist.
I know that I have the logic a little twisted. Can somebody guide with a better and efficient way of doing this.
Below is my code:
List list1 = [1,2,3,4,5,6,1,2,3]
List list2 = [1,2]
index1 = list1.findIndexValues {
it == list2[0];
}
index2 = list1.findIndexValues {
it == list2[1];
}
println index1
println index2
result = []
for (int i = 0; i < index1.size(); i++) {
result.add(index2[i]-index1[i]);
}
println result
Edit: no longer uses Collections due to new issue re: Elastic Search.
The following code traverses along the source list, creating a sublist. It checks the sublist to see if it starts with the target list. See the asserts below (e.g. the indexes are 0-based):
def listStartsWithSubList = { source, target ->
def result = false
if (source.size() >= target.size()) {
result = true
target.eachWithIndex { item, index ->
result = result && (item == source[index])
}
}
result
}
def indexOfSubLists = { source, target ->
def results = []
source.eachWithIndex { item, index ->
def tmpList = source[index..source.size()-1]
if (listStartsWithSubList(tmpList, target)) {
results << index
}
}
results
}
assert [1] == indexOfSubLists([1,2,3], [2,3])
assert [2] == indexOfSubLists([1,2,3], [3])
assert [] == indexOfSubLists([1,2,3], [4])
assert [0,6] == indexOfSubLists([1,2,3,4,5,6,1,2,3], [1,2])

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)

Remove numbers which are repeated several times in a row

I have collection
def list = [4,1,1,1,3,5,1,1]
and I need to remove numbers which are repeated three times in a row. As a result I have to get an [4,3,5,1,1]. How to do this in groovy ?
This can be done by copying the list while ensuring the two previous elements are not the same as the one to be copied. If they are, drop the two previous elements, otherwise copy as normal.
This can be implemented with inject like this:
def list = [4,1,1,1,3,5,1,1]
def result = list.drop(2).inject(list.take(2)) { result, element ->
def prefixSize = result.size() - 2
if ([element] * 2 == result.drop(prefixSize)) {
result.take(prefixSize)
} else {
result + element
}
}
assert result == [4,3,5,1,1]
You can calculate the uniques size in the next three elements and drop them when they are 1:
def list = [4,1,1,1,3,5,1,1]
assert removeTriplets(list) == [4,3,5,1,1]
def removeTriplets(list) {
listCopy = [] + list
(list.size()-3).times { index ->
uniques = list[index..(index+2)].unique false
if (uniques.size() == 1)
listCopy = listCopy[0..(index-1)] + listCopy[(index+3)..-1]
}
listCopy
}
Another option is to use Run Length Encoding
First lets define a class which will hold our object and the number of times it occurs in a row:
class RleUnit {
def object
int runLength
RleUnit( object ) {
this( object, 1 )
}
RleUnit( object, int runLength ) {
this.object = object
this.runLength = runLength
}
RleUnit inc() {
new RleUnit( object, runLength + 1 )
}
String toString() { "$object($runLength)" }
}
We can then define a method which will encode a List into a List of RleUnit objects:
List<RleUnit> rleEncode( List list ) {
list.inject( [] ) { r, v ->
if( r && r[ -1 ].object == v ) {
r.take( r.size() - 1 ) << r[ -1 ].inc()
}
else {
r << new RleUnit( v )
}
}
}
And a method that takes a List of RleUnit objects, and unpacks it back to the original list:
List rleDecode( List<RleUnit> rle ) {
rle.inject( [] ) { r, v ->
r.addAll( [ v.object ] * v.runLength )
r
}
}
We can then encode the original list:
def list = [ 4, 1, 1, 1, 3, 5, 1, 1 ]
rle = rleEncode( list )
And filter this RleUnit list with the Groovy find method:
// remove all elements with a runLength of 3
noThrees = rle.findAll { it.runLength != 3 }
unpackNoThrees = rleDecode( noThrees )
assert unpackNoThrees == [ 4, 3, 5, 1, 1 ]
// remove all elements with a runLength of less than 3
threeOrMore = rle.findAll { it.runLength >= 3 }
unpackThreeOrMore = rleDecode( threeOrMore )
assert unpackThreeOrMore == [ 1, 1, 1 ]

In Groovy, how do I add up the values for a certain property in a map?

I have the following map:
def map = [];
map.add([ item: "Shampoo", count: 5 ])
map.add([ item: "Soap", count: 3 ])
I would like to get the sum of all the count properties in the map. In C# using LINQ, it would be something like:
map.Sum(x => x.count)
How do I do the same in Groovy?
Assuming you have a list like so:
List list = [ [item: "foo", count: 5],
[item: "bar", count: 3] ]
Then there are multiple ways of doing it. The most readable is probably
int a = list.count.sum()
Or you could use the Closure form of sum on the whole list
int b = list.sum { it.count }
Or you could even use a more complex route such as inject
int c = list.count.inject { tot, ele -> tot + ele } // Groovy 2.0
// c = list.count.inject( 0 ) { tot, ele -> tot + ele } // Groovy < 2.0
All of these give the same result.
assert ( a == b ) && ( b == c ) && ( c == 8 )
I would use the first one.
You want to use the collect operator. I checked the following code with groovysh:
list1 = []
total = 0
list1[0] = [item: "foo", count: 5]
list1[1] = [item: "bar", count: 3]
list1.collect{ total += it.count }
println "total = ${total}"
First of all, you're confusing map and list syntax in your example. Anyhow, Groovy injects a .sum(closure) method to all collections.
Example:
[[a:1,b:2], [a:5,b:4]].sum { it.a }
===> 6

Resources