0

I have an entity

public class Report {
    private String departmentName;
    private BigDecimal amount;
    private String currency;
}

and I want to calculate the amount for each department depending on the currency and write the result in the line. Example:

    List<Report> list = new ArrayList<>();
    list.add(new Report("Moscow", new BigDecimal(100), "USD"));
    list.add(new Report("Moscow", new BigDecimal(100), "USD"));
    list.add(new Report("Paris", new BigDecimal(100), "USD"));
    list.add(new Report("Moscow", new BigDecimal(200), "RUR"));

Result: "Moscow 200 usd, Paris 100 USD, Moscow 200 RUR"

For it:

Map<String, Map<String, String>> collect = list.stream().collect(
            Collectors.groupingBy(doc -> doc.getDepartmentName(),
                    Collectors.groupingBy(Report::getCurrency,
                            Collectors.collectingAndThen(Collectors.toList(), doc -> {
                                BigDecimal sum = doc.stream()
                                        .map(A::getAmount)
                                        .filter(Objects::nonNull)
                                        .reduce(BigDecimal.ZERO, BigDecimal::add);

                                String cur = doc.stream().map(Report::getCurrency)
                                        .findFirst().get();
                                return sum.toString() + " " + cur;
                            }))));

And I receive a result:

{Paris={USD=100 USD}, Moscow={USD=200 USD, RUR= 200 RUR}} -- correct

but I don't know how to convert Map<String, Map<String, String>> to String

2
  • 2
    Please clarify what you mean to convert map to string. You have already converted it to string(using .toString()) to show the above result. Adding example of desired output format would greatly help. Commented Jun 7, 2022 at 14:39
  • @Chaosfire result of toString() = '{Paris={USD=100 USD}, Moscow={USD=200 USD, RUR= 200 RUR}}' but I need 'Paris - 100 USD, Moscow - 200 USD, Moscow- 200 RUR' Commented Jun 7, 2022 at 14:45

1 Answer 1

3

Desired result is "Moscow 200 usd, Paris 100 USD, Moscow 200 RUR" Try it like this using flatMap.

  • stream the entrySet of the main map.
  • flatMap the inner map's values via entrySet again.
  • the using the key of the outer map and the value of the inner, map them to a string.
  • Then join the strings with a collector.
String result = map.entrySet().stream()
        .flatMap(e -> e.getValue().entrySet().stream()
                .map(e2 -> e.getKey() + " " + e2.getValue()))
        .collect(Collectors.joining(", "));
System.out.println(result);

prints

Paris 100 USD, Moscow 200 RUR, Moscow 200 USD

Note: Since maps are unordered, the order of the elements may not be consistent from run to run based on sums, additional Reports, etc.

I don't know if your Map is required as is or a way to get the result, but you could also do it like this.

  • use groupingBy as before but use toMap as the collector.
Map<String, Map<String, BigDecimal>> map =
        list.stream()
                .collect(Collectors.groupingBy(
                        Report::getDepartmentName,
                        Collectors.toMap(Report::getCurrency,
                                Report::getAmount, BigDecimal::add)));
  • Then you would do it similar to before but convert the BigDecimal to a String here and append the currency type(e2.getKey()) to that.
String result = map1.entrySet().stream().flatMap(e -> e
                .getValue().entrySet().stream()
                .map(e2 -> e.getKey() + " " + e2.getValue().toString()
                        + " " + e2.getKey()))
                .collect(Collectors.joining(", "));
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.