Reputation: 2233
I have few files in an FTP folder with names like:
mainimport_31052017.csv
mainimport_21052017.csv
mainimport_15052017.csv
And I have a pattern string:
String pattern = "mainimport_ddmmyyy";
Now I am supposed to download the file with the latest date in its title. I am supposed to do it with Java 8 goodies.
I have a solution, but this is not pretty enough, I am doing it in 2 statements:
1) I first get the newest date:
Date newestDate = Collections.max(ftpFiles.stream().filter(fileName -> StringUtils.startsWith(fileName.getName(), prefix)).map(fileName -> {
String fileNameSuffix = fileName.getName().split("_")[1];
Date date = null;
try {
date = dateFormat.parse(fileNameSuffix);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}).collect(Collectors.toList()));
2) I then get the filename with the latest date:
Optional<FTPFile> file = ftpFiles.stream().filter(fileName->{
String fileNameSuffix = fileName.getName().split("_")[1];
Date date = null;
try {
date = dateFormat.parse(fileNameSuffix);
} catch (ParseException e) {
e.printStackTrace();
}
return StringUtils.startsWith(fileName.getName(), prefix) && date.equals(newestDate);
}).findFirst();
I am trying to do this both in a single statement, if it is possible.
Upvotes: 0
Views: 1457
Reputation: 298203
Assuming that the dates always have the specified six-character representation, you may use
Optional<FTPFile> max = ftpFiles.stream()
.filter(file -> file.getName().startsWith(prefix))
.max(Comparator.comparing(file -> file.getName()
.replaceFirst(".*_([0-9]{2})([0-9]{2})([0-9]{4}).*", "$3$2$1")));
The factory method Comparator.comparing
allows you to create a Comparator
based on a property, so that the maximum element will be the element with the maximum value for that property.
Note that this simply converts the ddmmyyyy
formatted date to a yyyymmdd
string which can be compared lexicographically, which works as long as the day and months always have a two-digit form, i.e. with a leading zero.
You may optimize this a bit by preparing and reusing the regex pattern:
Pattern pattern = Pattern.compile(".*_([0-9]{2})([0-9]{2})([0-9]{4}).*");
Optional<FTPFile> max = ftpFiles.stream()
.filter(file -> file.getName().startsWith(prefix))
.max(Comparator.comparing(file ->
pattern.matcher(file.getName()).replaceFirst("$3$2$1")));
If the DateFormat
is an unavoidable prerequisite, you may use
Optional<FTPFile> max = ftpFiles.stream()
.filter(file -> file.getName().startsWith(prefix))
.max(Comparator.comparing(file -> {
String name = file.getName();
name = name.substring(name.indexOf('_')+1);
try {
return dateFormat.parse(name);
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
}));
This performs the operation in one go, but has the disadvantage of performing the parsing operation more than necessary during the comparisons. If you want to avoid that, you may resort to the original two pass design, but you still don’t need to collect into a List
:
ftpFiles.stream()
.map(FTPFile::getName)
.filter(name -> name.startsWith(prefix))
.map(name -> {
name = name.substring(name.indexOf('_')+1);
try {
return dateFormat.parse(name);
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
})
.max(Comparator.naturalOrder())
.map(date -> prefix+'_'+dateFormat.format(date))
.flatMap(fileName -> ftpFiles.stream()
.filter(file -> file.getName().equals(fileName)).findAny())
.ifPresent(System.out::println);
Upvotes: 4