4

I'm having a problem continuing. I have a list and in each position contains a String (within the phrase there is at the end a date)

Example:

I am new here 20/8/2019 

I want to sort the list this way: In position zero I want the phrase containing the oldest date and the following positions the date will be more recent.

I tried to use SimpleDateFormat and Date, but I didn't know how to do it.

String variable, variable2, c;
int d = 0;
for(int i = 0; i < lista.size(); i++) {
    for(int j = 1; j <lista.size(); j++) {
        variable = lista.get(i);
        variable2 = lista.get(j);
        c = compareDates(variable, variable2);
        lista.add(d,c);
        d++;
    }
}

private static Date compareDates(String variable, String variable2) throws ParseException {
    SimpleDateFormat formateador = new SimpleDateFormat("dd/MM/yyyy");
    String var = formateador.format(variable);
    String var2 = formateador.format(variable2);
    if (var.before(var2)) {
        return var;
    } else {
        if (var2.before(var1)) {

        } else {

        }
        return null;
    }
}

Exception in thread "main" java.lang.Error: Unresolved compilation problem: Type mismatch: cannot convert from Date to String

at Ejercicio.ClaseMain.leerDes(ClaseMain.java:124)

Line 124: c = compareDates(variable, variable2);

Visual example: Each position in the list has a phrase with a date:

enter image description here

The thing is, I read a .txt file, where there are several lines. Contents of the file:

Sevilla reserves himself to Apoel and wins without brilliance; sport Julen Lopetegui revolutionized the eleven with the aim of giving rest to the regulars, which did not prevent his team from adding his second triumph of the competition sportyou 10/10/2019

A painting by Banksy of the British Parliament occupied by chimpanzees, sold >for 11 million euros culture An oil of artist Banksy representing the British House of Commons full of chimpanzees was topped on Thursday at an auction in London for 9.8 million pounds (11 million euros) 10/2019

I use a while to read the file line and save each line at each position in the list, and I want to sort the list. Old date ---> recent date.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
RM.
  • 59
  • 1
  • 7
  • 1
    please use java.time API, it can be more helpful – Youcef LAIDANI Nov 09 '19 at 12:34
  • Share `lista` instanciation please, the problem seems to come from here – azro Nov 09 '19 at 12:38
  • Is it absolutely sure that the all the string phrases in the list will have dates at the end only and in the same format `dd/mm/yyyy`?? – ambianBeing Nov 09 '19 at 12:48
  • @ambianBeing Yes, I've updated the post with the content that the list would have. – RM. Nov 09 '19 at 12:51
  • @RM. Have posted an answer to get the list of sorted dates from the phrases list, see if it helps. – ambianBeing Nov 09 '19 at 15:24
  • 1
    The `Date` and `SimpleDateFormat` classes are terrible. Avoid them. They were supplanted years ago by the *java.time* classes. – Basil Bourque Nov 09 '19 at 16:21
  • I too recommend you don’t use `SimpleDateFormat` and `Date`. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. Instead use `LocalDate` and `DateTimeFormatter`, both from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Nov 10 '19 at 11:00
  • How many strings are you sorting, just approximately? 50 or 5000? Asking because with 5000 strings you don’t want to parse them for each comparison. – Ole V.V. Nov 10 '19 at 11:07
  • Are you allowed to use `Collections.sort()`, `List.sort()` and/or `Stream.sorted()`? Using a built-in sorting method is strongly recommended. – Ole V.V. Nov 10 '19 at 11:32

6 Answers6

3

Considering all the strings in List are of same format and having date at fourth index after split, like below

List<String> list = new ArrayList<>();
list.add("I am new here 20/11/2019 ");
list.add("I am Deadpool here 20/7/2019 ");
list.add("I am IronMan here 20/6/2019 ");

Now use comparator to sort the List based on LocalDate

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/M/yyyy");
list.sort(Comparator.comparing(str->LocalDate.parse(str.split(" ")[4],formatter)));

    System.out.println(list);  //I am IronMan here 20/6/2019 , I am Deadpool here 20/7/2019 , I am new here 20/11/2019 ]
Ryuzaki L
  • 37,302
  • 12
  • 68
  • 98
2

Please don't use the legacy Date library, instead use java.time API, so if you are using Java 8 your solution can be :

String[] strs = {"20/10/2019", "5/2/2019", "12/12/2019",
        "1/8/2019", "25/12/2019", "2/1/2019", "6/9/2019"};
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/uuuu");
List<LocalDate> collect = Stream.of(strs)
        .map(s -> LocalDate.parse(s, formatter))  // convert your strings to dates
        .sorted() // sort the dates
        .collect(Collectors.toList()); // collect the result in a collection

Output

[2019-01-02, 2019-02-05, 2019-08-01, 2019-09-06, 2019-10-20, 2019-12-12, 2019-12-25]
Youcef LAIDANI
  • 55,661
  • 15
  • 90
  • 140
  • but, these are phrases that I don't know about, because I read it from a file, so I don't know what dates they are. I put that image to help you understand me. – RM. Nov 09 '19 at 12:55
2

The dates like "20/8/2019" are not matching the pattern "dd/MM/yyyy". The correct format should be like "20/08/2019". And the shortest solution for sorting is

list.sort(Comparator.comparing(
    source -> LocalDate.parse(source, DateTimeFormatter.ofPattern("dd/MM/yyyy"))));
Powerswitch
  • 21
  • 1
  • 2
2

My solution would be:

    List<String> lista = List.of(
            "Sevilla reserves himself to Apoel … sportyou 10/10/2019",
            "I am new here 20/8/2019",
            "A painting by Banksy … 19/10/2019");
    List<String> sortedList = lista.stream()
            .map(s -> new Object() {
                String theString = s;
                LocalDate date = extractDate(s);
            })
            .sorted(Comparator.comparing(obj -> obj.date))
            .map(obj -> obj.theString)
            .collect(Collectors.toList());
    sortedList.forEach(System.out::println);

Output from this is:

I am new here 20/8/2019
Sevilla reserves himself to Apoel … sportyou 10/10/2019
A painting by Banksy … 19/10/2019

The extractDate method that I am using is:

private static Pattern datePattern = Pattern.compile("\\d{1,2}/\\d{1,2}/\\d{4}$");
private static DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("d/M/u");

private static LocalDate extractDate(String fullString) {
    Matcher m = datePattern.matcher(fullString);
    if (m.find()) {
        String dateString = m.group();
        return LocalDate.parse(dateString, dateFormatter);
    } else {
        throw new IllegalArgumentException("String doesn’t end with a date: " + fullString);
    }
}

For efficient sorting of the strings — it only matters if there are many — I am extracting the trailing date and parsing it only once for each string (not for every comparison). I am parsing into LocalDate and use these for sorting. In order to get the original strings out after sorting I have put both the String and the LocalDate into an object and then sort these objects. It may surprise some that I can use an anonymous subclass of Object in this way, but it works nicely.

I recommend you don’t use SimpleDateFormat and Date. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. Instead I am using LocalDate and DateTimeFormatter, both from java.time, the modern Java date and time API.

Java has nice sorting facilities built-in. If writing your own sorting algorithm was for an exercise, that’s a good exercise. Frankly you still had a way to go before your sorting would work. You may want to read up on sorting algorithms, there’s a lot written, also on the WWW. For production code you should rely on a library method.

Link: Oracle tutorial: Date Time explaining how to use java.time.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
1

The error is happening because at compareDates the method return type is Date while the returns are String.

Now coming to the solution, if the intent is to just get the sorted dates extracted from the phrases this should work. But looking at the code in OP I sense you're trying to bubble sort the list of phrases sorted by dates which can also be achieved following the same lines.

The important part is extraction of the date via Regex.

Code:

List<LocalDate> ld = new ArrayList<LocalDate>();
for(int i = 0; i < lista.size(); i++){
  ld.add(getDateFromString(lista.get(i)));
}
//sorting the list of dates extracted
ld = ld.stream().sorted().collect(Collectors.toCollection(ArrayList::new));

private static LocalDate getDateFromString(String str){
  LocalDate d;
  //looks for pattern dd/mm/yyyy in the passed string
  Matcher m = Pattern.compile("(\\d{1,2}/\\d{1,2}/\\d{4})").matcher(str);
  if(m.find()) {
   String match = m.group(1);
   d = LocalDate.parse(match, DateTimeFormatter.ofPattern("d/MM/yyyy"));
  }
  return d;
}

Note: This takes the assumption that every phrase will have one date string in the form of dd/mm/yyyy

ambianBeing
  • 3,449
  • 2
  • 14
  • 25
  • 1
    A very good answer. It demonstrates how to extract the date from a longer string no matter how that string looks, and it is using java.time, the modern Java date and time API (which is what we should all want to do). – Ole V.V. Nov 10 '19 at 20:47
1

Simply, if you don't know the format of the date strings, its not possible to convert the strings to dates. Is "10/11/12" the 11 of October or the 10th of November of year '12 or is it the 12 of November of year '10? See How to convert String to Date without knowing the format?.

In your text example, the last date is simply "10/2019" and you used "20/8/2019" as another example so it seems you have a mix of possible formats. If you could limit the possibilities, it might be possible to find the best match.

If you can extract that date using regex as a sequence of numbers and forward slashes at the end of the text (see answer from ambianBeing), then you could try to parse this string using the possible formats from most strict to most relaxed, trapping the 'DateTimeParseException' exceptions and stopping at the first successful parse. If nothing succeeds, flag it so you can determine what to fix - either the text, add a new format or a better regex expression.

Using the examples above, you could start with format patterns

  • dd/MM/yyyy
  • dd/M/yyyy
  • MM/yyyy

If everything fails, use a null date to flag the entry.

If you put this in a method that returns the date, you can then use the stream solution to sort the list as suggested by several others.

Michael McKay
  • 650
  • 4
  • 11