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))
);

GOAL -> EXPECTED RESULTS: in class Main create the instance of the class Shopping where attribute is a Map with <key: Client, value: Map<Product, Integer> and fill it with random data // Integer - how many products were bought

QUESTION:

  1. How to create Shopping class with such an attibute
  2. How to create an instance of this Shopping class in Main with random data for practise purpose?

WHAT I DID -> CURRENT BEHAVIOUR:

  1. Separate class Shopping created:

Code

public class Shopping {

    private Map<Client, Map<Product,Integer>> quantitiesOfProductsBoughtByClient;
  1. Code in Main to create random object from Shopping class -> was trying diff combintation but still failed

Code

Map<Client, Map<Product,Integer>> s1 = new HashMap<>();
s1.put(clients.indexOf(0), new Map(products.indexOf(1),5) );

List<Shopping> productsBoughtByClient = List.of(
      new Map<Client, Map<Product,Integer>>(clients.indexOf(0), new Map<>(products.indexOf(0),5) ),
        );

Btw i tried to find similar question, but didn't find any,

2
Map is an interface. You cannot just create an interface with new. Instead of "new Map..." use "new HashMap...". Or use Map.of(...) - Ralf Renz
Hi Ralf. Thanks for your respond. Had an issue before with creating such a instance of that map, but now I managed to do that. //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),new Integer(5))); customersWithTheirShopping.put(clients.get(1), Map.of(products.get(1),6)); - qcp

2 Answers

2
votes

Here is an example as a starting point, where there is a method randomProductMap which accepts a list of products, shuffel this list, take a random number of products and produces a map with a random count of products. Note: I used lombok to save some lines of code, remove it if not needed.

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;


public class Test {

    public static void main(String[] args) {
        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"))
        );
        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))
        );
        Map<Client,Map<Product,Integer>> myMap = 
                clients.stream().collect(Collectors.toMap(
                        Function.identity(), c -> randomProductMap(products)));

        Shopping sh = new Shopping(myMap);

        sh.getQuantitiesOfProductsBoughtByClient()
                .forEach((client, prodMap) -> {
                    System.out.println(client.getName() + " " + client.getLastName() + " bought below products");
                    prodMap.forEach((key,value) -> {
                        System.out.println("\t" + value + " x " + key.getName());
                    });
                    //Edited
                    BigDecimal total = prodMap.entrySet().stream()
                        .map(e -> e.getKey().getPrice().multiply(BigDecimal.valueOf(e.getValue())))
                        .reduce(BigDecimal.ZERO, (p,q)-> p.add(q), BigDecimal::add);
                    System.out.println("and spent total amount of: " + total);
                    //Edited
                });
    }

    public static Map<Product,Integer> randomProductMap(List<Product> products){
        List<Product> copy = new ArrayList<>(products);
        Collections.shuffle(copy);
        Random r = new Random();
        List<Product> randomSizeList = copy.subList(0, r.nextInt(products.size()) + 1);
        return randomSizeList.stream()
                .collect(Collectors.toMap(Function.identity(), p -> r.nextInt(10)+1));
    }

    @Getter
    @Setter
    @AllArgsConstructor
    @ToString
    public static class Client {
        String name;
        String lastName;
        Integer age;
        BigDecimal cash;
    }

    @Getter
    @Setter
    @AllArgsConstructor
    @ToString
    public static class Product {
        String name;
        Enum category;
        BigDecimal Price;
    }

    public static enum Category {
        A, B, C, D;
    }

    @Getter
    @Setter
    @AllArgsConstructor
    @ToString
    public static class Shopping{
        private Map<Client, Map<Product,Integer>> quantitiesOfProductsBoughtByClient;
        
    }
}

Edited

public Client clientWithMostExpShoping() {
    return quantitiesOfProductsBoughtByClient.entrySet()
             .stream()
             .collect(Collectors.toMap(
                     Map.Entry::getKey,  
                     entry -> entry.getValue().entrySet().stream()
                             .map(prodMap -> prodMap.getKey().getPrice().multiply(BigDecimal.valueOf(prodMap.getValue())))
                             .reduce(BigDecimal.ZERO, (prod1,prod2)-> prod1.add(prod2), BigDecimal::add)
             )).entrySet().stream()
             .max(Comparator.comparing(Map.Entry::getValue))
             .get().getKey();                            
 }

The above method should give you the client who spend the most. A brief summary what is going on there: iterate over the map quantitiesOfProductsBoughtByClient collect to map keeping the client as key and multiply the price of each product with quantity and sum to a total and finally get the client with most spendings by iterating over the resulting map using Stream.max.

While the above will return a client as requested, you will get no additional info for example how much exactly he spent. Therefore I would suggest that you change the return value of the method to either Map.Entry<Client, BigDecimal> to get both client and total spendings. For this just leave out the last .getKey();

...
.max(Comparator.comparing(Map.Entry::getValue))
             .get();

or even simpler just return a Map<Client, BigDecimal> client to total spending map and leave the determination of the client with highes, lowest, average or whatever to the caller of the method:

public Map<Client, BigDecimal> totalSpendingsByClient() {
         return quantitiesOfProductsBoughtByClient.entrySet()
                 .stream()
                 .collect(Collectors.toMap(
                         Map.Entry::getKey,  
                         entry -> entry.getValue().entrySet().stream()
                                 .map(prodMap -> prodMap.getKey().getPrice().multiply(BigDecimal.valueOf(prodMap.getValue())))
                                 .reduce(BigDecimal.ZERO, (prod1,prod2)-> prod1.add(prod2), BigDecimal::add)
                 ));                            
     }

Then you can use to get the client with max or min spending for example like:

Map<Client, BigDecimal> clientsTotal = totalSpendingsByClient();
Client cliWithMaxTotal = clientsTotal.entrySet().stream().max(Comparator.comparing(Map.Entry::getValue)).get().getKey();     
Client cliWithMinTotal = //the same as above just change max to min     
       
0
votes

Solved, thanks a lot for help //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),new Integer(5)));

customersWithTheirShopping.put(clients.get(1),Map.of(products.get(1),6));