0
votes

Task here is to practise Maps & Collections.

We have 2 classes with its attributes in the brackets

1st Class: Client (String name, String lastName, Integer age, BigDecimal cash)

Below list of random clients in Main

List<Client> clients = List.of(
            new Client("Ola", "Chrzaszcz", 34, new BigDecimal("200")),
            new Client("Ala", "Kowalsky", 24, new BigDecimal("4000")),
            new Client("Olaf", "Chrzaszcz", 19, new BigDecimal("3999")),
            new Client("Piotr", "Nowak", 21, new BigDecimal("2099")),
            new Client("Ola", "Szwed", 45, new BigDecimal("3000"))
)
;

2nd Class: Product (String name, Enum category, BigDecimal Price)

List<Product> products = List.of(
            new Product("Szynka", Category.A, new BigDecimal(29)),
            new Product("Ser", Category.B, new BigDecimal(22)),
            new Product("Chleb", Category.C, new BigDecimal(6)),
            new Product("Maslo", Category.D, new BigDecimal(4)),
            new Product("Kielbasa", Category.A, new BigDecimal(25)),
            new Product("Jajka", Category.A, new BigDecimal(8)),
            new Product("Szynka", Category.C, new BigDecimal(25))
);

Based on these 2 list new Map is created (key: person, value: Map<Product, quantity of sold products)

    //s1 - new map creation

 Map<Client, Map<Product,Integer>> customersWithTheirShopping = new HashMap<>();
    

//adding values to the map

customersWithTheirShopping.put(clients.get(0), Map.of(products.get(0),5));
customersWithTheirShopping.put(clients.get(1), Map.of(products.get(1),6));
customersWithTheirShopping.put(clients.get(2), Map.of(products.get(6),16));
customersWithTheirShopping.put(clients.get(3), Map.of(products.get(5),11));
customersWithTheirShopping.put(clients.get(4), Map.of(products.get(4),5));
customersWithTheirShopping.put(clients.get(4), Map.of(products.get(3),6));
customersWithTheirShopping.put(clients.get(2), Map.of(products.get(2),8));
customersWithTheirShopping.put(clients.get(4), Map.of(products.get(1),9));

GOAL -> EXPECTED RESULTS: using stream to create method that return client with top money spent (price x quantity)

QUESTION:

  1. How to do it? How to get to the value of the map which is value itself?!
  2. As this is map and key (client) is unique, we can not expect duplication in instances of Client class, right (with this map structure)? It means that each customer can do order just once, right?

WHAT I DID -> CURRENT BEHAVIOUR:

public Client clientWithMostExpShoping() {

    return quantitiesOfProductsBoughtByClient
            .entrySet()
            .stream()
            .max(Comparator.comparing(p -> p.getValue().entrySet().stream().max(
                    Comparator.comparing(x -> x.getKey().getPrice().multiply(BigDecimal.valueOf(x.getValue()))))))
};

//btw did not find similar case unfortunately

1

1 Answers

0
votes

Yes, currently in your code create Immutable Maps of Product and Integers, so you can't add new Products for the Client. Also you overwrite the Map Entries for the client, for example client with index 4 has Map.of(products.get(1),9) as value, because it is the last one and overwrites every previous assignment

If you want to have multiple products per client you could refactor to:

        customersWithTheirShopping.put(clients.get(0), new HashMap<>(Map.of(products.get(0), 5)));
        customersWithTheirShopping.put(clients.get(1), new HashMap<>(Map.of(products.get(1), 6)));
        customersWithTheirShopping.put(clients.get(2), new HashMap<>(Map.of(products.get(6), 16)));
        customersWithTheirShopping.put(clients.get(3), new HashMap<>(Map.of(products.get(5), 11)));
        customersWithTheirShopping.put(clients.get(4), new HashMap<>(Map.of(products.get(4), 5)));
        customersWithTheirShopping.get(clients.get(4)).put(products.get(3), 6);
        customersWithTheirShopping.get(clients.get(2)).put(products.get(2), 8);
        customersWithTheirShopping.get(clients.get(4)).put(products.get(1), 9);

Then you can use streams to get the correct client

Client clientWithMostExpense = customersWithTheirShopping
                .entrySet()
                .stream()
                .max(
                        // check all clients and compare their mostExpensiveProducts
                        Comparator.comparing(
                        client -> {
                            Map.Entry<Product, Integer> mostExpensiveProductForClient = client.getValue().entrySet()
                                    .stream().max(
                                            //Find most expensive product of all for client
                                            Comparator.comparing(product ->
                                                    product.getKey().getPrice().multiply(BigDecimal.valueOf(product.getValue())))
                                    ).get();
                            // return the full price of the most expensive product for comparison
                            return mostExpensiveProductForClient.getKey().getPrice().multiply(BigDecimal.valueOf(mostExpensiveProductForClient.getValue()));
                        }
                )).get().getKey();

Another solution would be to refactor quantity and product to a new class called BoughtProduct or something similiar, in which you could already calculate its price

public class BoughtProduct {
        Product product;
        Integer quantity;

        public BoughtProduct(Product product, Integer quantity) {
            this.quantity = quantity;
            this.product = product;
        }

        public Integer getQuantity() {
            return quantity;
        }

        public void setQuantity(Integer quantity) {
            this.quantity = quantity;
        }

        public Product getProduct() {
            return product;
        }

        public void setProduct(Product product) {
            this.product = product;
        }

        public BigDecimal getTotalPrice(){
            return this.product.getPrice().multiply(BigDecimal.valueOf(quantity));
        }
    }

Then you could use lists instead of a map:

        Map<Client, List<BoughtProduct>> customersWithTheirShopping = new HashMap<>();
        customersWithTheirShopping.put(clients.get(0), new ArrayList<>(List.of(new BoughtProduct(products.get(0), 5))));
        customersWithTheirShopping.put(clients.get(1), new ArrayList<>(List.of(new BoughtProduct(products.get(1), 6))));
        customersWithTheirShopping.put(clients.get(2), new ArrayList<>(List.of(new BoughtProduct(products.get(6), 16))));
        customersWithTheirShopping.put(clients.get(3), new ArrayList<>(List.of(new BoughtProduct(products.get(5), 11))));
        customersWithTheirShopping.put(clients.get(4), new ArrayList<>(List.of(new BoughtProduct(products.get(4), 5))));
        customersWithTheirShopping.get(clients.get(4)).add(new BoughtProduct((products.get(3)), 6)); //could use a check whether product exists, then you could increase quantity
        customersWithTheirShopping.get(clients.get(2)).add(new BoughtProduct(products.get(2),8));
        customersWithTheirShopping.get(clients.get(4)).add(new BoughtProduct(products.get(1),9));

And make the streams also a bit more readable:

Client clientWithMostExpense = customersWithTheirShopping
                .entrySet()
                .stream()
                .max(
                        // check all clients and compare their mostExpensive BoughtProduct in their list
                        Comparator.comparing(
                                //For each client get their most expensive product
                                client -> client.getValue()
                                        .stream().max(Comparator.comparing(BoughtProduct::getTotalPrice)).get()
                                        .getTotalPrice()
                        )).get().getKey();