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());
});
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);
});
}
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 =