1

I have a List of objects List<FloatBalance> which can be represented by the following JSON. I want to get the most recent entry for each currency in the List and pass the pruned list back to the request.

    {
        "id": 1,
        "clientId": 50,
        "currency": "GBP",
        "floatType": "ChargeFloat",
        "floatCurrentValue": 300.00,
        "chargeReference": "PROD-1578486576_278",
        "cashOutReference": null,
        "cashInReference": null,
        "targetValue": 3000.00,
        "alertValue": 500.00,
        "maxValue": 4500.00,
        "createdDate": "2020-01-08T12:29:50Z"
    },
...
]

My first attempt is straight foward, I group them by currency with

Map<String, List<FloatBalance>> result = floats.stream()
                    .collect(groupingBy(FloatBalance::getCurrency));

and then I group them and order them by most recent date with

Map<String, List<FloatBalance>> floatGrouped = floats.stream()
                    .sorted(Comparator.comparing(FloatBalance::getCreatedDate).reversed())
                    .collect(groupingBy(FloatBalance::getCurrency));

Map Result

enter image description here

I can also reduce the list but I have only been able to carry out this operation on the complete list which returns only the most recent entry.

FloatBalance floatGrouped = floats.stream()
                    .sorted(Comparator.comparing(FloatBalance::getCreatedDate))
                    .reduce((first, second) -> second)
                    .orElse(null);

I have gone round in circles trying to figure this out, I've tried to .entrySet() to carry out reduce on each entry in the map but computer says no to everything I've tried.

Ideally I want to retrun the most recent entry for each currency as the system response.

2 Answers 2

3
List<FloatBalance> floatBalances = new ArrayList<>(floats.stream()
        .collect(Collectors.toMap(
                FloatBalance::getCurrency,
                Function.identity(),
                BinaryOperator.maxBy(Comparator.comparing(FloatBalance:: getCreatedDate))))
        .values());

First, this collects to a Map, where Key is currency and value is floatBalanace. When two currencies collide, there is a merger that will separate them - or tell which to pick. This is what BinaryOperator::maxBy. does. It basically says : "I will take one of the two floatBalancers where createdDate is the biggest". And, at the end, you simply take values - which will return a Collection<FloatBalance>.

Sign up to request clarification or add additional context in comments.

4 Comments

@naman why did you edit to incorrect getCreatedAt ? getCreatedDate is correct, I guess
What does Function.identity() do in this example? Everything else I can understand, but I'm not sure what purpose this serves.
@Eoin chooses a FloatBalance itself as the value and then for the next value that appears with the same key, gets compared within the maxBy
@EugeneKortov that was just a typo, the edit was meant to improve the code readability and completeness primarily.
1

Are you looking for :

List<FloatBalance> result = floats.stream()
        .collect(Collectors.groupingBy(FloatBalance::getCurrency,
                Collectors.maxBy(Comparator.comparing(FloatBalance::getCreatedDate))))
        .values().stream()
        .filter(Optional::isPresent)
        .map(Optional::get)
        .collect(Collectors.toList());

7 Comments

I have a lot of reading to do, I tried everything you have in your working answer but obviously I had the actions in the wrong order. Thank you.
You can filter values that are not present. Having said that, it's one thing I feel is overhead while using reduction, unless a significant identity element is defined.
@Naman you mean .values().stream().filter(Optional::isPresent).map(Optional::get)
@chrisloughnane this answer, while correct, does some operations that can be elided.
@Eugene Indeed. I have also moved slowly into thinking in the direction of replacing groupingBy and reducing by toMap. Not saying always, but deciding before implementing.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.