Да, выглядит как хороший кандидат для реализации с Collectors.toMap
. Взгляните на реализацию ниже (я использовал lombok (https://projectlombok.org/), чтобы сделать его более читабельным):
Map<PassType, PassTypeRate> group(List<DataEntry> entries) {
return entries.stream()
.collect(Collectors.toMap(
DataEntry::getPassType,
entry -> PassTypeRate.builder()
.adultRate(entry.getAgeType() == ADULT ? entry.getRate() : BigDecimal.ZERO)
.childRate(entry.getAgeType() == CHILD ? entry.getRate() : BigDecimal.ZERO)
.build(),
(rate1, rate2) -> PassTypeRate.builder()
.childRate(rate1.getChildRate().add(rate2.getChildRate()))
.adultRate(rate1.getAdultRate().add(rate2.getAdultRate()))
.build()
));
}
И краткое объяснение:
Во-первыхнам нужно определить PassType
как ключ карты
Во-вторых, нам нужно отобразить наши DataEntry
на отдельные PassTypeRate
объекты. Если это запись Adult
, тоnew PassTypeRate
должен иметь значение в поле adultRate
и ноль в childRate
. И наоборот.
Но некоторые записи могут иметь одинаковые PassType
! Поэтому мынужно определить третью функцию - функцию слияния. Как мы объединяем PassTypeRate
объекты? Добавляя правильные тарифы. Функция слияния возвращает новое PassTypeRate
в результате добавления двух PassTypeRate
объектов.
Я также подготовил несколько тестов, чтобы проверить, работает ли решение - и кажется, что оно работает :) Тестовый пример ниже:
public class RateGroupingTest {
private RateGrouping subject = new RateGrouping();
@Test
public void groups() {
//given
List<DataEntry> entries = List.of(
new DataEntry(ADULT, X, new BigDecimal("3")),
new DataEntry(ADULT, Y, new BigDecimal("5")),
new DataEntry(ADULT, Z, new BigDecimal("7")),
new DataEntry(CHILD, X, new BigDecimal("11")),
new DataEntry(CHILD, Y, new BigDecimal("13")),
new DataEntry(CHILD, Z, new BigDecimal("17")),
new DataEntry(ADULT, X, new BigDecimal("13")),
new DataEntry(ADULT, Y, new BigDecimal("25")),
new DataEntry(ADULT, Z, new BigDecimal("37")),
new DataEntry(CHILD, X, new BigDecimal("411")),
new DataEntry(CHILD, Y, new BigDecimal("513")),
new DataEntry(CHILD, Z, new BigDecimal("617"))
);
//when
Map<PassType, PassTypeRate> actual = subject.group(entries);
//then
assertThat(actual.get(PassType.X))
.isEqualTo(
PassTypeRate.builder()
.childRate(new BigDecimal("422"))
.adultRate(new BigDecimal("16"))
.build()
);
assertThat(actual.get(PassType.Y))
.isEqualTo(
PassTypeRate.builder()
.childRate(new BigDecimal("526"))
.adultRate(new BigDecimal("30"))
.build()
);
assertThat(actual.get(PassType.Z))
.isEqualTo(
PassTypeRate.builder()
.childRate(new BigDecimal("634"))
.adultRate(new BigDecimal("44"))
.build()
);
}
}