Как объединить Список Карт Карт в Карту Карт? - PullRequest
0 голосов
/ 30 ноября 2018

Не могли бы вы помочь мне с Java Streams?

Как видно из названия, мне нужно объединить List<Map<String, Map<String, Genuineness>>> в Map<String, Map<String, Genuineness>>.

Список представлен как List<Map<String, Map<String, Genuineness>>> и выглядит так:

[  
   {  
      "USER_1":{  
         "APP_1":{  
            "total":1,
            "totalGenuine":1,
            "totalDevelopment":1
         }
      },
      "USER_2":{  
         "APP_1":{  
            "total":1,
            "totalGenuine":1,
            "totalDevelopment":1
         },
         "APP_2":{  
            "total":2,
            "totalGenuine":2,
            "totalDevelopment":2
         }
      }
   },
   {  
      "USER_1":{  
         "APP_1":{  
            "total":1,
            "totalGenuine":1,
            "totalDevelopment":1
         }
      },
      "USER_2":{  
         "APP_1":{  
            "total":1,
            "totalGenuine":1,
            "totalDevelopment":1
         },
         "APP_2":{  
            "total":2,
            "totalGenuine":2,
            "totalDevelopment":2
         }
      }
   }
]

Итак, как вы можете видеть, дубликаты ключей могут быть везде.Моя цель - объединить их в Map<String, Map<String, Genuineness>>, объединив Genuineness.Слияние Genuineness просто означает возвращение нового объекта с суммированными значениями total, totalGenuine и totalDevelopment.

Вот моя реализация, которая терпит неудачу:

final Map<String, Map<String, Genuineness>> map = taskHandles.stream().map(this::mapTaskHandle)
                .flatMap(m -> m.entrySet().stream()).collect(
                        Collectors.toMap(Map.Entry::getKey, e -> e.getValue().entrySet().stream()
                                .collect(
                                        Collectors.toMap(Map.Entry::getKey,
                                                g -> new Genuineness(g.getValue().getTotal(), g.getValue().getTotalGenuine(), g.getValue().getTotalDevelopment()),
                                                (g1, g2) -> new Genuineness(g1.getTotal() + g2.getTotal(),
                                                        g1.getTotalGenuine() + g2.getTotalGenuine(),
                                                        g1.getTotalDevelopment() + g2.getTotalGenuine()
                                                )
                                        )
                                )
                        )
                );

Этозавершается с сообщением:

java.lang.IllegalStateException: Duplicate key {TEST_33_33_APP_1=live.attach.billing.domain.model.billing.Genuineness@951b6fe}

Итак, кажется, что в моей реализации я указал, как объединить внутреннюю карту, но не сделал слияние значений внешней карты, и я не знаю, как это сделатьэто.

Я очень ценю вашу помощь.Заранее спасибо!

ОБНОВЛЕНИЕ: Ожидаемый результат:

   {  
      "USER_1":{  
         "APP_1":{  
            "total":2,
            "totalGenuine":2,
            "totalDevelopment":2
         }
      },
      "USER_2":{  
         "APP_1":{  
            "total":2,
            "totalGenuine":2,
            "totalDevelopment":2
         },
         "APP_2":{  
            "total":4,
            "totalGenuine":4,
            "totalDevelopment":4
         }
      }
   }

Ответы [ 2 ]

0 голосов
/ 30 ноября 2018

Хотя предлагаемое решение Aomine кажется правильным, вы можете улучшить читабельность кода и упростить его, определив BinaryOperator<Genuineness> как:

BinaryOperator<Genuineness> remappingGenuineness = (g1, g2) -> new Genuineness(g1.getTotal() + g2.getTotal(),
        g1.getTotalGenuine() + g2.getTotalGenuine(),
        g1.getTotalDevelopment() + g2.getTotalGenuine()
);

а затем далее использовать его как:

final Map<String, Map<String, Genuineness>> map = taskHandles.stream()
        .flatMap(m -> m.entrySet().stream())
        .collect(Collectors.toMap(Map.Entry::getKey,
                e -> e.getValue().entrySet().stream().collect(
                        Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, remappingGenuineness)),
                (a, b) -> {
                    a.forEach((k, v) -> b.merge(k, v, remappingGenuineness));
                    return b;
                }));
0 голосов
/ 30 ноября 2018

Честно говоря, это ужасная структура данных, с которой нужно работать, и разработчикам этого кода будет трудно найти проблемы, которые возникают.

Вы должны сделать шаг назад и рассмотреть возможность рефакторинга кода, любойкак вы можете решить недостающую часть, используя следующую функцию слияния: toMap:

(l, r) -> {
      r.forEach((k, v) -> l.merge(k, v,
                    (bi, bii) -> new Genuineness(bi.getTotal() + bii.getTotal(),
                               bi.getTotalGenuine() + bii.getTotalGenuine(),
                               bi.getTotalDevelopment() + bii.getTotalGenuine())));
       return l;
}

Полный код:

taskHandles.stream().map(this::mapTaskHandle)
                .flatMap(m -> m.entrySet().stream()).collect(
                        Collectors.toMap(Map.Entry::getKey, e -> e.getValue().entrySet().stream()
                                .collect(
                                        Collectors.toMap(Map.Entry::getKey,
                                                g -> new Genuineness(g.getValue().getTotal(), g.getValue().getTotalGenuine(), g.getValue().getTotalDevelopment()),
                                                (g1, g2) -> new Genuineness(g1.getTotal() + g2.getTotal(),
                                                        g1.getTotalGenuine() + g2.getTotalGenuine(),
                                                        g1.getTotalDevelopment() + g2.getTotalGenuine()
                                                )
                                        )
                                ),(l, r) -> {
                                  r.forEach((k, v) -> l.merge(k, v,
                                          (bi, bii) -> new Genuineness(bi.getTotal() + bii.getTotal(),
                                                  bi.getTotalGenuine() + bii.getTotalGenuine(),
                                                  bi.getTotalDevelopment() + bii.getTotalGenuine())));
                                  return l;
                                }

                        )
                );

Ideone

...