Reputation: 1118
I have a directory called /home/ftp
which contains sub-directories like this:
dir1
dir2
dir3
dir1.ok (this is just an empty file)
dir3.ok (this is just an empty file)
The code should only process the files under directories which have a corresponding ".ok"
file. So for eg. in this example, the code should pick those files for processing which are found under dir1
and dir3
but not dir2
.
I could do this the "normal" way:
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
But I really want to do this using functional Java (I'm using Java8) using Streams. How can i refactor this code to achieve it?
Upvotes: 2
Views: 617
Reputation: 2246
You can split this in two steps.
@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"));
}
If you want to do this as oneliner, you can use grouping functions. In my opinion this does not look good in 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"));
}
Upvotes: 0
Reputation: 31878
I think what you're expecting as an output is:
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());
Upvotes: 2
Reputation: 17289
The other way is using of pattern regex.
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());
Upvotes: 2
Reputation: 56433
if you want to convert your current approach using streams, you could do:
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());
On another note, there's probably a better way to do this using java.nio.file
API, some few examples here and here etc..
Upvotes: 0
Reputation: 3154
You are making this harder on your self. If you know the file is gonna contain .ok
just filter it by that and then collect it to a new list and then remove the extension when trying to get the file without .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
});
Upvotes: 1