Java calculate max customer price using streams

lukassz :

I have a problem with streams. I have many Customer objects and I would like to calculate which one of them paid the most.

This is my sample data:

class Orders {

    private List<Order> orders = new ArrayList<>();

    public void prepareData() {

        Product socks = new ProductBuilder()
                .setPrice(new BigDecimal("23"))
                .setCategory(Category.C)
                .setName("SOCKS")
                .build();

        Product jacket = new ProductBuilder()
                .setPrice(new BigDecimal("199"))
                .setCategory(Category.A)
                .setName("JACKET")
                .build();

        Product watch = new ProductBuilder()
                .setPrice(new BigDecimal("100"))
                .setCategory(Category.B)
                .setName("WATCH CASIO")
                .build();


        Customer john = new CustomerBuilder()
                .setAge(18)
                .setName("JOHN")
                .setSurname("JOHNSON")
                .setEmail("[email protected]")
                .build();

        Customer mike = new CustomerBuilder()
                .setAge(20)
                .setName("MIKE")
                .setSurname("MAX")
                .setEmail("[email protected]")
                .build();


        Order orderJohn = new OrderBuilder()
                .setQuantity(2)
                .setCustomer(john)
                .setProduct(watch)
                .setOrderDate(LocalDate.now())
                .build();

        Order orderJohn2 = new OrderBuilder()
                .setQuantity(4)
                .setCustomer(john)
                .setProduct(socks)
                .setOrderDate(LocalDate.now())
                .build();


         Order orderMike = new OrderBuilder()
                .setQuantity(2)
                .setCustomer(mike)
                .setProduct(jacket)
                .setOrderDate(LocalDate.now())
                .build();

        orders.add(orderJohn);
        orders.add(orderJohn2);
        orders.add(orderMike);
    }
}

Now I should group by customer because one customer has many orders and calculate price * and quantity and select maximum using orders.stream()? How can I do that?

My class definitions:

public class Order {

    private Customer customer;
    private Product product;
    private int quantity;
    private LocalDate orderDate;
   //get/set
}
public class Customer {

    private String name;
    private String surname;
    private int age;
    private String email;
    //get/set
}
public class Product {
    private String name;
    private BigDecimal price;
    private Category category;
    //get/set
}

And builders

    public class CustomerBuilder {

        private Customer customer = new Customer();

        public CustomerBuilder setName(String name){
            customer.setName(name);
            return this;
        }

        public CustomerBuilder setSurname(String surname){
            customer.setSurname(surname);
            return this;
        }

        public CustomerBuilder setAge(int age){
            customer.setAge(age);
            return this;
        }

        public CustomerBuilder setEmail(String email){
            customer.setEmail(email);
            return this;
        }

        public Customer build()  {

            return customer;
        }

    }
public class OrderBuilder {
   private Order order = new Order();

   public OrderBuilder setCustomer(Customer customer){
       order.setCustomer(customer);
       return this;
   }

   public OrderBuilder setProduct(Product product){
       order.setProduct(product);
       return this;
   }

   public OrderBuilder setQuantity(int quantity){
       order.setQuantity(quantity);
       return this;
   }

   public OrderBuilder setOrderDate(LocalDate orderDate){
       order.setOrderDate(orderDate);
       return this;
   }

   public Order build(){
       return order;
   }

}
public class ProductBuilder {

    private Product product = new Product();

    public ProductBuilder setCategory(Category category){
        product.setCategory(category);
        return this;
    }

    public ProductBuilder setName(String name){
        product.setName(name);
        return this;
    }

    public ProductBuilder setPrice(BigDecimal bigDecimal){
        product.setPrice(bigDecimal);
        return this;
    }

    public Product build() {
        return product;
    }
}
ernest_k :

The following finds the top customer by first grouping by the customer field (mapped to the total of corresponding purchase value [quantity * price]).

The result of that aggregation is then traversed to find "max" by total purchase value.

Customer topCustomer = orders.stream()
        .collect(Collectors.groupingBy(Order::getCustomer,
                Collectors.mapping(
                        order -> order.getProduct()
                                      .getPrice()
                                      .multiply(new BigDecimal(order.getQuantity())),
                        Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))))
        .entrySet().stream()
        .max(Comparator.comparing(Entry::getValue))
        .map(Entry::getKey)
        .orElse(null);

It's important to note that this assumes hashCode() and equals() are properly overridden in Customer for the grouping to work correctly.

EDIT:

If the total amount of purchases is also required, you will need to get the full entry instead of mapping to just the key (below code is based on snippet above):

Optional<Entry<Customer, BigDecimal>> topCustomerEntry = orders.stream()
        .collect(Collectors.groupingBy(Order::getCustomer,
                Collectors.mapping(order -> 
                    order.getProduct()
                            .getPrice()
                            .multiply(new BigDecimal(order.getQuantity())),
                Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))))
        .entrySet().stream()
        .max(Comparator.comparing(Entry::getValue));

BigDecimal topValue = null; //total value for top customer
Customer customer = null;   //customer with most purchases

if(topCustomerEntry.isPresent()) {
    topValue = topCustomerEntry.get().getValue();
    customer = topCustomerEntry.get().getKey();
}

This will just print the values. But you can restructure the code to assign them to a variable.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=36904&siteId=1