2

I'm trying to code a multilevel filter. I have folders and files.

public class Folder {
  String name;
  List<Folder> folders;
  List<File> files;
  ...
}

public class File {
  String name;
  String type;
  ...
}

I need a filter who looks for some property e.g. name, which can be a folder/file name. I wrote a piece of code that works for one level, but I don't how could I do that looking for two level loops.

The final result would be a list of folders: the ones which names match the 'name filter' or folders which file names match the filter.

List<Folder> result = folders.stream()
  .filter(folder -> folder.getFiles().stream().anyMatch(file -> file.getName().contains(filter)))
  .collect(Collectors.toList());
1
  • 2
    a method which uses recursion is perhaps a better idea Commented Mar 6, 2018 at 20:50

2 Answers 2

3

I think it will be better to add a helper method to the Folder class that will flatten nested folders into a Stream recursively:

class Folder {
    String name;
    List<Folder> folders;
    List<File> files;

    Stream<Folder> flatten() {
        return Stream.concat(Stream.of(this), folders.stream().flatMap(Folder::flatten));
    }
}

After that, you can get the final result using flatMap(...)

List<Folder> result = folders.stream()
    .flatMap(Folder::flatten)
    .filter(folder -> folder.getFiles()
        .stream()
        .anyMatch(file -> file.getName().contains(filter)))
    .collect(Collectors.toList());
Sign up to request clarification or add additional context in comments.

Comments

0

A running test which filters by filename across folders (without any helper method).

The predicate in your filter method is what makes the difference alongside flatMap as @Ernest refers to that flattens all the File elements into one list.

private static final String FILE_NAME_FILTER = "1";
private static final String HOME_FOLDER_FILTER = "home";
private static final String VAR_FOLDER_FILTER = "var";
private static final String BIN_FOLDER_FILTER = "bin";

@Test
public void test() {
    File f1 = new File("1", "txt");
    File f2 = new File("1", "json");
    File f3 = new File("1", "yml");
    File f4 = new File("4", "doc");

    Folder folder1 = new Folder(HOME_FOLDER_FILTER, asList(f1, f2));
    Folder folder2 = new Folder(VAR_FOLDER_FILTER, asList(f2, f3));
    Folder folder3 = new Folder(BIN_FOLDER_FILTER, asList(f3, f4));
    List<Folder> folders = asList(folder1, folder2, folder3);

    List<File> files = folders.stream()
            .filter(folder -> folder.getName().equals(HOME_FOLDER_FILTER))
            .map(Folder::getFiles) // no helper method
            .flatMap(List::stream)
            .filter(file -> file.getName().equals(FILE_NAME_FILTER))
            .collect(Collectors.toList());
    Assert.assertEquals(2, files.size());
}

Pretty much along the line of what @Oleksandr suggests.

Edit: I see i missed out on the fact that a folder can contain a folder. Should I delete this answer?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.