0

I asked a question some time back about parsing CSV files for a single matching row. In the example shown below, I use a bufferedreader to read the header row as the first step. Using this row, I parse the column names and then proceed to search for matching rows. The filter criteria I need to search for the matching row should be based on 2 column values, instead the code shown below only returns the 1 row - presumably because I use

.findFirst().get();

Instead I need something along the following lines (but this code is not valid)

List<String> rowCols = reader.lines()
    //.skip(1)
    .map((line) -> Arrays.asList(line.split(",")))
    .filter(list -> 
        !list.get(col1Index).equalsIgnoreCase("0:00") && 
        !list.get(col2Index).equalsIgnoreCase("0:00"))
    .findFirst().get();

as this also just returns 1 row - however the filter matches multiple rows.

I now need to return multiple matching rows but I cannot figure out the correct syntax.

String fileName = ...
try (BufferedReader reader = new BufferedReader(
    new InputStreamReader(ftpClient.
        retrieveFileStream(fileName)))){
    List<String> columns = reader.lines()
        .findFirst()
        .map(line -> Arrays.asList(line.split(",")))
        .get();
    // find the relevant sections from the CSV file
    // we are only interested in the row with the CA ServiceName
    int serviceNameIndex = columns.indexOf("ServiceName");
    int col1Index = columns.indexOf("Column1");
    int col2Index = columns.indexOf("Column2");
    // we need to know the index positions of the columns
    // also note that due to using a BufferedReader we don't
    // have to re-read the csv file to extract the values
    List<String> rowCols = reader.lines()
        //.skip(1)
        .map((line) -> Arrays.asList(line.split(",")))
        .filter(list -> list.get(serviceNameIndex).equalsIgnoreCase("service1"))
        .findFirst().get();
    EnumMap<Parameter, String> params = new EnumMap(Parameter.class) {{
        put(Parameter.ServiceName, rowCols.get(serviceNameIndex));
        put(Parameter.Column1, rowCols.get(col1Index));
        put(Parameter.Column2, rowCols.get(col2Index));
    }};
    params.put("service1", params);
}
Community
  • 1
  • 1
johnco3
  • 2,401
  • 4
  • 35
  • 67
  • 1
    Instead of calling `.findFirst().get();`, why are you not collecting the elements into a list with `collect(Collectors.toList())` (since you want all rows)? – Tunaki Jun 16 '16 at 17:17
  • @Tunaki unfortunately that does not work unless I remove the map and filter lines (and then it just returns all rows except the header row which is not what I want. The map entry splits the current line into a list of col values that I need to filter on. It says something about inference variable T has incompatible bounds - equality constraints String, lower bounds: List – johnco3 Jun 16 '16 at 17:26
  • 1
    @johnco3 "unfortunately that does not work unless I remove the map and filter line" what do you mean by that? What doesn't work, show what you've tried – george Jun 16 '16 at 21:18

2 Answers2

3

I am not sure what you are trying to achieve exactly but my best guess is that you would like to return a list of strings based on the splitting of the line like line.split(","). Your code doesn't work because after your filter method you return the whole list of strings but you need to further stream the list. In other words you need to flatten the list to get its contents like below

List<String> rowCols = reader.lines()
    .map((line) -> Arrays.asList(line.split(",")))
    .filter(list -> 
        !list.get(col1Index).equalsIgnoreCase("0:00") && 
        !list.get(col2Index).equalsIgnoreCase("0:00"))
    .flatMap(e->e.stream())
    .collect(Collectors.toList());

This should collect the contents(strings which you've split) of each line into a single list.

george
  • 3,102
  • 5
  • 34
  • 51
2

I think you are expecting a List of string in a list to get all the matching rows. The below should work in that case:

        List<List<String>> rowCols = reader.lines()
                //.skip(1)
                .map((line) -> Arrays.asList(line.split(",")))
                .filter(list ->
                        !list.get(col1Index).equalsIgnoreCase("0:00") &&
                                !list.get(col2Index).equalsIgnoreCase("0:00"))
                .collect(Collectors.toList());

Edited as per comment by @Tunaki and @george

Vijay
  • 542
  • 4
  • 15
  • @Tunaki your guess was correct, one question though do you know how I might return a Map> to save a bit of post processing on the List>. I am having a bit of trouble making the .collect(Collectors.toMap()); call the String part of the Map is just column 1 of the filtered results while the EnumMap should be just the mappings from Parameter.Column1 and Parameter.Column2) (slightly different than in my question where I alao include the serviceNameIndex (column 0) in the EnumMap. I know I should probably ask another question for this... – johnco3 Jun 17 '16 at 15:28