I have two classes :
public class Invoice {
private int InvoiceId;
private Customer customer;
private Date invoiceDate;
}
And
public class Customer {
private int customerId;
private String customerType;
}
customerType field can have three values : F,H,B
I have an arrayList of Invoice objects, i want to sort it using the customerType property in a specific order :all the the invoice with customerType "F" then those with "H" then "B".
I tried to implement the Comparator interface , but the problem is that his method compare takes two arguments of Customer objects and the sorting is made in ascending or descending way.
This is the code :
Collections sort(invoicesList, new Comparator<Invoice >() {
public int compare(Invoice s1,Invoice s2) {
return s1.getCustomer().getCustomerTpe().equalsIgnoreCase("F").compareTo(s2.getCustomer().getCustomerTpe().equalsIgnoreCase("H"))
}
}
);
Is there a way to sort my collection with a specific order with three fileds value ?
Thnks
One way to customize the sort order is to first map the arguments and then sort on the mapped values. In your case mapping of the customerType can be done by finding its index in the string "FHB". In Java 8, this might be expressed as
Collections.sort(invoicesList, Comparator.comparingInt(
(invoice) -> "FHB".indexOf( invoice.getCustomer().getCustomerType() )));
Related
I am using JPA specs for searching of orders but inside my order Entity there is an another property ProductEntity which is a list. there is a another property ProductType inside my ProductEntity. now i need to filter my productList inside my orderEntity on the basis of productType That i will send in RequestParam of my api..
1.here is my parent Entity
OrderEntity{
private String tgxId;
private List<ProductEntity> products;
}
2.here is my ChildEntity
ProductEntity{
private String id;
private String orderId;
ProductType type;
}
now , when i do search orderslist i also need to filter my product list in the basis of ProductType that i send from RequestParam.
Result should contains entire order object list but products list inside each orderEntity should be filtered on the basis productType that i will send form RequestParam.
Thanks.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 12 months ago.
Improve this question
class Person{
private String name;
private String address;
private int id;
private int uniqueIdentificationNumber;
}
class Company{
private String name;
private String address;
private int id;
private int uniqueIdentificationNumber;
private String company;
}
class Test{
public static void main(String[] args)
{
List<Person> persons = new ArrayList<>();
Person p1 = new Person();
p1.setName("ABC");
p1.setAddress("US");
p1.setId(1);
p1.setUniqueIdentificationNumber(11);
Person p2 = new Person();
p2.setName("PQR");
p2.setAddress("US");
p2.setId(2);
p2.setUniqueIdentificationNumber(22);
persons.add(p1);
persons.add(p2);
List<Company> companies = new ArrayList<>();
Company c1 = new Comapny();
c1.setName("ABC");
c1.setAddress("US");
c1.setId(3);
c1.setUniqueIdentificationNumber(44);
c1.setCompany("C1")
Company c2 = new Comapny();
c2.setName("ABC");
c2.setAddress("US");
c2.setId(1);
c2.setUniqueIdentificationNumber(11);
c2.setCompany("C2");
companies.add(c1);
companies.add(c2)
}
}
I want to compare two different object types of lists (companies and persons) with Java8 Stream API and return the customer object which is matching with Id and setUniqueIdentificationNumber. i.e here in this case it should retun c2.
Can anyone help on this
It doesn't clear how uniqueIdentificationNumber of the Person and Company are related. It's worth to refine these classes to represent the relationship between them in a better way (maybe a company can hold a reference to a list of customers). And don't overuse setters, if id is unique there's no need to allow it to be changed.
Although it's not clear how these values are connected because of the drawbacks of your class design technically it's doable.
return the customer object which is matching with Id
For that, you need to create two maps that will associate these identifiers with companies and persons. Then create a stream over the keys of one of these maps and check for every key whether if contained in another map. And then retrieve the Person objects for filtered keys and collect the result into a list.
Map<Integer, Person> personById =
persons.stream()
.collect(Collectors.toMap(Person::getUniqueIdentificationNumber,
Function.identity()));
Map<Integer, Company> companyById =
companies.stream()
.collect(Collectors.toMap(Company::getUniqueIdentificationNumber,
Function.identity()));
List<Person> customers =
personById.keySet().stream()
.filter(companyById::containsKey) // checking whether id is present in the company map
.map(personById::get) // retrieving customers
.collect(Collectors.toList());
Update
Let me rephrase the description of the problem.
There are two unrelated classes A and B. Both classes have two fields of type int, let's say val1 and val2 (and maybe a few more fields but we are not interested in them).
We have a list of objects A and a list of objects B. The goal is to find a single object A for which exists an object B with the same values for both val1 and val2 (in order to keep things simple I propose to stick with this example).
There are two approaches that could be used for that purpose:
create an auxiliary class with two fields val1 and val2, and associate every instance of A and B with instances of this class;
create a nested map Map<Integer, Map<Integer, *targetClass*>>, this solution is more complicated and less flexible, if you'll need to compare objects by three, four, etc. fields the code will quickly become incomprehensible.
So I'll stick with the first approach. We need to declare the ValueHolder class with two fields and implement the equals/hashCode contract based on these fields. For Java 16 and above we can utilize a record for that purpose and make use of equals(), hashCode, getters provided by the compiler. The option with the record will look like this:
public record ValueHolder(int val1, int val2) {} // equals/hashCode, constructor and getters provided by the compiler
Classes A and B
public class A {
private int val1;
private int val2;
// constructor and getters
}
public class B {
private int val1;
private int val2;
// constructor and getters
}
And a method that accepts two lists: List<A> and List<B>, and return a result as Optional<A>. Because the matching element may or may not be present and it's a good practice to return an optional object in such cases instead of returning null in case the result was not found. It provides more flexibility and that's precisely the case for which the optional was designed.
public Optional<A> getMatchingItem(List<A> listA, List<B> listB) {
Map<ValueHolder, A> aByValue = listA.stream()
.collect(Collectors.toMap(a -> new ValueHolder(a.getVal1(), a.getVal2()),
Function.identity()));
Map<ValueHolder, B> bByValue = listB.stream()
.collect(Collectors.toMap(b -> new ValueHolder(b.getVal1(), b.getVal2()),
Function.identity()));
return aByValue.keySet().stream()
.filter(bByValue::containsKey)
.findFirst()
.map(aByValue::get);
}
I have the following classes
#Document("artists")
public class Artist {
private String name;
private List<Album> discography;
// Getters and Setters
}
public class Album {
private String title;
private Instant releaseDate;
// Getters and Setters
}
My goal is to write an Aggregate function using Spring data mongodb that will allow me to first find an Artist by name, then look through the associated List<Album> and filter its contents based on a date range provided. It should then return a List<Album> with all the instances of Album that fell within that date range.
The main problem I'm having is that an instance of CriteriaDefinition is provided, and I need to construct the Aggregation and $filter conditions using the contents of it.
// A CriteriaDefinition gets passed into this method
// which contains various criteria to search on
Aggregation agg = Aggregation.newAggregation(
Aggregation.match(criteriaDefinition), //This returns the correct Artist, but with all Album objects
Aggregation.project()
.and(filter("album.discography")
.as("discography")
.by( //how do use a CriteriaDefinition here? )
.as("albums")
);
List<Album> albums = mongoTemplate
.aggregate(agg, Artist.class, Album.class);
return albums;
Is it even possible to filter on CriteriaDefinition?
You can use unwind operator with a match on subdocuments. For example i filtered artists with name "foobar" and returned its albums with a releaseDate greater than now.
Please try :
Aggregation aggregation = newAggregation(
match(where("name").is("foobar")),
unwind("discography"),
match(where("discography.releaseDate").gt(new Date())));
AggregationResults<Album> results = mongoTemplate.aggregate(aggregation, Artist.class, Album.class);
List<Album> albums = results.getMappedResults();
I have a database table which holds Metadata for documents. My task now is to get a list with documenttypes. The documenttypes are not unique in the database table but of course I want them to be in my list. The sql is very simple:
SELECT DISTINCT groupname, group_displayorder
FROM t_doc_metadata
ORDER BY group_displayorder;
I have learned that I can use projections to get a subset of fields from my entity DocMetadata. I solved this as follows. My Entity:
#Entity
#Table(name="T_DOC_METADATA")
#Data
public class DocMetadata {
..............
#Column(nullable=false)
private String displayname;
#Column(nullable=false)
private Integer displayorder;
#Column(nullable=false)
private String groupname;
#Column(name="GROUP_DISPLAYORDER",
nullable=false)
private Integer groupDisplayorder;
#Column(name="METADATA_CHANGED_TS",
nullable=false,
columnDefinition="char")
private String metadataChangedTimestamp;
..........
}
My inteface for projection:
public interface GroupnameAndOrder {
String getGroupname();
Integer getGroupDisplayorder();
void setGroupname(String name);
void setGroupDisplayorder(int order);
}
Now I thought I'd be extraordinary clever by adding these lines to my repository:
#Query("select distinct d.groupname, d.groupDisplayorder from DocMetadata d order by d.groupDisplayorder")
public List<GroupnameAndOrder> findSortedGroupnames();
Sadly, when iterating over the result list and calling getGroupname() the result is null.
So I changed the lines in my repository according to the documentation:
public List<GroupnameAndOrder> findBy();
Now I get the groupnames but of course they are not unique now. So it doesn't solve my problem.
Is there any way to receive a ordered list with unique groupnames?
You are trying to be too clever. Instead just write the proper find method and return the GroupnameAndOrder. Spring Data JPA will then only retrieve what is needed for the projection.
Something like this should do the trick.
List<GroupnameAndOrder> findDistinctByOrderByGroupDisplayorder();
I would like to know how to access a deep collection class attribute in a GET request. My endpoint maps my query strings through #ModelAttribute annotation:
Given that:
public class MyEntity
{
Set<Item> items;
Integer status;
// getters setters
}
public class Item
{
String name;
// getters setters
}
And my GET request: localhost/entities/?status=0&items[0].name=Garry
Produces bellow behavior?
#RequestMapping(path = "/entities", method = RequestMethod.GET)
public List<MyEntity> findBy(#ModelAttribute MyEntity entity) {
// entity.getItems() is empty and an error is thrown: "Property referenced in indexed property path 'items[0]' is neither an array nor a List nor a Map."
}
Should my "items" be an array, List or Map? If so, thereĀ“s alternatives to keep using as Set?
Looks like there is some problem with the Set<Item>.
If you want to use Set for the items collection you have to initialize it and add some items:
e.g. like this:
public class MyEntity {
private Integer status;
private Set<Item> items;
public MyEntity() {
this.status = 0;
this.items = new HashSet<>();
this.items.add(new Item());
this.items.add(new Item());
}
//getters setters
}
but then you will be able to set only the values of this 2 items:
This will work: http://localhost:8081/map?status=1&items[0].name=asd&items[1].name=aaa
This will not work: http://localhost:8081/map?status=1&items[0].name=asd&items[1].name=aaa&items[2].name=aaa
it will say: Invalid property 'items[2]' of bean class MyEntity.
However if you switch to List:
public class MyEntity {
private Integer status;
private List<Item> items;
}
both urls map without the need to initialize anything and for various number of items.
note that I didn't use #ModelAttribute, just set the class as paramter
#GetMapping("map")//GetMapping is just a shortcut for RequestMapping
public MyEntity map(MyEntity myEntity) {
return myEntity;
}
Offtopic
Mapping a complex object in Get request sounds like a code smell to me.
Usually Get methods are used to get/read data and the url parameters are used to specify the values that should be used to filter the data that has to be read.
if you want to insert or update some data use POST, PATCH or PUT and put the complex object that you want to insert/update in the request body as JSON(you can map that in the Spring Controller with #RequestBody).