kaviya .P
kaviya .P

Reputation: 479

Group and get the latest month data from the map

I have a map as follows:

rupFeedDC.forEach(rl -> rl.getTransactionDate()
                          .toLocalDateTime()
                          .truncatedTo(ChronoUnit.DAYS));

Map<Timestamp, List<STUP>> timestampListMap =
    rupFeedDC.stream()
    .collect(Collectors.groupingBy(STUP::getTransactionDate));

Now, I want to get date which contains the latest month.

For example: for the map:

map.put("23/04/2017", someList);
map.put("21/04/2017", someList);
map.put("03/03/2017", someList);
map.put("04/02/2017", someList);
map.put("09/01/2017", someList);

I want to have only the key "23/04/2017", because it consists of the latest month. Can anyone please suggest me what can I do for it?

Upvotes: 1

Views: 1184

Answers (5)

Anonymous
Anonymous

Reputation: 86324

    Optional<Entry<LocalDate, List<STUP>>> latest = rupFeedDC.stream()
            .collect(Collectors.groupingBy(rl -> rl.getTransactionDate()
                    .toLocalDateTime()
                    .toLocalDate()))
            .entrySet()
            .stream()
            .max(Comparator.comparing(Map.Entry<LocalDate, List<STUP>>::getKey));

This will give you just the map entry for the latest date (or an empty Optional in case the list was empty). Use for example like this:

    latest.ifPresent(e -> {
        LocalDate latestDate = e.getKey();
        List<STUP> latestList = e.getValue();
        // ...
    });

If it turns out that grouping all objects (the ones from earlier dates too) has too big of a memory footprint, here’s a suggestion for how to avoid that:

    Optional<LocalDate> latestDate = rupFeedDC.stream()
            .map(STUP::getTransactionDate)
            .max(Comparator.naturalOrder())
            .map(ts -> ts.toLocalDateTime().toLocalDate());
    latestDate.ifPresent(d -> {
        List<STUP> latestList = rupFeedDC.stream()
                .filter(rl -> rl.getTransactionDate().toLocalDateTime()
                                    .toLocalDate()
                                    .equals(d))
                .collect(Collectors.toList());
        // ...
    });

Better still if you could use LocalDateTime in your STUP class instead of Timestamp. The Timestamp is long outdated, and this change would save a conversion for each object in your original list.

Upvotes: 1

Holger
Holger

Reputation: 298389

There is not much sense in performing a grouping operation when you are interested in just one group. Since these groups are determined by a property, you are just looking for all elements having the same value for that property.

So first, determine the value of that property, i.e. the maximum timestamp:

Timestamp latest = rupFeedDC.stream()
    .map(STUP::getTransactionDate)
    .max(Comparator.naturalOrder())
    .orElseThrow(() -> new IllegalStateException("no entries"));

Then, collect all elements having that property value

List<STUP> items = rupFeedDC.stream()
    .filter(item -> item.getTransactionDate().equals(latest))
    .collect(Collectors.toList());

If you still think you need a Map containing that one group, you can use

Map<Timestamp, List<STUP>> timestampListMap = Collections.singletonMap(latest, items);

Your question’s code suggests that you want to process the dates at day granularity (though it’s not working), which you can achieve with

LocalDateTime latest = rupFeedDC.stream()
    .map(item -> item.getTransactionDate().toLocalDateTime().truncatedTo(ChronoUnit.DAYS))
    .max(Comparator.naturalOrder())
    .orElseThrow(() -> new IllegalStateException("no entries"));
List<STUP> items = rupFeedDC.stream()
    .filter(item -> item.getTransactionDate().toLocalDateTime()
                        .truncatedTo(ChronoUnit.DAYS).equals(latest))
    .collect(Collectors.toList());
Map<LocalDateTime, List<STUP>> timestampListMap = Collections.singletonMap(latest, items);

As a side note, it makes no sense to say that you want “the latest month” when both, your code and your explanation by example indicate that you actually want the latest day.

Upvotes: 2

lexicore
lexicore

Reputation: 43689

You can group and search for max element in a single collect operation. Something along the lines:

    Comparator<STUP> comparingTransactionDate =
            Comparator.comparing(STUP::getTransactionDate);

    List<STUP> result = rupFeedDC.stream().collect(() -> new ArrayList<STUP>(), (list,  newItem) -> {
        if (!list.isEmpty()) {
            STUP existingItem = list.get(0);
            int comparingExistingItemToNewItem = 
                    comparingTransactionDate.compare(existingItem, newItem);
            if (comparingExistingItemToNewItem < 0) {
                list.clear();
                list.add(newItem);
            } else if (comparingExistingItemToNewItem == 0){
                list.add(newItem);
            }
        }
        else {
            list.add(newItem);
        }
    }, (list1, list2) -> {
        if (list1.isEmpty()) {
            list1.addAll(list2);
        }
        else if (!list2.isEmpty()) {
            STUP left = list1.get(0);
            STUP right = list2.get(0);
            int leftComparedToRight = comparingTransactionDate.compare(left, right);
            if (leftComparedToRight == 0) {
                list1.addAll(list2);
            } else if (leftComparedToRight < 0) {
                list1.clear();
                list1.addAll(list2);
            }
        }
    });

This approach saves you the construction of the complete Map<Timestamp, List<STUP>> which isn't really needed to find the latest element. It might be helpful if you have many STUP elements and want to save the memory.

Upvotes: 0

Sandesh Gupta
Sandesh Gupta

Reputation: 1195

You can iterate over the keys of the map and find the latest.

Set<String> keys = map.keySet();
        Date latest = null;

        for (String s : keys) {
            if(latest==null){
                latest=new SimpleDateFormat("dd/MM/yyyy").parse(s);
            }else{
                //check if current is latest that 'latest'
                Date current = new SimpleDateFormat("dd/MM/yyyy").parse(s);
                if(current.after(latest)){
                    latest = current;
                }
            }
        }

Upvotes: 0

Eran
Eran

Reputation: 393936

Since your key is a java.sql.Timestamp, you can take advantage of the fact that it implements Comparable. Modify your stream pipeline to generate a TreeMap (in which the keys are sorted in ascending order), and the last key will be the latest timestamp:

TreeMap<Timestamp, List<STUP>> timestampListMap =
    rupFeedDC.stream()
             .collect(Collectors.groupingBy(STUP::getTransactionDate,
                                            TreeMap::new,
                                            Collectors.toList()));
Timestamp last = timestampListMap.lastKey();

Upvotes: 3

Related Questions