Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java streams: group and sort by a previous mapping result?

I have to following code:

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class TimeZonesExample
{
    public static void main(String[] args)
    {
        Set<String> allZones = ZoneId.getAvailableZoneIds();
        LocalDateTime localDateTime = LocalDateTime.now();

        List<String> zoneList = new ArrayList<>(allZones);

        for (String s : zoneList)
        {
            ZoneId zoneId = ZoneId.of(s);
            ZonedDateTime zoneDateTime = localDateTime.atZone(zoneId);
            ZoneOffset zoneOffset = zoneDateTime.getOffset();
            String out = String.format("%35s %10s%n", zoneId, zoneOffset);

            System.out.printf(out);
        }
    }
}

This produces such a list:

                  Asia/Aden     +03:00
             America/Cuiaba     -03:00
                  Etc/GMT+9     -09:00
                  Etc/GMT+8     -08:00
             Africa/Nairobi     +03:00
            America/Marigot     -04:00
                 Asia/Aqtau     +05:00
          Pacific/Kwajalein     +12:00
        America/El_Salvador     -06:00
             Asia/Pontianak     +07:00
               Africa/Cairo     +02:00
          Pacific/Pago_Pago     -11:00
             Africa/Mbabane     +02:00
               Asia/Kuching     +08:00
                               ....

Instead of the for loop and to get more familiar with stream API, I tried to rewrite this to a stream result whereby the result should be sorted by the offset. The result should look like this:

-09:00 Etc/GMT+9   
-08:00 Etc/GMT+8    
-04:00 America/Marigot     
-03:00 America/Cuiaba
+03:00 Africa/Nairobi     
+03:00 Asia/Aden 

I was able to print the offsets (unsorted) with the expression below, but how to apply them on the zoneIds? I somehow have to remember the previous zoneId mapping result.

allZones.stream()
    .map(s -> ZoneId.of(s))
    .map(zId -> localDateTime.atZone(zId).getOffset())
    .forEach(System.out::println);
like image 274
Bevor Avatar asked Jun 17 '26 20:06

Bevor


2 Answers

You can grab the ZoneId back from ZonedDateTime.getZone(). Try this out:

ZoneId.getAvailableZoneIds()
        .stream()
        .map(ZoneId::of)
        .map(ZonedDateTime::now)
        .sorted(Comparator.comparing(ZonedDateTime::getOffset).reversed())
        .forEach(zdt -> System.out.println(zdt.getOffset() + "\t" + zdt.getZone()));
like image 91
shmosel Avatar answered Jun 20 '26 10:06

shmosel


You can't reach back into the previous step of the stream to get the element before the current step.

To sort the stream you must provide a way to sort the strings using a Comparator.

Converting your code to apply sorted(), this produces the output you want:

allZones.stream()
    .sorted(Comparator.<String, ZoneOffset>comparing(s -> localDateTime.atZone(ZoneId.of(s)).getOffset()).reversed())
    .map(s -> String.format("%10s %s", localDateTime.atZone(ZoneId.of(s)).getOffset(), ZoneId.of(s)))
    .forEach(System.out::println);
like image 30
Bohemian Avatar answered Jun 20 '26 08:06

Bohemian