Как отфильтровать список каталогов, используя свойство из результирующего списка в Java? - PullRequest
0 голосов
/ 11 декабря 2018

У меня есть каталог с именем /home/ftp, который содержит такие подкаталоги, как этот:

dir1
dir2
dir3
dir1.ok (this is just an empty file)
dir3.ok (this is just an empty file)

Код должен обрабатывать файлы только в тех каталогах, которые имеют соответствующий ".ok" файл. Так, например,в этом примере код должен выбрать те файлы для обработки, которые находятся под dir1 и dir3, но не dir2.

Я мог бы сделать это «обычным» способом:

List<String> list = Arrays.asList(new File("/home/ftp").list());
Set<String> set = new HashSet<>();      
List<String> dirToProcess = new ArrayList<>();

for (String name : list){
    name = name.contains(".ok") ? name.substring(0, name.indexOf(".ok")) : name;
    if (!set.add(name)){
        dirToProcess.add(name);
    }       
}
// now dirToProcess contains the directory names which should be processed

Но я действительно хочу сделать это, используя функциональную Java (я использую Java8), используя Streams.Как я могу рефакторинг этого кода для достижения этого?

Ответы [ 5 ]

0 голосов
/ 11 декабря 2018

Вы можете разделить это на два шага.

@Test
public void should_return_dirs_with_corresponding_ok_file() {
    List<String> list = Arrays.asList("dir1.ok", "dir1", "dir2", "dir3.ok");

    //find all files ending with .ok and extract prefix
    List<String> okEndingFilesPrefix = list.stream()
        .filter(s -> s.endsWith(".ok"))
        //save those dirs prefix
        .map(s -> s.replace(".ok", ""))
        .collect(Collectors.toList());

    //filter existing dirs if they have corresponding file ending with ok, from previous step.
    List<String> dirsWithCorrespondingOkEndingFile = list.stream()
        .filter(s -> okEndingFilesPrefix.contains(s))
        .collect(Collectors.toList());

    assertEquals(dirsWithCorrespondingOkEndingFile, Lists.newArrayList("dir1"));
}

Если вы хотите сделать это как oneliner, вы можете использовать функции группировки.На мой взгляд, это не очень хорошо выглядит в Java.

@Test
public void should_return_dirs_with_corresponding_ok_file_with_grouping() {
    List<String> list = Arrays.asList("dir1.ok", "dir1", "dir2", "dir3.ok");


    List<String> dirsWithCorrespondingOkEndingFile = list.stream()
        //create "touple" with dir name/file name without .ok suffix as key.
        .map(s -> Pair.of(s.replaceAll(".ok", ""), s))
        // group by key
        .collect(
            Collectors.groupingBy(
                Pair::getKey, Collectors.toSet()
            ))
        //once again to stream
        .entrySet()
        .stream()
        // filter elements that has dir and file with .ok suffix
        .filter(entry -> entry.getValue().size() == 2)
        .map(Map.Entry::getKey)
        .collect(Collectors.toList());

    assertEquals(dirsWithCorrespondingOkEndingFile, Lists.newArrayList("dir1"));
}
0 голосов
/ 11 декабря 2018

Другой способ - использование регулярного выражения.

Pattern hasOk = Pattern.compile("\\b.ok");
List<String> list = Arrays.asList("dir1.ok (this is just an empty file)","dir1.o","dir3");
List<String> result = list.stream()
            .filter(hasOk.asPredicate()) //or  .map(name -> hasOk.matcher(name).find()?name.substring(0,name.indexOf(".ok")):name)
            .collect(Collectors.toList());
0 голосов
/ 11 декабря 2018

Если вы хотите преобразовать свой текущий подход с использованием потоков, вы можете сделать:

List<String> result = 
          Arrays.stream(new File("/home/ftp").list())
                .map(name -> name.endsWith(".ok") ? name.substring(0, name.indexOf(".ok")) : name)
                //.distinct()
                .collect(toList());

С другой стороны, вероятно, есть лучший способ сделать это, используя java.nio.file APIНесколько примеров здесь и здесь и т. д.

0 голосов
/ 11 декабря 2018

Я думаю, что вы ожидаете получить:

List<String> dirToProcess = list.stream()
                .filter(name -> name.contains(".ok") && list.contains(name.substring(0, name.indexOf(".ok"))))
                .map(name -> name.substring(0, name.indexOf(".ok")))
                .collect(Collectors.toList());
0 голосов
/ 11 декабря 2018

Ты усложняешь себе.Если вы знаете, что файл будет содержать .ok, просто отфильтруйте его по этому, а затем соберите его в новый список и затем удалите расширение при попытке получить файл без .ok

List<String> filteredList = list.stream()
        .filter(s -> s.endsWith(".ok"))
        .collect(Collectors.toList());

    filteredList.forEach(s -> {
        String name = s.substring(0, s.indexOf(".ok"));
        //You can handle it here
    });
...