Group and sum collection in Groovy - grails

I have a collection of objects that I want to group by month and name and sum total:
def things = [
[id:1, name:"fred", total:10, date: "2012-01-01"],
[id:2, name:"fred", total:10, date: "2012-01-03"],
[id:3, name:"jane", total:10, date: "2012-01-04"],
[id:4, name:"fred", total:10, date: "2012-02-11"],
[id:5, name:"jane", total:10, date: "2012-01-01"],
[id:6, name:"ted", total:10, date: "2012-03-21"],
[id:7, name:"ted", total:10, date: "2012-02-09"]
];
I would like the output to be:
[
"fred":[[total:20, month:"January"],[total:10, month:"February"]],
"jane":[[total:20,month:"January"]],
"ted" :[[total:10, month:"February"],[total:10, month:"March"]]
]
or something along those lines. What is the best way to accomplish this using groovy/grails?

The following lines
things.inject([:].withDefault { [:].withDefault { 0 } } ) {
map, v -> map[v.name][Date.parse('yyyy-MM-dd', v.date).format('MMMM')] += v.total; map
}
will give you this result:
[fred:[January:20, February:10], jane:[January:20], ted:[March:10, February:10]]
(works with Groovy >= 1.8.7 and 2.0)

I ended up with
things.collect {
// get the map down to name, total and month
it.subMap( ['name', 'total' ] ) << [ month: Date.parse( 'yyyy-MM-dd', it.date ).format( 'MMMM' ) ]
// Then group by name first and month second
}.groupBy( { it.name }, { it.month } ).collectEntries { k, v ->
// Then for the names, collect
[ (k):v.collectEntries { k2, v2 ->
// For each month, the sum of the totals
[ (k2): v2.total.sum() ]
} ]
}
To get the same result as Andre's much shorter, much better answer ;-)
Edit
bit shorter, but still not as good...
things.groupBy( { it.name }, { Date.parse( 'yyyy-MM-dd', it.date ).format( 'MMMM' ) } ).collectEntries { k, v ->
[ (k):v.collectEntries { k2, v2 ->
[ (k2): v2.total.sum() ]
} ]
}

Here's a solution to do the same thing as the other solutions, but in parallel using GPars. There may be a tighter solution, but this one does work with the test input.
#Grab(group='org.codehaus.gpars', module='gpars', version='1.0.0')
import static groovyx.gpars.GParsPool.*
//def things = [...]
withPool {
def mapInner = { entrylist ->
withPool{
entrylist.getParallel()
.map{[Date.parse('yyyy-MM-dd', it.date).format('MMMM'), it.total]}
.combine(0) {acc, v -> acc + v}
}
}
//for dealing with bug when only 1 list item
def collectSingle = { entrylist ->
def first = entrylist[0]
return [(Date.parse('yyyy-MM-dd', first.date).format('MMMM')) : first.total]
}
def result = things.parallel
.groupBy{it.name}.getParallel()
.map{ [(it.key) : (it.value?.size())>1?mapInner.call(it.value):collectSingle.call(it.value) ] }
.reduce([:]) {a, b -> a + b}
println "result = $result"
}

Related

Gatling: How to pass jsonPath saved variable to another exec

I am new to Gatling and scala. facing issue on passing jsonpath saved variable from one repeat section to another forEach repeat section.
following variable "dcIds" is not able to pass to forEach section. Also please direct me to make the below code more better.
var dcIdd = ""
val r = new scala.util.Random
def orderRef() = r.nextInt(100)
def getCreateRequest: String = {
val data = s"""
[{
"name":"DC_${orderRef()}",
"location":"Seattle, Washington, USA",
"type":"Colocation"
}]
""".stripMargin
data
}
def createAppRequest: String = {
val data = s"""
[{
"name":"App_${orderRef()}",
"owner":"a#a.com",
"dataCenterId":"${dcIdd}",
"strategy":"Rehost",
"migrationStrategy":"Rehost"}]
}]
""".stripMargin
data
}
val scn = scenario("Add DC")
.repeat(DcIterations, "index") {
exec(
http("List_plans")
.get(uri2 + "?plan_id=")
.headers(headers_sec)
.resources(
http("DC add")
.post(uri2)
.headers(headers_sec)
.body(StringBody(session => getCreateRequest))
.check(jsonPath("$.ids[*]").findAll.saveAs("dcIds"))))
}
.foreach("${dcIds}", "dcId") {
dcIdd = "${dcId}"
repeat(AppIterations, "index") {
exec(http("Add Application")
.post(uri1 + "/applications/${dcId}")
.headers(headers_sec)
.body(StringBody(session => createAppRequest))
)
}
}

Display Date and time on highcharts

I have the following data
firstData = [
["2019-11-24", "12:38:54"],
["2019-11-21", "07:06:29"],
["2019-11-20", "19:26:37"],
["2019-09-26", "19:56:00"] ]
secondData = [
["2019-09-26", "10:26:00"],
["2019-11-20", "06:52:34"],
["2019-11-21", "07:06:19"],
["2019-11-24", "07:38:54"] ]
I would like to display graph like this.
date and time graph
So as I mentioned in the comment the y value must be a number. If you want to render it as a time format you will need to convert this string into a proper number, check the function below and the demo: https://jsfiddle.net/BlackLabel/2vc7aphd/1/
function parseToNumber(string) {
return Date.parse("1-1-1 " + string) - Date.parse('1-1-1 00:00:00')
}
function parseData(data) {
let output = [];
data.forEach(d => {
let x = d[0],
y = d[1];
output.push({
name: x,
y: parseToNumber(y),
label: y
});
})
return output;
}

Combine two Groovy maps and get differences [duplicate]

This question already has an answer here:
Compare two maps and find differences using Groovy or Java
(1 answer)
Closed 6 years ago.
I have two maps and I would like to combine this maps into new map with the differences. I mean with differences is if startDate or appId are different for that cuInfo and service.
Notice: The attributes cuInfo and service are unique in both maps.
Map 1:
[
[name:"Apple",cuInfo:"T12",service:"3",startDate:"14-02-16 10:00",appId:"G12351"],
[name:"Apple",cuInfo:"T13",service:"3",startDate:"14-01-16 13:00",appId:"G12352"],
[name:"Apple",cuInfo:"T16",service:"3",startDate:"14-01-16 13:00",appId:"G12353"],
[name:"Google",cuInfo:"T14",service:"9",startDate:"10-01-16 11:20",appId:"G12301"],
[name:"Microsoft",cuInfo:"T15",service:"10",startDate:"26-02-16 10:20",appId:"G12999"]
]
Map 2:
[
[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"]
]
I would like to get map below as final result:
[
[name:"Apple",cuInfo:"T12",service:"3",startDate:"14-02-16 10:00",appId:"G12351",startDate2:"14-01-16 13:22",appId2:"G12355"],
[name:"Apple",cuInfo:"T13",service:"3",startDate:"14-01-16 13:00",appId:"G12352",startDate2:"12-02-16 13:00",appId2:"G12356"],
[name:"Google",cuInfo:"T14",service:"9",startDate:"10-01-16 11:20",appId:"G12301",startDate2:"10-01-16 11:20",appId2:"G12300"]
]
I tried the code below but it gives not what I want:
def total = [list1: list1, list2: list2].collectEntries { label, maps ->
[(label): maps.countBy { it.iccId} ]
}.inject([:]) { result, label, counts ->
counts.entrySet().each { entry ->
if(!result[entry.key]) result[entry.key] = [:]
result[entry.key][(label)] = entry.value
}
result
}.collect { iccId , counts -> [iccId: iccId] << counts }
The code above give me this, it merge the maps and count the cuInfos but I'm not able to get the differences:
[
[cuInfo:T12, list1:1, list2:1], [cuInfo:T13, list1:1, list2:1], [cuInfo:T14, list1:1, list2:1], [cuInfo:T15, list1:1, list2:1],[cuInfo:T16, list1:1]
]
Please who can help to get it work please with example. Thanks
Is that what you're looking for:
def col1 = [
[name:"Apple",cuInfo:"T12",service:"3",startDate:"14-02-16 10:00",appId:"G12351"],
[name:"Apple",cuInfo:"T13",service:"3",startDate:"14-01-16 13:00",appId:"G12352"],
[name:"Apple",cuInfo:"T16",service:"3",startDate:"14-01-16 13:00",appId:"G12353"],
[name:"Google",cuInfo:"T14",service:"9",startDate:"10-01-16 11:20",appId:"G12301"],
[name:"Microsoft",cuInfo:"T15",service:"10",startDate:"26-02-16 10:20",appId:"G12999"]
]
def col2 = [
[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"]
]
col1
.findAll { it.cuInfo in col2.cuInfo && !(it.subMap('cuInfo', 'service', 'startDate', 'appId') in col2) }
.collect {
def e = col2.find { i2 -> it.cuInfo == i2.cuInfo }
it << [startDate2: e.startDate, appId2: e.appId]
}
.each {
println it
}
?

How to access fields of a value bound to a variable in a match expression?

I have code similar to:
use std::string::{String};
use std::vec::{Vec};
enum State {
A {
n: usize,
lines: Vec<String>,
},
B {
n: usize,
}
}
fn main() {
use State::*;
let lines = vec!["a", "b", "GO", "c", "GO", "d"];
let mut state = B { n: 0 };
for line in &lines {
state = match state {
A { n, lines } => {
if line == &"GO" {
B { n: n + 1 }
} else {
let mut new_lines = Vec::from(lines);
new_lines.push(line.to_string());
A { n: n, lines: new_lines }
}
},
B { n } => {
A { n: n, lines: vec![line.to_string()] }
},
};
}
let final_n = match state {
A { n, .. } => n,
B { n } => n,
};
println!("final_n = {}", final_n);
}
Rust Playground link: http://is.gd/0QTYaQ
(Note that this is a simplification of the actual code. See the first revision of this question for the full background.)
I want to avoid creating the new_lines vector, so I tried binding the State::A value to a variable and accessing the fields of the value like so:
s # A { .. } => {
if line == &"GO" {
B { n: s.n + 1 }
} else {
s.lines.push(line.to_string());
s
}
},
However, this fails to compile:
ParseState_enum_test.rs:23:28: 23:31 error: attempted access of field `n` on type `State`, but no field with that name was found
ParseState_enum_test.rs:23 B { n: s.n + 1 }
^~~
ParseState_enum_test.rs:19:5: 33:6 note: in this expansion of for loop expansion
ParseState_enum_test.rs:25:21: 25:28 error: attempted access of field `lines` on type `State`, but no field with that name was found
ParseState_enum_test.rs:25 s.lines.push(line.to_string());
^~~~~~~
ParseState_enum_test.rs:19:5: 33:6 note: in this expansion of for loop expansion
error: aborting due to 2 previous errors
How do I access the fields of the value bound to the variable?
EDIT: I am aware of ref mut in pattern binding, but I don't think that this is a good solution in my case. If I use ref mut, then I need to create a clone of the vector because this code does not compile:
A { n, ref mut lines } => {
if line == &"GO" {
B { n: n + 1 }
} else {
lines.push(line.to_string());
A {
n: n,
lines: lines, // error: mismatched types
}
}
},
The following seems to work. Does it solve the problem?
let new_state = match state {
B {n} => A { n: n, lines: vec![line.to_string()] },
A {n, mut lines} => {
match *line {
"GO" => B { n: n + 1 },
_ => {
lines.push(line.to_string());
A{ n:n, lines: lines}
}
}
}
};
state = new_state
https://play.rust-lang.org/?gist=4fa712834999e45ccd4d&version=stable
Let's look at a much simpler version of your issue:
enum Foo {
Alpha { score: u8 },
Beta { lives_left: u8 },
}
fn main() {
let the_value = Foo::Alpha { score: 42 };
match the_value {
alpha_only # Alpha => println!("Score is {}", alpha_only.score),
_ => println!("Dunno what to do!"),
}
}
The problem is that enum variants are not standalone types. That is, there is no way to have a variable of type Foo::Alpha; you can only have it be the type Foo. You can see this in the error message:
attempted access of field score on type Foo, but no field with that name was found
When you use # to bind the entire pattern, you can only know that you are getting something of type Foo.
The normal way of dealing with this is to bind to a component of the item using ref:
match the_value {
Foo::Alpha { ref score } => println!("Score is {}", score),
_ => println!("Dunno what to do!"),
}
And if you need to mutate the value, use ref mut:
match the_value {
Foo::Alpha { ref mut score } => {
*score += 1;
println!("Score is {}", score)
},
_ => println!("Dunno what to do!"),
}
Of if you can consume the value, you don't need ref:
let the_value = Foo::Alpha { score: 42 };
let new_value = match the_value {
Foo::Alpha { score } => Foo::Alpha { score: score + 1 },
Foo::Beta { lives_left } => Foo::Alpha { score: lives_left * 2 },
};

Getting info on Groovy functions (name, signature, body code)

I have a Groovy file containing a bunch of simple functions like so:
// useful functions
def myFunc1(String arg) {
println("Hello " + arg)
}
def myFunc2(String arg) {
println("Goodbye " + arg)
}
I'd like to obtain from this:
the method name
the arguments
the body code of the function
(All as simple strings, I don't need to run anything yet.)
I was about to resort to some Regexing, but since I'm using a JVM language (Scala) I figured I might be able to use some of the Groovy compiler's stuff to do this a "nicer" way.
There seems to be a fair bit of information on loading Groovy code dynamically and running it, but not so much on introspecting the source. Any ideas?
(Failing a "nice" way, I'll also accept some Scala-foo to parse the information in a succinct fashion.)
This works, and demonstrates the token types required to find each node of importance in the AST. Hope it makes sense... By using lots of Groovy dynamism, I hope I haven't made it too hard for a port to Scala :-(
import org.codehaus.groovy.antlr.*
import org.codehaus.groovy.antlr.parser.*
import static org.codehaus.groovy.antlr.parser.GroovyTokenTypes.*
def code = '''
// useful functions
def myFunc1(String arg) {
println("Hello " + arg)
}
def myFunc2(arg, int arg2) {
println("Goodbye " + arg)
}
public String stringify( int a ) {
"$a"
}
'''
def lines = code.split( '\n' )
// Generate a GroovyRecognizer, compile an AST and assign it to 'ast'
def ast = new SourceBuffer().with { buff ->
new UnicodeEscapingReader( new StringReader( code ), buff ).with { read ->
read.lexer = new GroovyLexer( read )
GroovyRecognizer.make( read.lexer ).with { parser ->
parser.sourceBuffer = buff
parser.compilationUnit()
parser.AST
}
}
}
// Walks the ast looking for types
def findByPath( ast, types, multiple=false ) {
[types.take( 1 )[ 0 ],types.drop(1)].with { head, tail ->
if( tail ) {
findByPath( ast*.childrenOfType( head ).flatten(), tail, multiple )
}
else {
ast*.childrenOfType( head ).with { ret ->
multiple ? ret[ 0 ] : ret.head()[0]
}
}
}
}
// Walk through the returned ast
while( ast ) {
def methodModifier = findByPath( ast, [ MODIFIERS ] ).firstChild?.toStringTree() ?: 'public'
def returnType = findByPath( ast, [ TYPE, IDENT ] ) ?: 'Object'
def methodName = findByPath( ast, [ IDENT ] )
def body = findByPath( ast, [ SLIST ] )
def parameters = findByPath( ast, [ PARAMETERS, PARAMETER_DEF ], true ).collect { param ->
[ type: findByPath( param, [ TYPE ] ).firstChild?.toStringTree() ?: 'Object',
name: findByPath( param, [ IDENT ] ) ]
}
def (y1,y2,x1,x2) = [ body.line - 1, body.lineLast - 1, body.column - 1, body.columnLast ]
// Grab the text from the original string
def snip = [ lines[ y1 ].drop( x1 ), // First line prefix stripped
*lines[ (y1+1)..<y2 ], // Mid lines
lines[ y2 ].take( x2 ) ].join( '\n' ) // End line suffix stripped
println '------------------------------'
println "modifier: $methodModifier"
println "returns: $returnType"
println "name: $methodName"
println "params: $parameters"
println "$snip\n"
// Step to next branch and repeat
ast = ast.nextSibling
}
It prints out:
------------------------------
modifier: public
returns: Object
name: myFunc1
params: [[type:String, name:arg]]
{
println("Hello " + arg)
}
------------------------------
modifier: public
returns: Object
name: myFunc2
params: [[type:Object, name:arg], [type:int, name:arg2]]
{
println("Goodbye " + arg)
}
------------------------------
modifier: public
returns: String
name: stringify
params: [[type:int, name:a]]
{
"$a"
}
Hope it helps, or points you in the right direction :-)

Resources