How to Merge two or more Objects in a ObjectsList - groovy

I have would like to know if there is a way to merge two ( or more ) objects in one list.
Exemple:
I have this class:
class User {
String name
Integer age
Integer score
}
and I got this method on another class
methodTest() {
User a = new User().with{
it.name = "JACK"
it.age = 20
}
User b = new User().with{
it.name = "JACK"
it.score = 50
}
User c = new User().with{
it.name = "TONY"
it.age = 25
}
User d = new User().with{
it.name = "TONY"
it.score = 30
}
List userList = new ArrayList()
userList.add(a)
userList.add(b)
userList.add(c)
userList.add(d)
}
Tere is a way to get a userList merged by name? Something like :
userList = userList.mergeBy(it.name)
and then get a list of Users with:
[{name:"Jack", age: 20 , score: 50},{name:"TONY", age: 25, score: 30}]

You can use .groupBy to group your list by User.name and then transform it to a List<User> by applying .inject function. Below you can find an example (fixed version the code you have shown us):
import groovy.json.JsonOutput
class User {
String name
Integer age
Integer score
}
User a = new User(name: "JACK", age: 20)
User b = new User(name: "JACK", score: 50)
User c = new User(name: "TONY", age: 25)
User d = new User(name: "TONY", score: 30)
List userList = new ArrayList()
userList.add(a)
userList.add(b)
userList.add(c)
userList.add(d)
List<User> users = userList.groupBy { it.name } // (1)
.values() // (2)
.inject([]) { result, users -> // (3)
result << users.inject(new User()) { User merged, User user -> // (4)
merged.name = user.name ?: merged.name
merged.age = user.age ?: merged.age
merged.score = user.score ?: merged.score
return merged
}
}
println JsonOutput.toJson(users)
Let's see what happens here step-by-step:
(1) userList.groupBy { it.name } produces following map:
[JACK:[User(JACK, 20, null), User(JACK, null, 50)], TONY:[User(TONY, 25, null), User(TONY, null, 30)]]
(2) calling .values() on this map returns a list of list of users:
[[User(JACK, 20, null), User(JACK, null, 50)], [User(TONY, 25, null), User(TONY, null, 30)]]
(3) then .inject([]) { result, users -> /* ... */ } collects every list of users, applies transformation and adds result to result list (we start with empty [] here)
(4) here we call another .inject() function on users list (this users list contains a list of users with same name, e.g. [JACK:[User(JACK, 20, null), User(JACK, null, 50)]). We start with a new "empty" user (.inject(new User())). We access it by merged variable inside the closure - this variable holds the last result of each iteration inside .inject() function. So it starts with this empty user, gets the first one, sets the name and age (score is not set, because it is null), then it gets second user, sets name (same one) and score (age is not set, because in this user has null age). Final User is added to result list using left shift operator <<.
Eventually when you print to console your final users list you will see desired output:
[{"age":20,"score":50,"name":"JACK"},{"age":25,"score":30,"name":"TONY"}]
Final note
Of course you can make this code even simple, e.g. you can add a method to User class that merges two user instances, something like:
import groovy.json.JsonOutput
class User {
String name
Integer age
Integer score
User merge(User user) {
return new User(
name: user.name ?: name,
age: user.age ?: age,
score: user.score ?: score
)
}
}
List<User> userList = [
new User(name: "JACK", age: 20),
new User(name: "JACK", score: 50),
new User(name: "TONY", age: 25),
new User(name: "TONY", score: 30)
]
List<User> users = userList.groupBy { it.name }
.values()
.inject([]) { result, users ->
result << users.inject(new User()) { User merged, User user -> merged.merge(user) }
}
println JsouOutput.toJson(users)

Using some simple groovy magic:
class User{
String name
Integer age
Integer score
String toString(){ "$name:$age:$score" }
}
User a = new User(
name:"JACK",
age : 20
)
User b = new User(
name : "JACK",
score :50
)
User c = new User(
name : "TONY",
age : 25
)
User d = new User(
name : "TONY",
score : 30
)
List userList = [ a, b, c, d ]
def mergedList = userList.inject( [:].withDefault{ new User() } ){ res, User u ->
res[ u.name ].name = u.name
if( u.age ) res[ u.name ].age = u.age
if( u.score ) res[ u.name ].score = u.score
res
}.values()
assert '[JACK:20:50, TONY:25:30]' == mergedList.toString()

Related

AzureDevOps - Creating WorkItem via JsonPatchOperation

To create a workitem I need to specify its fields but where exactly can I see all the possible "field paths" on my AzureDevOps site?
I've edited an existing workitem and added some more fields to it but I cant seem to find the needed "field path" for my JsonPatchOperation.
Any ideas? Thanks in advance!
public static WorkItem CreateWorkItem(VssConnection connection, string title, string type, string description, string tags)
{
string project = "xxx";
// Construct the object containing field values required for the new work item
JsonPatchDocument patchDocument = new JsonPatchDocument();
patchDocument.Add(
new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/System.Title", <-- field path
Value = title
}
);
patchDocument.Add(
new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/System.Description", <-- field path
Value = description
}
);
// Get a client
WorkItemTrackingHttpClient workItemTrackingClient = connection.GetClient<WorkItemTrackingHttpClient>();
// Create the new work item
WorkItem newWorkItem = workItemTrackingClient.CreateWorkItemAsync(patchDocument, project, type).Result;
Console.WriteLine("Created work item ID {0} {1}", newWorkItem.Id, newWorkItem.Fields["System.Title"]);
return newWorkItem;
}
You can use the process template editor to see all fields in your org.
Install Process Editor to VS:
Open Fields Browser:
Check needed fields:
Another way: using the rest api.
WorkItemTrackingProcessHttpClient ProcessHttpClient = Connection.GetClient<WorkItemTrackingProcessHttpClient>();
string processName = "My New Process"; //existing process
string witName = "Task"; //existing work item type
Guid procId;
string witRefName;
GetProcAndWIT(processName, witName, out procId, out witRefName);
ShowCurrentFields(procId, witRefName);
private static void ShowCurrentFields(Guid procId, string witRefName)
{
var fields = ProcessHttpClient.GetAllWorkItemTypeFieldsAsync(procId, witRefName).Result;
Console.WriteLine("{0, -20} : {1, -40} : {2, -10} : {3, -8} : {4, -8} : {5, -8}",
"Name", "Reference Name", "Type", "Required", "ReadOnly", "Default");
foreach (var field in fields)
{
Console.WriteLine("------------------------------------------------------------------------------------------------------------");
Console.WriteLine("{0, -20} : {1, -40} : {2, -10} : {3, -8} : {4, -8} : {5, -8}",
field.Name, field.ReferenceName, field.Type, field.Required, field.ReadOnly, field.DefaultValue);
}
Console.WriteLine("------------------------------------------------------------------------------------------------------------\n\n\n\n");
}
private static void GetProcAndWIT(string processName, string witName, out Guid procId, out string witRefName)
{
procId = GetProcessGuid(processName);
if (procId == null)
{
throw new Exception("Can not find process.");
}
witRefName = GetWITrefName(procId, witName);
if (string.IsNullOrEmpty(witRefName))
{
throw new Exception("Can not find work item type.");
}
}
private static Guid GetProcessGuid(string processName)
{
Guid newProcessGuid = Guid.Empty;
var processes = ProcessHttpClient.GetListOfProcessesAsync().Result;
return (from p in processes where p.Name == processName select p.TypeId).FirstOrDefault();
}
private static string GetWITrefName(Guid procGuid, string witName)
{
var wiTypes = ProcessHttpClient.GetProcessWorkItemTypesAsync(procGuid).Result;
return (from p in wiTypes where p.Name == witName select p.ReferenceName).FirstOrDefault();
}

groovy.lang.MissingMethodException: No signature of method

I am getting the following error -
groovy.lang.MissingMethodException: No signature of method: Script64$_run_closure5_closure7_closure8_closure9_closure10_closure11.doCall() is applicable for argument types: (java.lang.String) values: Possible solutions: doCall(java.lang.Object, java.lang.Object), isCase(java.lang.Object), isCase(java.lang.Object) error at line:
Code - EDIT
import groovy.xml.*
List tempList = []
List listgenerated = []
def count = 0
for (a in 0..totalCount-1)
{
//nameList and valueList lists will have all the contents added as below commented pseudo code
/*for (b in 0..50)
{
nameList.add(b,number) // number is some calculated value
valueList.add(b,number)
e.g. nameList=[name1, name2, name3,name4, name5]
valueList =[val1, val2, val3, , val5]
listgenerated should be = [[name1:val1, name2:val2], [name3:val3, name4: , name5:val5]]
} */
tempList = []
for (j in count..nameList.size())
{
count = j
def nameKey = nameList[j]
def value
if (nameKey != null)
{
value = valueList[j]
tempList << [(nameKey) : value]
}
}
count = count
listgenerated.putAt(a,tempList)
number = number +1
}
def process = { binding, element, name ->
if( element[ name ] instanceof Collection ) {
element[ name ].each { n ->
binding."$name"( n )
}
}
else if( element[ name ] ) {
binding."$name"( element[ name ] )
}
}
class Form {
List fields
}
def list = [[ name:'a', val:'1' ], [ name:'b', val :'2', name2:4, xyz:'abc', pqr:'']] //Edited list
f = new Form( fields: list ) //Works fine
f = new Form( fields: listgenerated ) //Gives the above error
String xml = XmlUtil.serialize( new StreamingMarkupBuilder().with { builder ->
builder.bind { binding ->
data {
f.fields.each { fields ->
item {
fields.each { name, value ->
process( binding, fields, name )
}
}
}
}
}
} )
If while creating the "listgenerated" single quotes are added around values it takes it as character and while printing both lists seem different.
I am unable to figure it out what exactly is going wrong. Any help is appreciated. Thanks.
Ref - Groovy: dynamically create XML for collection of objects with collections of properties
I believe, where you do:
//some loop to add multiple values to the list
listgenerated << name+":"+value
You need to do:
//some loop to add multiple values to the list
listgenerated << [ (name): value ]
And add a map to the list rather than a String. It's hard to say though as your code example doesn't run without alteration, and I don't know if it's the alterations that are solving the problem

Grails 2.1 How Can i add value in join table

I create 2 tables one Category and one Manufacturer, and There relationship is Many-to-Many,
So i use a join table,
I insert values into two tables individually. Now i want to join two table by their id, but i cannot do, Can you help me....
When i try to insert value in join table give an exception, here is the exception:
Cannot invoke method addToManufacturers() on null object. Stacktrace follows:
java.lang.NullPointerException: Cannot invoke method addToManufacturers() on null object
here is my domain class for Category
static hasMany = [manufacturers: Manufacturer]
static constraints = {
name blank: false, size: 0..60, unique: false
}
static mapping = {
table 't01i001'
id column: 'f_category_id'
name column: 'f_name', length: 30
version column: 'f_revision'
manufacturers joinTable: [name: 't01j001', key: 'k_category_id', column: 'k_manufacturer_id']
}
here is my domain class for manufacturer
static belongsTo = Category
static hasMany = [categories: Category]
static constraints = {
name blank: false, size: 0..60, unique: false
}
static mapping = {
table 't01i002'
id column: 'f_manufacturer_id'
name column: 'f_name', length: 30
version column: 'f_revision'
categories joinTable: [name: 't01j001', key: 'k_manufacturer_id', column: 'k_category_id']
}
add my controller where i try to insert
def manuInsertInCat(){
def message, flag,count=0,categories = []
int catid = params.containsKey("catid") ? params.catid : '0'
int manuid = params.containsKey("manuid") ? params.manuid : '0'
def category = Category.get(catid);
def manufacture = Manufacturer.get(manuid)
category.addToManufacturers(manufacture)
message = "Successfully Loaded"
count++
flag =true
render Category.getJsonData(categories, count, flag, message)
}
At last i complete my job by this process its works fine.
def catInsertInManu(){
def message, flag,count=0,manufacturers = []
String catid = params.containsKey("catid") ? params.catid : '0'
String manuid = params.containsKey("manuid") ? params.manuid : '0'
def category = Category.get(catid)
def manufacture = Manufacturer.get(manuid)
manufacture.addToCategories(category)
def m01i001001s = []
manufacture.categories.each{ cat ->
m01i001001s << [id:cat.id, name:cat.name]
}
manufacturers << [id: manufacture.id, name:manufacture.name, m01i001001s:m01i001001s]
message = "Successfully Loaded"
count++
flag =true
render Manufacturer.getJsonData(manufacturers, count, flag, message)
}

groovy change bean variables using properties

Is there a way in groovy to do something like:
class Person{
def name, surname
}
public void aMethod(anoherBean){
def bean = retrieveMyBean()
p.properties = anoherBean.properties
}
The property properties is final, is there another way to do this shortcut?
properties is a virtual property; you have to call the individual setters. Try this:
def values = [name: 'John', surname: 'Lennon']
for( def entry : values.entries() ) {
p.setProperty( entry.getKey(), entry.getValue() );
}
Or, using MOP:
Object.class.putAllProperties = { values ->
for( def entry : values.entries() ) {
p.setProperty( entry.getKey(), entry.getValue() );
}
}
Person p = new Person();
p.putAllProperties [name: 'John', surname: 'Lennon']
[EDIT] To achieve what you want, you must loop over the properties. This blog post describes how to do that:
def copyProperties(def source, def target){
target.metaClass.properties.each{
if (source.metaClass.hasProperty(source, it.name) && it.name != 'metaClass' && it.name != 'class')
it.setProperty(target, source.metaClass.getProperty(source, it.name))
}
}
If you don't have any special reason then just use named parameters
def p = new Person(name: 'John', surname: 'Lennon')
After question being updated
static copyProperties(from, to) {
from.properties.each { key, value ->
if (to.hasProperty(key) && !(key in ['class', 'metaClass']))
to[key] = value
}
}

Detect Change in EntityFrameWork

In my current project I need write in a table all values are changed in the application.
Ex. the guy update the UserName, I need put in a table UserName old value "1" new value "2".
I tried use the ObjectStateEntry but this return all fields. I think the FW return all because my code.
public USER Save(USER obj)
{
using(TPPTEntities db = new TPPTEntities())
{
db.Connection.Open();
USER o = (from n in db.USERs where n.ID == obj.ID select n).FirstOrDefault();
if (o == null)
{
o = new USER()
{
BruteForce = 0,
Email = obj.Email,
IsBlock = false,
LastLogin = DateTime.Now,
Name = obj.Name,
UserName = obj.UserName,
UserPassword = new byte[0],
};
db.AddToUSERs(o);
}
else
{
o.Email = obj.Email;
o.Name = obj.Name;
o.UserName = obj.UserName;
}
db.SaveChanges();
db.Connection.Close();
}
return obj;
}
A way to get old and new values is this:
var ose = this.ObjectStateManager.GetObjectStateEntry(o.EntityKey);
foreach (string propName in ose.GetModifiedProperties())
{
string.Format("Property '{0}', old value: {1}, new value: {2}",
propName, ose.OriginalValues[propName], ose.CurrentValues[propName]);
}
This is pretty useless, of course, but I'm sure you'll know what to do in the foreach loop to store the changes.
Is this a WCF Service? In that case, the changes will probably never come trough since changes to the Object Graph are made where the Object Context is not available. Consider using Self-Tracking Entities

Resources