-1

Could someone please help me in implementing a method in JAVA that gets the 50 most sold books of a bookstore?

This is the Bookstore class:

public class Bookstore implements Serializable {

    private static final long serialVersionUID = -3099048826035606338L;
    private boolean populated;
    private final List<Country> countryById;
    private final Map<String, Country> countryByName;
    private final List<Address> addressById;
    private final Map<Address, Address> addressByAll;
    private final List<Customer> customersById;
    private final Map<String, Customer> customersByUsername;
    private final List<Author> authorsById;
    private final List<Book> booksById;
    private final List<Cart> cartsById;
    private final List<Order> ordersById;
    private final LinkedList<Order> ordersByCreation;

OrderLine is another class with the details of the order, like:

    private final Book book;
    private final int qty;
    private final double discount;
    private final String comments;

I started doing this: (I have to consider the Order status SHIPPED and the subject of the book), but I don't know if this is right or what to do next, for instance, how to sum the quantities by bookId and order the bestSellers list:

public List<Book> getBestSellers(String subject) {

         ArrayList<Book> bestSellers = new ArrayList<Book>();
         for (Order order : ordersById) {
             if (order.getStatus().equalsIgnoreCase("SHIPPED")) {
                 for (int i=0; i<order.getLines().size(); i++){
Volodya Lombrozo
  • 2,325
  • 2
  • 16
  • 34

1 Answers1

0

First of all, you need to collect all your books with quantity to one Map<Book, Integer>. Then you should sort them and limit only 50.

It would be something like this:

        List<Book> books = orders.stream().filter(PhilippBooks::isShipped)
                .flatMap(Order::lines)
                .collect(Collectors.toMap(OrderLine::getBook, OrderLine::getQuantity, Integer::sum))
                .entrySet()
                .stream()
                .sorted(PhilippBooks.reversed())
                .limit(50)
                .peek(System.out::println)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());

And be sure that your Book class implements the hashCode function correctly. It's necessary for collecting Books on the map.

The full code example:

public class PhilippBooks {

    public static void main(String[] args) {
        Book tolstoy = new Book("Tolstoy");
        Book dostoevsky = new Book("Dostoevsky");
        Book london = new Book("London");

        List<Order> orders = List.of(
                new Order("SHIPPED", new OrderLine(tolstoy, 3), new OrderLine(london, 6)),
                new Order("SHIPPED", new OrderLine(tolstoy, 2), new OrderLine(dostoevsky, 10)),
                new Order("OTHER", new OrderLine(tolstoy, 1), new OrderLine(london, 1), new OrderLine(dostoevsky, 1)));

        List<Book> books = orders.stream().filter(PhilippBooks::isShipped)
                .flatMap(Order::lines)
                .collect(Collectors.toMap(OrderLine::getBook, OrderLine::getQuantity, Integer::sum))
                .entrySet()
                .stream()
                .sorted(PhilippBooks.reversed())
                .limit(50)
                .peek(System.out::println)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());

        System.out.println(books);
    }

    private static Comparator<Map.Entry<Book, Integer>> reversed(){
        return (o1, o2) -> o2.getValue() - o1.getValue();
    }

    private static boolean isShipped(Order order){
        return order.getStatus().equalsIgnoreCase("SHIPPED");
    }

    private static class Book {
        private final String name;

        private Book(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Book{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

    private static class Order {
        private final String status;
        private final List<OrderLine> lines;

        public Order(String status, OrderLine... lines) {
            this(status, Arrays.asList(lines));
        }

        public Order(String status, List<OrderLine> lines) {
            this.status = status;
            this.lines = lines;
        }

        public Stream<OrderLine> lines(){
            return lines.stream();
        }

        public String getStatus() {
            return status;
        }
    }

    private static class OrderLine {

        private final Book book;
        private final int quantity;

        public OrderLine(Book book, int quantity) {
            this.book = book;
            this.quantity = quantity;
        }

        public Book getBook() {
            return book;
        }

        public int getQuantity() {
            return quantity;
        }
    }
}

Volodya Lombrozo
  • 2,325
  • 2
  • 16
  • 34
  • Thank you for helping me! But I have one more (final I guess) question...If I have getBestSellers method implements like this (on the next comment) – Philipp Jun 16 '21 at 18:47
  • Map mapa = new HashMap(); List bestSellers = new ArrayList(); for (Order order : ordersById) { if (order.getStatus().equalsIgnoreCase("SHIPPED")) { for (OrderLine orderLine: order.getLines()) { int qtd = 0; if (subject.equalsIgnoreCase(orderLine.getBook().getSubject())) { if (mapa.get(orderLine.getBook()) !=null) { qtd = mapa.get(orderLine.getBook()); } mapa.put(orderLine.getBook(), orderLine.getQty() + qtd); – Philipp Jun 16 '21 at 18:49
  • how it's the best way to order this using list and returning only the 50 bestSellers? – Philipp Jun 16 '21 at 18:49
  • I've done it in the my example above: ```mapa.entrySet() .stream().sorted(PhilippBooks.reversed()).limit(50)``` – Volodya Lombrozo Jun 17 '21 at 09:35
  • Then I got list from sorted enties: ```.map(Map.Entry::getKey).collect(Collectors.toList())``` – Volodya Lombrozo Jun 17 '21 at 09:38