Home > Enterprise >  Add value to map if the key has generated dates
Add value to map if the key has generated dates

Time:09-19

I have a class as a helper to an entity

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ChildReports {
    private LocalDate date;
    private BigDecimal amount;
}

I have entries in the database, for example:

|    date         |        amount       |
 ----------------- --------------------- 
| 2022-06-20      |        10000        |
| 2023-01-15      |         8000        |
| 2023-07-05      |         6500        |
| 2024-02-11      |         5000        |
| 2024-08-18      |         1000        |

Now I want to fill in the gaps between the dates, so that the previous digits are written in those months in which there is no data. At the end it should look something like this:

|    date         |        amount       |
 ----------------- --------------------- 
| 2022-06-20      |        10000        |
| 2022-07-20      |        10000        |
| 2022-08-20      |        10000        |
| 2022-09-20      |        10000        |
       .............
| 2022-12-20      |        10000        |
| 2023-01-15      |         8000        |
| 2023-02-15      |         8000        |
| 2023-03-15      |         8000        |

             and so on

In the service, I started writing a method in which I took the entire range of dates, starting from dateStart and ending with dateEnd.

        LocalDate dateStart = Objects.requireNonNull(childReports.stream().findFirst().orElse(null)).getDate();
        LocalDate dateEnd = Objects.requireNonNull(childReports.stream().reduce((first, second) -> second).orElse(null).getDate());
        long monthsBetween = ChronoUnit.MONTHS.between(dateStart, dateEnd);
        List<LocalDate> totalMonths = LongStream.iterate(0,i -> i 1)
                                      .limit(monthsBetween).mapToObj(dateStart::plusMonths)
                                      .collect(Collectors.toList());

        Map<List<LocalDate>, BigDecimal> map = new HashMap<>();
        
        for (ChildReports childReport : childReports) {
            BigDecimal amount = childReport.getAmount();
            map.put(totalMonths, amount);
        }
        System.out.println(map);

I get this interval correctly, but now I want to add a value - amunt, so that at the end the result would come out, as I indicated above. Can't get this result

CodePudding user response:

Make sure to adjust the start date to be on the same day as the end date before finding the months beween them:

ChronoUnit.MONTHS.between(dateStart.withDayOfMonth(dateEnd.getDayOfMonth()), dateEnd)

The rest of the things should be pretty straightforward using nested loops. Given below is a sample report:

class ReportRow {
    private LocalDate date;
    private BigDecimal amount;

    // Parametrised constructor and getters

    @Override
    public String toString() {
        return date   " | "   amount;
    }
}

public class Solution {
    public static void main(String[] args) {
        List<ReportRow> originalReport = List.of(
                new ReportRow(LocalDate.of(2022, 6, 20), BigDecimal.valueOf(10000)),
                new ReportRow(LocalDate.of(2023, 1, 15), BigDecimal.valueOf(8000)),
                new ReportRow(LocalDate.of(2023, 7, 5), BigDecimal.valueOf(6500)));

        System.out.println("Before:");
        originalReport.forEach(System.out::println);

        List<ReportRow> updatedReport = new ArrayList<>();

        int size = originalReport.size();
        if (size > 0)
            updatedReport.add(originalReport.get(0));

        if (size > 1) {
            for (int i = 1; i < size; i  ) {
                ReportRow lastRow = originalReport.get(i - 1);
                ReportRow currentRow = originalReport.get(i);

                BigDecimal lastAmount = lastRow.getAmount();
                LocalDate dateStart = lastRow.getDate();
                LocalDate dateEnd = currentRow.getDate();
               
                if (ChronoUnit.MONTHS.between(dateStart.withDayOfMonth(dateEnd.getDayOfMonth()), dateEnd) > 1) {
                    for (LocalDate date = dateStart.plusMonths(1); date.isBefore(dateEnd); date = date.plusMonths(1))
                        updatedReport.add(new ReportRow(date, lastAmount));
                } 
                
                updatedReport.add(currentRow);
            }
        }

        System.out.println("After:");
        updatedReport.forEach(System.out::println);
    }
}

Output:

Before:
2022-06-20 | 10000
2023-01-15 | 8000
2023-07-05 | 6500
After:
2022-06-20 | 10000
2022-07-20 | 10000
2022-08-20 | 10000
2022-09-20 | 10000
2022-10-20 | 10000
2022-11-20 | 10000
2022-12-20 | 10000
2023-01-15 | 8000
2023-02-15 | 8000
2023-03-15 | 8000
2023-04-15 | 8000
2023-05-15 | 8000
2023-06-15 | 8000
2023-07-05 | 6500

Note: if you plan to use a Map and intend to maintain the order, you should use LinkedHashMap instead of HashMap.

  • Related