Kevin Valdez
Kevin Valdez

Reputation: 389

Recursively filter an object with a recursive list of children

Given this object:

public class Menu {

  private String id;
  private String matchCode;
  private List<Menu> children;
  
  //getters and setters

  /**
   * Flattened funcition to recursive children list
   */
  public Stream<Menu> flatenned() {
    return Stream.concat(
          Stream.of(this),
          children.stream().flatMap(Menu::flatenned));
  }

}

I need to filter a List<Menu> and remove all (parent) items that doesn't match to a given matchCode. I also need to filter all the children (at this point there can be 'N' children) by the same field (matchCode).

Since the children is a recursive list structure I found that the method flatenned can help achieve this. (see Konrad Garus: Walking Recursive Data Structures Using Java 8 Streams).

So far I have this:

private List<Menu> filterByMatchRoleCode(List<Menu> menuList) {
  return menuList.stream()
    .filter(p -> "string".matches(p.getMatchCode()))
    .map(c -> c.getChildren()
          .stream()
          .map(o -> o.flatenned()
                .map(Menu::getMatchCode)
                .filter(v -> "string".matches(v)).collect(Collectors.toList())));
}

This method filterByMatchRoleCode gives an error trying to return the value.

What am I missing, or is there a different approach to solve this?

Upvotes: 1

Views: 907

Answers (2)

M. Justin
M. Justin

Reputation: 21258

Here is a recursive solution that copies rather than modifies the values. It also doesn't use the flattened stream approach.

List<Menu> filterByMatchRoleCode(List<Menu> menuList) {
    return menuList.stream()
            .filter(m -> "string".matches(m.getMatchCode()))
            .map(this::copyWithFilteredChildren)
            .toList();
}

private Menu copyWithFilteredChildren(Menu menu) {
    Menu filtered = new Menu();
    filtered.setId(menu.getId());
    filtered.setMatchCode(menu.getMatchCode());
    filtered.setChildren(filterByMatchRoleCode(menu.getChildren()));
    return filtered;
}

Upvotes: 0

Ravi Gupta
Ravi Gupta

Reputation: 224

I think could be simpler.

    private List<Menu> filterByMatchRoleCode(List<Menu> menuList) {
        return menuList.stream()
                .peek( x -> x.setChildren( filterByMatchRoleCode(x.children)))
                .filter(p -> "string".matches(p.getMatchCode()))
                .collect(Collectors.toList());
    }

Upvotes: 1

Related Questions