Marci-man
Marci-man

Reputation: 2233

JAVA8: Find the file with the newest date in its filename

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

Answers (1)

Holger
Holger

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

Related Questions