Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java calculate max customer price using streams

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;
    }
}
like image 771
lukassz Avatar asked Oct 08 '18 12:10

lukassz


People also ask

How do I find the maximum element in a stream list?

Calling stream() method on the list to get a stream of values from the list. Calling mapToInt(value -> value) on the stream to get an Integer Stream. Calling max() method on the stream to get the max value. Calling orElseThrow() to throw an exception if no value is received from max()

How do you find the max element in an array using stream?

With the introduction of Stream with Java 8, we can convert the array into the corresponding type stream using the Arrays. stream() method. Then we can call the max() and min() method, which returns the maximum and minimum element of this stream as OptionalInt .

How does stream Max work?

Stream. max() returns the maximum element of the stream based on the provided Comparator. A Comparator is a comparison function, which imposes a total ordering on some collection of objects. max() is a terminal operation which combines stream elements and returns a summary result.

How do you sum a stream in Java?

Using Stream.collect() The second method for calculating the sum of a list of integers is by using the collect() terminal operation: List<Integer> integers = Arrays. asList(1, 2, 3, 4, 5); Integer sum = integers. stream() .

What is Max stream in Java?

Stream.max () method in Java with Examples. Stream.max () returns the maximum element of the stream based on the provided Comparator. A Comparator is a comparison function, which imposes a total ordering on some collection of objects. max () is a terminal operation which combines stream elements and returns a summary result.

How to calculate average salary of employees using Java stream API?

The traditional way is to create a for-loop to sum up the salary of each employee and then calculate the average by dividing the total sum by number of record. How to achieve it using Java Stream API? It transforms employee records to salary amount in the data stream flow and then calculates the average.

How do I sum the elements of a stream in Java?

The Stream API provides us with the mapToInt () intermediate operation, which converts our stream to an IntStream object. This method takes a mapper as a parameter, which it uses to do the conversion, then we can call the sum () method to calculate the sum of the stream's elements. Let's see a quick example of how we can use it:

What is the power of Java stream API?

Let me show you the power of the stream API with an example. Say, the task is to group an array of employee records into a data map organized by job title. Here is a traditional way to loop through the list and construct a data map. Java Stream API imply applies the collector to generate data map grouping by employee’s title.


2 Answers

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.

like image 154
ernest_k Avatar answered Oct 16 '22 00:10

ernest_k


In case you need the customer which spends the most (by email for example):

  orders.stream()
            .collect(Collectors.toMap(
                    x -> x.getCustomer().getEmail(),
                    x -> x.getProduct().getPrice().multiply(new BigDecimal(x.getQuantity())),
                    BigDecimal::add))
            .entrySet()
            .stream()
            .max(Entry.comparingByValue())
            .ifPresent(System.out::println);
like image 41
Eugene Avatar answered Oct 16 '22 01:10

Eugene