Search code examples
javareportexport-to-csvreportingexport-to-excel

Generating csv file for multiple loop in Java?


I am trying to generate a report with cascade values a shown below:

| Country      | City     | Town     |
--------------------------------------
| Country A    | City X   | Town 1   |
| Country A    | City X   | Town 2   |
| Country A    | City Y   | Town 1   |
| Country A    | City Y   | Town 2   |
| Country B    | City Q   | Town 1   |
| Country B    | City Q   | Town 2   |
| Country B    | City T   | Town 1   |
| Country B    | City T   | Town 2   |


I can properly generate Country and City as shown above, but I pass a j index value for each city as shown below. However, for towns, I will also need another index variable like k and loop.

public MultipartFile exportData() throws IOException {

    // code omitted for brevity

    int rowCount = 0;
    final List<CountryDTO> countryList = countryService.findAll();

    int iSize = countryList.size();
    for (int i = 0; i < iSize; i++) {
        int jSize = countryList.get(i).getCityList().size();
        for (int j = 0; j < jSize; j++) {
            int kSize = countryList.get(i).getCityList().get(j).getTownList().size();
            for (int j = 0; k < kSize; k++) {
                Row row = sheet.createRow(rowCount++);
                write(countryList.get(i), row, j, k);
            }
        }
    }

    // code omitted for brevity
}

private static void write(CountryDTO country, Row row, int j) {

    Cell cell = row.createCell(0);
    cell.setCellValue(country.getName());

    cell = row.createCell(1);
    cell.setCellValue(country.getCityList().get(j).getName());

    cell = row.createCell(2);
    cell.setCellValue(country.getCityList().get(j).getTownList().get(k).getName());
}

I am not sure if there is a better approach for this. As I am new for generating report in Java, I have no idea how to proceed with the following approach (if it is ok I will use this approach as it was already used in the current project).


Solution

  • For-each

    You could use for-each syntax rather than indexed for loop.

    for( Country country : countryService.findAll() )
    {
        for( City city : country.getCityList() )
        {
            for( Town town : city.getTownList() )
            {
                write( country , city , town ) ;
            }
        }
    }
    

    Notice that we redefined the write method to be ignorant of the originating data structure. It’s job is to write data, not retrieve it. No need for that method to understand the nested lists.

    Study up on Separation Of Concerns. A method and a class should know as little about the outside world as possible. This prevents your code from becoming “brittle”, where one little change causes your code to break all over the place.

    Records

    I would go further in Java 16+ to pass a record object to the write method. A record is brief way to write a class whose main purpose is to communicate data transparently and immutably. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString.

    record CountryCityTown( String country , String city , String town ) {}
    

    Change write method to take a single object of that type.

    void write ( CountryCityTown countryCityTown ) { … }