AbrA
AbrA

Reputation: 480

How to convert Map<String, Map<String, String>> to String?

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

Upvotes: 0

Views: 661

Answers (1)

WJS
WJS

Reputation: 40024

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(", "));

Upvotes: 3

Related Questions