Find each column in a table in my java 8 stream

Viewed 3257

I am using HtmlUnit to get an HtmlTable. I am trying to get a List of cells of each column.

So far, in the code I've tried I can find the first column. How can I iterate through each column and run some code in them?

I'd like to make sure they're all sorted alphabetically, but I just need to figure out where to place that code.

EDIT: I found my answer. I think I worded the question wrong. I needed to get each column and put them into their own collection. In the original example, I showed only column1. But I need EACH column (based upon how many cells are in each row). Below is some code that worked. But it may be able to be optimized better.

HtmlPage htmlPage = webClient.getPage("http://localhost:8080/myurl");

    HtmlTable myTable = htmlPage.getHtmlElementById("mytable");

    // find the number of columns by grabbing the first row and returning the number
    // of cells within the first row
    int numberOfColumns = myTable.getRows().stream().map(row -> row.getCells()).findFirst().get()
            .size();

    // initialize columns
    List<List<String>> columns = new ArrayList<List<String>>(numberOfColumns);

    // initialize new arraylists for each column based upon the number of columns
    for (int i = 0; i < numberOfColumns; i++)
        columns.add(new ArrayList<>());

    // iterate through each column
    for (int columnIndex = 0; columnIndex < numberOfColumns; columnIndex++) {

        // iterate through each row
        for (int rowIndex = 0; rowIndex < myTable.getRows().size(); rowIndex++) {

            String asText = myTable.getCellAt(rowIndex, columnIndex).asText();
            columns.get(columnIndex).add(asText);
        }
    }

    //iterate through the columns and do stuff!
    columns.forEach(a -> {
        //do stuff to the column such as verify it was sorted, or sort it yourself etc
        System.out.println("column" + a.toString());
        a.forEach(b -> {
            //do stuff 
            LOG.info(b);
        });
    });
4 Answers

You can just do this as part of your join as single stream:

webClient.getPage("http://localhost:8080/myUrl")
         .getHtmlElementById("myTable")
         .getRows()
         .stream()
         .map(row -> row.getCells().stream().findFirst().get().asText())
         .sort((o1, o2) -> o1.compareTo(o2)) // make alphabetical
         .collect(Collectors.joining("|"));

You can collect it to a List of List's:

List<List<HtmlTableCell>> columns = 
                          myTable.getRows()
                                 .stream()
                                 .map(row -> row.getCells()
                                                .stream()
                                                .collect(Collectors.toList())
                                 .collect(Collectors.toList());

And then when you need to log:

LOG.info(columns.stream()
                .flatMap(List::stream)                    
                .map(m -> m.asText())
                .sorted()         //Sort the list
                .collect(Collectors.joining("|")));

If you want to get the table as a list of lists (List<List<HtmlTableCell>>) this will do it

List<List<HtmlTableCell>> table = myTable.getRows().stream()
        .map(row -> row.getCells().stream().collect(Collectors.toList())
        .collect(Collectors.toList());

Or, if you won't need the List later, you can skip collecting to list and execute your code instead

List<List<HtmlTableCell>> table = myTable.getRows().stream()
        .map(row -> row.getCells().stream().collect(Collectors.toList())
        .forEachOrdered(cellList -> System.out.println(cellList));

As you want a List<HtmlTableCell> representing the data in a given column of your html table you need to use getCellAt which takes a row index and column index.

IntStream.range(0, numberOfColumns)
         .mapToObj(colIndex -> IntStream.range(0, numberOfRows)
             .mapToObj(rowIndex -> myTable.getCellAt(rowIndex, colIndex)).collect(toList())) 
         .collect(toList()); 

where numberOfColumns should be replaced with the number of columns in your HTML table and the numberOfRows should be replaced with the number of rows in your HTML table.

This will yield a List<List<HtmlTableCell>> where each List<HtmlTableCell> is all the cells for each column.


For completeness here is how you can sort each List<HtmlTableCell> i.e. each columns data.

List<List<HtmlTableCell>> result = 
     IntStream.range(0, numberOfColumns)
              .mapToObj(colIndex -> IntStream.range(0, numberOfRows)
                     .mapToObj(rowIndex -> myTable.getCellAt(rowIndex, colIndex))
                     .sorted(Comparator.comparing(HtmlTableCell::asText))
                     .collect(toList())) 
              .collect(toList()); 

and log it:

// concatenate each cell data of each column separated by a pipe and then separate each column data by a line separator.
String joined = result.stream()
      .map(l -> l.stream().map(HtmlTableCell::asText).collect(Collectors.joining("|")))
      .collect(Collectors.joining(System.lineSeparator()));
// log it! 
LOG.info(joined);

Note that if all you're doing is logging then it's not worth collecting to an intermediate List<List<HtmlTableCell>> rather, you can get the result you require as:

 String joined = IntStream.range(0, numberOfColumns)
                          .mapToObj(colIndex -> IntStream.range(0, numberOfRows)
                                .mapToObj(rowIndex -> myTable.getCellAt(rowIndex, colIndex).asText())
                                .sorted()
                                .collect(Collectors.joining("|")))
                        .collect(Collectors.joining(System.lineSeparator()));
LOG.info(joined);
Related