I have a number of evaluations I'm looking to make to a collection of data and I've been trying to figure out how to accomplish this with streams. I'm looking at streams specifically because the collection sizes can be fairly large and i'd like to take advantage of the parallel processing.
Here is some sample code, which reflects a basic structure -
import java.math.BigDecimal;
import java.util.Map;
import java.util.List;
public class TestApplication {
/***
* Enumerated list of various product types
***/
public enum ProductType {
TOY, BATH, BEDROOM, OUTDOOR
}
/***
* Represents each product we have in inventory.
***/
class Product {
public String name;
public ProductType type;
public int quantity = 0;
public double costPerUnit = 0;
public double productValue = 0;
public float percentOfTotalInventory;
public void setName(String name) { this.name = name; }
public void setProductType(ProductType type) { this.type = type; }
public void setQuantity(int quantity) { this.quantity = quantity; }
public void setCostPerUnit(double unitCost) { this.costPerUnit = unitCost; }
public void calculateProductValue() {
this.productValue = quantity * costPerUnit;
}
public void calculatePercentageOfTotalInventory(double totalInventoryValue) {
this.percentOfTotalInventory = (float) ((costPerUnit * 100) / totalInventoryValue);
}
public String getName() { return name; }
public ProductType getType() { return type; }
public int getQuantity() { return quantity; }
public double getCostPerUnit() { return costPerUnit; }
public float getPercentOfTotalInventory() { return percentOfTotalInventory; }
}
/***
* summary of all the products in our inventory.
*
* in addition to the catalog of products, will keep a running total cost of
* the current inventory and a summary grouping of the products by type
***/
class ProductInventory {
public List<Product> products;
public BigDecimal totalCostOfInventory;
public Map<ProductType, List<ProductSummaryByType>> productsByType;
public void accept(Product p) {
p.calculateProductValue();
products.add(p);
totalCostOfInventory = totalCostOfInventory.add(BigDecimal.valueOf(p.getCostPerUnit()));
}
public void combine(ProductInventory other) {
this.products.addAll(other.products);
this.totalCostOfInventory = totalCostOfInventory.add(other.totalCostOfInventory);
}
}
private class ProductSummaryByType {
//for a type (toy, bath, bed)
public ProductType Type;
//provide total value of inventory for this product type
public double value;
public float percentageOfTotal;
public void calcPercentOfTotal(double total) {
//given the typeValuation, calculate percentage of the total inventory is this type?
this.percentageOfTotal = (float) ((this.value * 100) / total);
}
}
private Map<String, BigDecimal> getProductPrice(String productName) {
//get current price for product
Map<String, BigDecimal> productInfo = new HashMap<String, BigDecimal>();
//simulate the pricing
productInfo.put(productName, BigDecimal.ONE);
return productInfo;
}
public void run(String... args) throws Exception {
//provide product list and process stream
}
}
For a collection of Products, each Product will have a varying purchase price each day, so there is a different routine to set the costPerUnit.
I'm looking to:
iterate over the list of products and capture a collection of the product names
e.g: List<String> productNames = productList.stream().map(Product::getName).collect(Collectors.toList());
Map the names to a function to get the current price
e.g: productNames.stream().map(this::getProductPrice).collect(Collectors.toList());
For each item in the updated price list, set the costPerUnit for each matching instance in the product inventory collection
For each instance of the product, calculate the cost (calculateProductValue) on each instance and add to the ProductInventory, I assume as a custom collector, so it can calculate the total cost of all inventory
with total cost of all inventory, calculate the percentOfTotalInventory for each product by calling the calculatePercentageOfTotalInventory
lastly, group the inventory by ProductType with the total value for the group (adding the productValue for each instance in the group) and calculate the percentageOfTotal for the group in the ProductSummaryByType class
the ProductInventory class should be fully populated at the end of the chain
Again, the reason for trying to do this with nested logic is because I'd like to invoke with parallelism. If you have any suggestions on how best to accomplish, I could use the guidance.
Related
I have classes "BaseProduct" - abstract, "Food" - implements BaseProduct and a "Cart" which puts many foods inside.
So how do I put the foods inside the Cart and how do I go about adding their expiration date discount(5 days before their expiration date, they get discounted with 10%)?
I should also Create a class "Cashier" that has a method to print a receipt. The method accepts a "Cart" (collection of products) and the date and time of purchase. It should print all purchased products with their price, quantity, the total sum and the total discount.
Would be greatly thankful if someone helps.
Class BaseProduct looks like this:
public abstract class BaseProduct {
private String name;
private String brand;
private Double price;
protected BaseProduct(String name, String brand, Double price) {
this.name = name;
this.brand = brand;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}
Class Food looks like this :
package Products.PerishableProducts;
import Products.BaseProduct;
import java.util.Date;
public class Food extends BaseProduct {
private Date expirationDate;
//sample date - 2020/04/20 -> yyyy-MM-dd
//TODO:CHECK WHETHER OR NOT YOU HAVE EXPIRATION DATE DISCOUNTS WHEN ADDING ITEMS IN CART
public Food(String name, String brand, Double price, Date expirationDate) {
super(name, brand, price);
this.expirationDate = expirationDate;
}
public Date getExpirationDate() {
return expirationDate;
}
public void setExpirationDate(Date expirationDate) {
this.expirationDate = expirationDate;
}
}
Class "Cart"(shopping cart) looks like this:
package Cart;
import Products.BaseProduct;
import java.util.List;
public class Cart {
private static List<BaseProduct> products;
//TODO:add products
public List<BaseProduct> add(List<BaseProduct> products){
}
//TODO: remove products
}
There can be multiple approaches to solve this problem. If we think about where would it count to have a discount is obviously when we calculate the price of a product. So I would refactor the Food class as follows:
import java.time.LocalDate;
public class Food extends BaseProduct {
// Use LocalDate instead of the outdated Date class. It is way easier to use and to avoid some common pitfalls
private LocalDate expirationDate;
public Food(String name, String brand, Double price, LocalDate expirationDate) {
super(name, brand, price);
this.expirationDate = expirationDate;
}
public LocalDate getExpirationDate() {
return expirationDate;
}
public void setExpirationDate(LocalDate expirationDate) {
this.expirationDate = expirationDate;
}
// Override the calculation of the price of a Food product and apply the discount
#Override
public Double getPrice() {
return super.getPrice() * (1.0 - getDiscount());
}
// Calculate the discount percentage. If the expiration day is less than 5 days compared to current day, return a 10% discount, otherwise return 0
private Double getDiscount() {
return expirationDate.minusDays(5).isAfter(LocalDate.now()) ? 0.0 : 0.1;
}
}
Now, in case of a Cart we probably care about the total price of how much a client would want to pay. So we can write the following methods:
import java.util.ArrayList;
import java.util.List;
public class Cart {
List<BaseProduct> products = new ArrayList<>();
public void addProduct(BaseProduct product) {
products.add(product);
}
// Returns the total price of the items currently in our cart. Discount is applied to the items which are eligible
public Double getTotalPrice() {
return products.stream().mapToDouble(BaseProduct::getPrice).sum();
}
}
We can use the Cart and in the following way:
public static void main(String[] args) {
BaseProduct cheese = new Food("cheese", "CheeseBrand", 10.0, LocalDate.of(2021, 10, 1));
BaseProduct vine = new Food("vine", "Vine", 50.0, LocalDate.of(2025, 12, 1));
Cart cart = new Cart();
cart.addProduct(cheese);
cart.addProduct(vine);
System.out.println(cart.getTotalPrice());
}
Assuming that the current date is: 2021 31th of October, we would get the following total amount for the example above: 59.0.
Also, we may want to think about what would happen if there is an expired product. For now, we apply a 10% discount to it, we probably would not want to be able to put the item into the cart.
To add products to the cart
Finish writing the Cart::add method.
Initializing the products list will help. Then have a look at the add methods on the list implementation you chose .
To apply a discount.
Overload the getPrice method in Food with a getprice(Date purchaseDate) method. return the price from super.getPrice() less your discount when appropriate.
You may find that having a getDiscount(Date purchaseDate) method is useful, as the Cashier class wants a total discount.
Why is your products list static?
To initialize the list there are two things you can do:
a. Eager initialization:
private List<BaseProduct> products = new ArrayList<>();
public void add(BaseProduct product) {
this.products.add(product);
}
b. Lazy Initialization:
public void add(BaseProduct product) {
if (this.products == null) {
this.products = new ArrayList<>();
}
this.products.add(product);
}
Let me know if this answers your question or you have any other query.
I am trying to get the total value from all the products purchased of the Cart.
This information came from an ArrayList but I am not sure if the code I am doing is correct.
​public class Cart {
// creating a new list every time so need to modify..it will hold the list
private List<CartLine> cartLineList = new ArrayList<>();
/**
*
* #return the actual cartline list
*/
public List<CartLine> getCartLineList() {
return cartLineList;
}
public double getTotalValue(List<CartLine> cartLineList)
{
//TODO implement the method
//return Products*Price
double results=0;
// for(CartLine cartLine: getCartLineList()){
//results += (cartLine.getQuantity()* cartLine.getProduct().getPrice());
//}
return results;
}
//more code here...
}
this what CartLine looks like
public class CartLine {
private Product product;
private int quantity;
public CartLine(Product product, int quantity) {
this.product = product;
this.quantity = quantity;
}
public double getSubtotal() {
return quantity * product.getPrice();
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
If getQuantity does returns the amount purchased, and .getProduct().getPrice() returns the price of the item, then the summation code looks fine (only commented out). Here's what it should look like:
public double getTotalValue(List<CartLine> cartLineList) {
double results = 0;
for(CartLine cartLine: getCartLineList()){
results += (cartLine.getQuantity() * cartLine.getProduct().getPrice());
}
return results;
}
It would be helpful if you showed us what CartLine is. Could there be different products with different prices in the same CartLine instance?
The same answer but using lambdas
Collector<CartLine, double[], double[]> productSumCollector = Collector.of(
() -> new double[1],
(result, cartLine) -> result[0] += (cartLine.getQuantity() * cartLine.getProduct().getPrice()),
(result1, result2) -> {
result1[0] += result2[0];
return result1;
}
);
return Arrays.stream(cartLines).collect(productSumCollector)[0];
For reference see https://www.deadcoderising.com/2017-03-07-java-8-creating-a-custom-collector-for-your-stream/
I have a question about hashmap,
I have a class product:
public class product{
private String libele;
and also a class special product:
public class SpecialProd extends Product{
private HashMap<Product, Price> comp = new HashMap<Product, Price>();
public HashMap<Product, Price> getComp() {
return comp;
}
}
The problem is that i need to know how much products i have in stock, the total amount of the products and how much for every product:
so i used this code in the class storage:
public class Storage{
private HashMap<Product, Price> port = new HashMap<Product, Price>();
public void total (){
int nombre=0;
int total = 0;
int i=0;
for (Product p:port.keySet()){
if (port.containsKey(a)){
// ++nombre;
}
total = port.size();
System.out.println(+nombre+ "of" +a.getLibele());
}
System.out.println("total products:" +total);
}
// this is an intern class
static class Part{
private int price;
public Price(int price) {
this.price= price;
}
public int getprice() {
return price;
}
public void setPrice(int price) {
this.price= price;
}
}
}
i couldn't get the count of every product and the total prices.
Any ideas please?
You can try enumerating the HashMap and get the desired values.
int count=0;
for(p : port.keySet()){
count ++;
price = price + port.get(a);
}
I have a class of Person with an ArrayList of the class Groceries.
Let's name the Arraylist shoppingBag.
Student and Groceries has one field each, int money and int price.
The specific amount of money and price is up to you when initializing new objects.
So every time a Person adds an object of Groceries to his shoppingBag, the amount of money he has needs to be reduced with the total price of groceries added to the bag.
How do you do that?
So, let my try to understand what you want (as I do the same for my clients)
You have a class Groceries with price field:
class Groceries {
private int price;
public Groceries(int price) {
this.price = price;
}
public int getPrice() {
return price;
}
#Override
public String toString() {
return "Groceries{" +
"price=" + price +
'}';
}
}
And class person with int money filed and shopping bag field as List of Groceries:
class Person {
private List<Groceries> shoppingBag = new ArrayList<>();
private int money;
public Person(int money) {
this.money = money;
}
public List<Groceries> getShoppingBag() {
return shoppingBag;
}
public int getMoney() {
return money;
}
}
Firstly you create an instance of Person with some mount of money: Person person = new Person(150);
And then each time when you add a groceries to the shopping bag, like person.getShoppingBag().add(new Groceries(10)); you do want to reduce amount of money from the person instance.
So, If I am correct, you need to implement several things:
1) You should forbid adding groceries to the shopping bag with the way described before. We need to throw an exception when someone tries to add an element to the List via getter. It can be implemented using an Unmodifiable copy of your list:
public List<Groceries> getShoppingBag() {
List<Groceries> bag = new UnmodifiableArrayList<>(shoppingBag.toArray(new Groceries[shoppingBag.size()]), shoppingBag.size());
return bag;
}
or a little bit nicely and shortly using Guava:
public List<Groceries> getShoppingBag() {
List<Groceries> bag = ImmutableList.copyOf(shoppingBag);
return bag;
}
2) Add a method that will add a groceries directly. You can also throw an exception if there is no enough money to not have negative balance:
public void addToShoppingBag(Groceries groceries) {
if (0 > money - groceries.getPrice()) {
throw new IllegalStateException("You have not enough money!");
}
shoppingBag.add(groceries);
money -= groceries.getPrice();
}
3) Probably you will need to have possibility to add some money:
private void addMoney(int amout) {
money += amout;
}
Please see the completely demo example:
class Demo {
public static void main(String[] args) throws IOException {
Person person = new Person(42);
try {
System.out.println(person.getMoney());
person.addToShoppingBag(new Groceries(12));
person.addToShoppingBag(new Groceries(20));
person.addToShoppingBag(new Groceries(5));
System.out.println(person.getMoney());
System.out.println(person.getShoppingBag());
person.getShoppingBag().add(new Groceries(1));
} catch (UnsupportedOperationException e) {
e.printStackTrace();
}
try {
person.addToShoppingBag(new Groceries(66));
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
}
class Person {
private List<Groceries> shoppingBag = new ArrayList<>();
private int money;
public Person(int money) {
this.money = money;
}
public List<Groceries> getShoppingBag() {
List<Groceries> bag = ImmutableList.copyOf(shoppingBag);
return bag;
}
public void addToShoppingBag(Groceries groceries) {
if (0 > money - groceries.getPrice()) {
throw new IllegalStateException("You have not enough money!");
}
shoppingBag.add(groceries);
money -= groceries.getPrice();
}
private void addMoney(int amout) {
money += amout;
}
public int getMoney() {
return money;
}
}
class Groceries {
private int price;
public Groceries(int price) {
this.price = price;
}
public int getPrice() {
return price;
}
#Override
public String toString() {
return "Groceries{" +
"price=" + price +
'}';
}
}
PS: Please next time describe some examples of code and demos to get an answer :)
I have an assignment to make this Restaurant Program. it Consists of an Order Class a product class and the main class. Order has an ArrayList to hold the products. I create an instance of the Order and then I add items through my main method.A product has a name(string) a bar-code(string), and a price(float).
Then I have to output a receipt.But what if a customer orders more of one product? Do I instantiate everything one by one? Is a second Beer Product independent? Should I hold quantities somehow? If I want to add a second beer I have to create a new product Beer2 etc? I don't know beforehand how many products each order will hold and the quantity of each so Is this way of instantiating proper? Thanks
Note: it is still incomplete as I want to deal with this before I move on.
import java.util.Date;
public class MyRestaurantTester {
public static void main(String[] args) {
Date currentDate = new Date();
Paraggelia order1 = new Paraggelia(currentDate,"11B");
Product Beer = new Product("Amstel","111222",1.20f);
Product Beef = new Product("Pork Beef","333444",8.50f);
order1.add(Beer);
order1.add(Beef);
System.out.println(order1.getReceipt(30f));
}
}
Order Class(nevermind the name Paraggelia I gave it)
import java.util.ArrayList;
import java.util.Date;
/*Notes to self:
* -Work on Comments
* -Javadocs maybe?
* -try to optimize the rough code.
*/
/*Order class*/
public class Paraggelia {
private Date orderDate;
private String tableNumber;
private int customerCount;
private ArrayList<Product> listOfItems;
/*Constructor(s)*/
Paraggelia(Date orderDate,String tableNumber){
this.orderDate=orderDate;
this.tableNumber=tableNumber;
this.listOfItems = new ArrayList<Product>();
}
/*Add && Delete Products from the Order class*/
public void add(Product p){
if(p == null)
{
throw new IllegalArgumentException();
}else{
listOfItems.add(p);
}
}
public void delete(Product p){
if(p == null)
{
throw new IllegalArgumentException();
}
else
{
listOfItems.remove(p);
}
}
/** Calculates and returns the total price
* Usually called directly as a parameter of getReceipt function
* */
public static float getTotalPrice(){
return 0;
}
/** Creates and returns the final Receipt!
* -Display must consist of:
* Item$ - BarCode# - Item Amount#
* Total Price#
* Table Number#
*/
public String getReceipt(float totalPrice){
StringBuilder receipt = new StringBuilder();
for(int i =0; i<this.listOfItems.size();i++){
receipt.append(listOfItems.get(i).getName());
receipt.append("\n");
}
return new String(receipt);
}
/*Getters && Setters */
public Date getOrderDate() {
return orderDate;
}
public void setOrderDate(Date orderDate) {
this.orderDate = orderDate;
}
public String getTableNumber() {
return tableNumber;
}
public void setTableNumber(String tableNumber) {
this.tableNumber = tableNumber;
}
public int getCustomerCount() {
return customerCount;
}
public void setCustomerCount(int customerCount) {
this.customerCount = customerCount;
}
}
Product Class:
public class Product {
private String Name;
private String barCode;
private float sellingPrice;
/*Constructors: */
Product(){}
Product(String Name,String barCode,float sellingPrice){
this.Name=Name;
this.barCode=barCode;
this.sellingPrice=sellingPrice;
}
/*Getters & Setters*/
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getBarCode() {
return barCode;
}
public void setBarCode(String barCode) {
this.barCode = barCode;
}
public float getSellingPrice() {
return sellingPrice;
}
public void setSellingPrice(float sellingPrice) {
this.sellingPrice = sellingPrice;
}
}
Instead of ArrayList ( List ) you can use Map ( HashMap for example )
MyRestaurantTester
public class MyRestaurantTester {
public static void main(String[] args) {
Date currentDate = new Date();
Paraggelia order1 = new Paraggelia(currentDate,"11B");
Product Beer = new Product("Amstel","111222",1.20f);
Product Beef = new Product("Pork Beef","333444",8.50f);
order1.add(Beer, 1);
order1.add(Beef, 5);
System.out.println(order1.getReceipt(30f));
}
}
Paraggelia
class Paraggelia {
private Date orderDate;
private String tableNumber;
private int customerCount;
private Map<Product, Integer> listOfItems;
/*Constructor(s)*/
Paraggelia(Date orderDate,String tableNumber){
this.orderDate=orderDate;
this.tableNumber=tableNumber;
this.listOfItems = new HashMap<Product, Integer>();
}
/*Add && Delete Products from the Order class*/
public void add(Product p, int quantity){
if(p == null)
{
throw new IllegalArgumentException();
}else{
listOfItems.put(p, quantity);
}
}
public void delete(Product p){
if(p == null)
{
throw new IllegalArgumentException();
}
else
{
listOfItems.remove(p);
}
}
/** Calculates and returns the total price
* Usually called directly as a parameter of getReceipt function
* */
public static float getTotalPrice(){
return 0;
}
/** Creates and returns the final Receipt!
* -Display must consist of:
* Item$ - BarCode# - Item Amount#
* Total Price#
* Table Number#
*/
public String getReceipt(float totalPrice){
StringBuilder receipt = new StringBuilder();
for(Map.Entry<Product,Integer> entry : this.listOfItems.entrySet()) {
Product product = entry.getKey();
Integer quantity = entry.getValue();
receipt.append(product.getName() + " " + quantity);
receipt.append("\n");
}
return new String(receipt);
}
/*Getters && Setters */
public Date getOrderDate() {
return orderDate;
}
public void setOrderDate(Date orderDate) {
this.orderDate = orderDate;
}
public String getTableNumber() {
return tableNumber;
}
public void setTableNumber(String tableNumber) {
this.tableNumber = tableNumber;
}
public int getCustomerCount() {
return customerCount;
}
public void setCustomerCount(int customerCount) {
this.customerCount = customerCount;
}
}
OUTPUT:
Pork Beef 5
Amstel 1
Three basic approaches come to mind:
Instantiate each product individually
Instead of ArrayList, have another structure that can associate items with quantities; or,
Make a class Article, which belongs to a Product: Product beerProduct = new Product("beer", "0129", 1.37); Article beer = new Article(beerProduct), beer2 = new Article(beerProduct).
The first solution gives you a lot of flexibility (e.g. to discount individual articles for, say, being damaged). The second solution is more economical with objects. The third one captures the intuition that all the Heineken bottles are the same. It is really up to what you want to do - both approaches are equally valid, for some purpose.