Below are my entities:
Product
@Entity
@Table(name = "Product")
public class Product extends ReusableFields
{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
Long productId;
@NonNull
@Column(name = "product_name")
String productName;
String measurementUnit;
//more fields and getters setters
}
Inward Outward List related to product:
@Entity
@Table(name = "inward_outward_entries")
public class InwardOutwardList extends ReusableFields
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Long entryid;
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "productId", nullable = false)
@JsonIgnoreProperties(
{ "hibernateLazyInitializer", "handler" })
Product product;
@JsonSerialize(using = DoubleTwoDigitDecimalSerializer.class)
Double quantity;
//more fields
}
Inward inventory having set of inward outward list:
@Entity
@Table(name = "inward_inventory")
public class InwardInventory extends ReusableFields implements Cloneable
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "inwardid")
Long inwardid;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name = "inwardinventory_entry", joinColumns =
{ @JoinColumn(name = "inwardid", referencedColumnName = "inwardid") }, inverseJoinColumns =
{ @JoinColumn(name = "entryId", referencedColumnName = "entryId") })
Set<InwardOutwardList> inwardOutwardList = new HashSet<>();
//more fields
}
I have a list of inward inventories which I want to group by based on product. So, I want to do is
SUM(InwardInventory.InwardOutwardList.quantity) while grouping by based on InwardInventory.InwardOutwardList.Product.productName and InwardInventory.InwardOutwardList.Product.measurementUnit
I am new to streams, I know it can be done but not able to get exact solution. Can someone provide guidance or help.
CodePudding user response:
From what I understand, you want a Map
where the key is some representantion of a unique productName
and measurementUnit
, and the value is a Double.
First, you need to define the class that you'll use as the map's keys. The requirement for it is to implement equals and hashCode ONLY based on productName
and measurementUnit
fields. Else, you won't be able to properly aggregate the quantities into the map. If you follow Vlad Mihalcea's advice then you can't use the Product
class as keys because it implements equals and hashCode based only on the id
field. In the example below Pair<L, R>
has properly implemented equals and hashCode based on the L
and R
fields.
List<InwardInventory> inwardInventoryList = ...;
Map<Pair<String, String>, Double> map = inwardInventoryList.stream()
.flatMap(i -> i.getInwardOutwardList().stream())
.collect(Collectors.toMap(l -> Pair.of(l.getProduct().getProductName(), l.getProduct().getMeasurementUnit()),
InwardOutwardList::getQuantity,
Double::sum));
The first operation is a flatMap
because every InwardInventory
has a Set<InwardOutwardList>
, but we want a single stream of InwardOutwardList
objects.
The second operation is the Collectors.toMap
defined here.
CodePudding user response:
I created simple classes from your code since the annotations are not required.
Class Product
public class Product {
private Long productId;
private String measurementUnit;
private String productName;
public Product(Long productId, String measurementUnit, String productName) {
this.productId = productId;
this.measurementUnit = measurementUnit;
this.productName = productName;
}
public Long getProductId() {
return productId;
}
public String getMeasurementUnit() {
return measurementUnit;
}
public String getProductName() {
return productName;
}
}
Class InwardOutwardList
public class InwardOutwardList {
private Long entryid;
private Product product;
private Double quantity;
public InwardOutwardList(Long entryid, Product product, Double quantity) {
this.entryid = entryid;
this.product = product;
this.quantity = quantity;
}
public Long getEntryid() {
return entryid;
}
public Product getProduct() {
return product;
}
public Double getQuantity() {
return quantity;
}
}
Finally, class InwardInventory
which contains method main
that demonstrates how to use the stream API to achieve your desired result.
(Notes after the code.)
import java.util.DoubleSummaryStatistics;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class InwardInventory {
private Long inwardid;
private Set<InwardOutwardList> inwardOutwardList = new HashSet<>();
public InwardInventory(Long id) {
inwardid = id;
}
public static void main(String[] args) {
Product p1 = new Product(1L, "unit", "Product_1");
Product p2 = new Product(1L, "unit", "Product_1");
InwardOutwardList ioLst = new InwardOutwardList(1L, p1, 1D);
InwardOutwardList ioLst2 = new InwardOutwardList(2L, p2, 2D);
InwardInventory ii = new InwardInventory(1L);
ii.inwardOutwardList.add(ioLst);
ii.inwardOutwardList.add(ioLst2);
Map<String, DoubleSummaryStatistics> map = ii.inwardOutwardList.stream()
.collect(Collectors.groupingBy(iol -> iol.getProduct().getProductName(),
Collectors.summarizingDouble(InwardOutwardList::getQuantity)));
map.forEach((p, s) -> System.out.println(p " = " s.getSum()));
}
}
ii.inwardOutwardList.stream()
creates a Stream where every element in that stream is an instance ofInwardOutwardList
.- the collect method has a single argument whose type is Collector.
- Class Collectors is a utility class that contains methods that return special kinds of collectors.
- the
collect
method returns aMap
where the map key is the product name – extracted from classProduct
and the map value is an instance of classDoubleSummaryStatistics
. - method
getSum
, in classDoubleSummaryStatistics
returns, in this case, the sum of the quantities.
Running above code produces the following result:
Product_1 = 3.0