0

I have a java List of CodeObj. CodeObj has 2 attributes : codeName and version. My java List looks like this when mapped to a JSON object :

[
    { "codeName": "test1", "version": "1" },

    { "codeName": "test1", "version": "2" },

    { "codeName": "test1", "version": "3" },

    { "codeName": "test2", "version": "1" },

    { "codeName": "test2", "version": "2" },

    { "codeName": "test2", "version": "3" },

    { "codeName": "test2", "version": "4" },
]

I would like to filter this List by each codeName most recent version in java, so it would give this result when mapped to a JSON object :

[
    { "codeName": "test1", "version": "3" },

    { "codeName": "test2", "version": "4" },
]

To make it, I have to use the Stream java class and its methods like filter, map, max, etc.

I started with this bit of code :

codeObjs.stream()
.max(Comparator.comparing(CodeObj::getVersion))

I feel like I have to isolate each set of CodeObj that has the same codeName, but I don't know how to do it while turning the List into a Stream.

shmosel
  • 49,289
  • 6
  • 73
  • 138
Clem
  • 3
  • 3

2 Answers2

3
return codeObjs.stream()
            .collect(Collectors.groupingBy(
                CodeObj::getCodeName,
                Collectors.maxBy(Comparator.comparing(CodeObj::getVersion))
            ))
            .values().stream()
            .flatMap(Optional::stream)
            .collect(Collectors.toList());

So, basically, you are isolating each instance of CodeObj with a unique name by using Collectors.groupingBy. Then using Collectors.maxBy on each group, you get the highest version. Then, by streaming the resulting map, you access collections of Optional<CodeObj> instances, which represent the highest versions per group. flatMap(Optional::stream) transforms these Optionals into a sequence of CodeObj. Finally, you collect them into a list through Collectors.toList()

Chaosfire
  • 4,818
  • 4
  • 8
  • 23
  • 1
    Slightly simpler when using `toMap` instead of `groupingBy`, e.g. `.collect(Collectors.collectingAndThen(Collectors.toMap(CodeObj::getCodeName, Function.identity(), BinaryOperator.maxBy(Comparator.comparing( CodeObj::getVersion))), map -> new ArrayList<>(map.values())));` – Holger Aug 25 '23 at 17:10
1

Given the following class:

class CodeObj {
    private String codeName;
    private String version;

    public CodeObj(String codeName, String version) {
        this.codeName = codeName;
        this.version = version;
    }

    public String getCodeName() {
        return codeName;
    }

    public String getVersion() {
        return version;
    }

    @Override
    public String toString() {
        return "CodeObj = [%s, %s]".formatted(codeName, version);
    }

}

With the initial list of objects.

List<CodeObj> codeObjs = List.of(new CodeObj("test1", "1"),
        new CodeObj("test1", "2"), new CodeObj("test1", "3"),
        new CodeObj("test2", "1"), new CodeObj("test2", "2"),
        new CodeObj("test2", "3"), new CodeObj("test2", "4"));

Here is a max filter which can either be declared separately as shown or put in-line with the stream. It compares each version and returns the object with the latest.

BinaryOperator<CodeObj> latestVersion = (co1, co2) -> co1.getVersion()
        .compareTo(co2.getVersion()) > 0 ? co1 : co2;

And here is the solution. The CodeObj objects are reduced to the required max versions and the resulting Collection is remapped to an ArrayList.

List<CodeObj> results = codeObjs.stream()
   .collect(Collectors.collectingAndThen(
           Collectors.groupingBy(CodeObj::getCodeName,
                Collectors.reducing(new CodeObj("", ""), latestVersion)),
                mp->new ArrayList<>(mp.values())));


results.forEach(System.out::println);

prints

CodeObj = [test2, 4]
CodeObj = [test1, 3]
WJS
  • 36,363
  • 4
  • 24
  • 39
  • 1
    Note that the `toMap` collector already has the logic of `groupingBy` and `reducing`, see also [this comment](https://stackoverflow.com/questions/76967784/stream-max-on-a-set-of-elements-from-a-stream#comment135704380_76967864) or [this Q&A](https://stackoverflow.com/q/57041896/2711488) – Holger Aug 25 '23 at 17:13
  • @Holger Had never noticed that `BinaryOperator` had those static methods. Thanks for pointing it out. – WJS Aug 26 '23 at 13:55