Compare two list in javers - javers

I have a question regarding list comparator. I have a web application which have page with table.
I can edit data in this table and also delete row.
Standard comparator working correct when i edit data but i have problem when i remove row.
The problem is common (i think) when i remove e.g one row what is happening
javers comparing old list with now one it's looks something like this:
old list have two object now list have one object (i removed first one) now javers don't know which object
was removed and he compare first object from old list with second object from new list and for him
whole object was changed and that is not true.
My thought was I will wrote own list comparator and in this comparator before javers compare method will be colled
i check if comparing objects have the same ID.
Unfortunately i have problem to obtain object ID.
My comparator looks like this
public class ListComperator implements CustomPropertyComparator {
public ValueChange compare(List list1, List list2, GlobalId globalId, Property property) {
for (Object o1 : list1) {
for (Object o2 : list2) {
if(o1.getId().equals(o2.getId()) ) {
javers.compare(o1, o2);
}
}
}}}
The second list "list2" have my object from which i can get any property e.g ID.
The firs list "list1" is list with some ValueObjectId and i don't know how to get property with id from object o1.
Is there a way to get this information or maybe i'm doing something totally wrong please help.

I suppose that you have ValueObjects stored in those Lists? Since ValueObjects has no identifiers, there is no way to detect how they 'move' between List indexes when List is changed. Custom comparator won't help much without a
way to identify you objects.
I suggest to add some kind of identifiers to your objects stored in a List and map them as Entities (use #Id annotation).

Related

Spring Sorting with Nulls_Last Handling not working

I created a sort rule like this for sorting my list:
Sort sort = new Sort(new Sort.Order(Sort.Direction.ASC,"productOrder", Sort.NullHandling.NULLS_LAST), new Sort.Order(Sort.Direction.ASC,"producedYear", Sort.NullHandling.NULLS_LAST));
with this rule I want to sort the productOrder first, and then if the productOrder is the same then the producedYear will be compared and sorted. If there are null values presented, it should be sorted at the end of the list. productOrder will have type Long and producedYear will have type Double.
My Repository interface extends the JpaRepository:
public interface ProductRepository extends JpaRepository<Product, String> {
List<Product> findByDisabledAndValid(int disabled, int valid, Sort sort);
}
But the sorted list I received is containing the null values always at the beginning of the list. This means the null values will be sorted first, then come the productOrder, and finally the producedYear will be sorted. It seems that the third parameter that I defined on my Sort.Order method is not working.
Does anyone have an idea why? Thank you very much
Instead of using the Sort.NullHandling.NULLS_LAST integrated with Spring, I solved the above problem by creating my own Sort function, like this:
Collections.sort(resultList, Comparator
.comparing(Product::getProductOrder, Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(Product::getProducedYear, Comparator.nullsLast(Comparator.naturalOrder())));
Hope this help if someone also faces the same problem.
Regards!

Realm Xamarin LINQ Select

Is there a way to restrict the "columns" returned from a Realm Xamarin LINQ query?
For example, if I have a Customer RealmObject and I want a list of all customer names, do I have to query All<Customer> and then enumerate the results to build the names list? That seems cumbersome and inefficient. I am not seeing anything in the docs. Am I missing something obvious here? Thanks!
You have to remember that Realm is an object based store. In a RDBMS like Sqlite, restricting the return results to a sub-set of "columns" of an "record" makes sense, but in an object store, you would be removing attributes from the original class and thus creating a new dynamic class to then instantiate these new classes as objects.
Thus is you want just a List of strings representing the customer names you can do this:
List<string> names = theRealm.All<Customer>().ToList().Select(customer => customer.Name).ToList();
Note: That you take the Realm.All<> results to a List first and then using a Linq Select "filter" just the property that you want. Using a .Select directly on a RealmResults is not currently supported (v0.80.0).
If you need to return a complex type that is a subset of attributes from the original RealObject, assuming you have a matching POCO, you can use:
var custNames = theRealm.All<Customer>().ToList().Select((Customer c) => new Name() { firstName = c.firstName, lastName = c.lastName } );
Remember, once you convert a RealmResult to a static list of POCOs you do lose the liveliness of using RealmObjects.
Personally I avoid doing this whenever possible as Realm is so fast that using a RealmResult and thus the RealObjects directly is more efficient on processing time and memory overhead then converting those to POCOs everytime you need to new list...

Java 8 list manipulation

I'm trying to do some basic map/filter operations on a list in a ListChangeListener.onChanged(Change<? extends Place>) method and I can get it working using the old-fashioned "iterate and do some ifs" way, but I wanted to try to write it using the stream() method from java 8. The commented part doesn't give the same result though, it fails to filter out the categories correctly (and yes, I have a working implementation of equals(Object) for Category
for (Place p : change.getAddedSubList()) {
if (!categories.contains(p.getCategory())) {
categories.add(p.getCategory());
}
}
// List<Category> addedCategories = change.getAddedSubList().stream()
//                      .map(Place::getCategory)
//                      .filter((c) -> { return !categories.contains(c); })
//   .collect(Collectors.toList());
// categories.addAll(addedCategories);
That's because in the first version, once you have added a category to the list, a subsequent occurrence of this category isn't added a second time: you have already added it to the list. The second version doesn't do the same thing. So you need to make sure categories are unique in the stream:
change.getAddedSubList().stream()
.map(Place::getCategory)
.distinct()
.filter(c -> !categories.contains(c))
.forEachOrdered(c -> categories.add(c));
Note that you also don't need to collect to a temporary list.
Duplicates in you stream may lead to duplicates in the categories list, when they are not contained in the categories list beforehand, since the filter method is applied for all items, before one of them is inserted.
One solution would be to insert a call to .distinct() in your Stream, another way to collect via Collectors.toSet().

Converting Object to Class object

in my Spring MVC project i m using Hibernate, by using Criteria API i am applying Group BY and Order BY clause. Query get executed on DB successfully and it brings result also but its an array of Object--
Here is code of Criteria API
Criteria criteria = session.createCriteria(DashboardSubindicatorSubmission.class, "DashboardSubindicatorSubmission")
.setProjection(Projections.projectionList()
.add(Projections.sum("InputValue").as("InputValue"))
.add(Projections.groupProperty("fkAccademicYearId"))
.add(Projections.groupProperty("fkAssessmentPlanID"))
.add(Projections.groupProperty("fkSubindicatorID"))
.add(Projections.groupProperty("InputTitle")))
.addOrder(Order.asc("fkAccademicYearId"))
.addOrder(Order.asc("fkAssessmentPlanID"))
.addOrder(Order.asc("InputTitle"));
List<DashboardSubindicatorSubmission> dashboardSubindicatorSubmissionList = (List<DashboardSubindicatorSubmission>)criteria.list();
session.flush();
transaction.commit();
return dashboardSubindicatorSubmissionList;
I am casting criteria.list() to List<DashboardSubindicatorSubmission> but when i try to do dashboardSubindicatorSubmissionList.get(i) on controller it gives me exception java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to mkcl.accreditation.model.DashboardSubindicatorSubmission.
i come to know that, though i m casting it to List<DashboardSubindicatorSubmission> still its an list of object[] thats why i cant do dashboardSubindicatorSubmissionList.get(i) because it returns me object of DashboardSubindicatorSubmission. (Correct me if i am wrong)
So how can i convert my result into list of DashboardSubindicatorSubmission class?
Does setResultTransformer() helps me in this case?
You have two options. When you use projections, Hibernate doesn't know how to respect each field because it uses the name of each field to build objects and he doesn't know the names yet.
Thus, your first option is to name the fields grouped so that they match the names of object properties. This is necessary even if the string you use in projection is already the name of the object field. Something like:
.add(Projections.groupProperty("fkAccademicYearId"), "fkAccademicYearId") // same value
.add(Projections.groupProperty("fkAssessmentPlanID"), "other") // other value
The second option is to do what you yourself suggested, create your own implementation of ResultTransformer. I reckon this a interesting option if you want to extract other object of this query, as when you make a report.

Can LINQ ToArray return a strongly-typed array in this example?

I've contrived this example because it's an easily digested version of the actual problem I'm trying to solve. Here are the classes and their relationships.
First we have a Country class that contains a Dictionary of State objects indexed by a string (their name or abbreviation for example). The contents of the State class are irrelevant:
class Country
{
Dictionary<string, State> states;
}
class State { ... }
We also have a Company class which contains a Dictionary of zero or more BranchOffice objects also indexed by state names or abbreviations.
class Company
{
Dictionary<string, BranchOffice> branches;
}
class BranchOffice { ... }
The instances we're working with are one Country object and an array of Company objects:
Country usa;
Company companies[];
What I want is an array of the State objects which contain a branch. The LINQ I wrote is below. First it grabs all the companies which actually contain a branch, then joins to the list of states by comparing the keys of both lists.
The problem is that ToArray returns an anonymous type. I understand why anonymous types can't be cast to strong types. I'm trying to figure out whether I could change something to get back a strongly typed array. (And I'm open to suggestions about better ways to write the LINQ overall.)
I've tried casting to BranchOffice all over the place (up front, at list2, at the final select, and other less-likely candidates).
BranchOffice[] offices =
(from cm in companies
where cm.branches.Count > 0
select new {
list2 =
(from br in cm.branches
join st in usa.states on br.Key equals st.Key
select st.Value
)
}
).ToArray();
You can do:
select new MyClassOfSomeType {
..
)
For selection, you can give it a custom class type. You can also then use ToList. With ArrayList, if you need to keep it loosely typed, you can then make it strongly typed later using Cast<>, though only for any select result that doesn't generate an anonymous class.
HTH.
If i understand the problem correctly, the you want just the states that have office brances in them, not the branches too. If so, one posible linq is the following:
State[] offices =
(from cm in companies
where cm.branches.Count > 0
from br in cm.branches
join st in usa.states on br.Key equals st.Key
select st.Value
).Distinct().ToArray();
If you want both the states and the branches, then you will have to do a group by, and the result will be an IEnumerable>, which you can process after.
var statesAndBranches =
from cm in companies
where cm.branches.Count > 0
from br in cm.branches
join st in usa.states on br.Key equals st.Key
group br.Value by st.Value into g
select g;
Just one more thing, even though you have countries and branches declared as dictionaries, they are used as IEnumerable (from keyValuePair in dictionary) so you will not get any perf benefit form them.

Resources