I'm trying to group some records. I have this entity:
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@EqualsAndHashCode
@ToString
public class Candidate {
private Integer personId;
private String codeHswCandId;
private List<String> codeHswCandIdRelated;
private LocalDateTime updateDate;
}
And a list of them:
LocalDate today = LocalDate.of(2023,2,16);
LocalTime now = LocalTime.now();
LocalDate yesterday = LocalDate.of(2023,2,15);
LocalDate beforeYesterday = LocalDate.of(2023,2,14);
List<Candidate> candidateList = List.of(
Candidate.builder().personId(1).codeHswCandId("1").updateDate(LocalDateTime.of(today, now)).build(),
Candidate.builder().personId(1).codeHswCandId("2").updateDate(LocalDateTime.of(yesterday, now)).build(),
Candidate.builder().personId(1).codeHswCandId("3").updateDate(LocalDateTime.of(beforeYesterday, now)).build(),
Candidate.builder().personId(2).codeHswCandId("4").updateDate(LocalDateTime.of(today, now)).build(),
Candidate.builder().personId(2).codeHswCandId("5").updateDate(LocalDateTime.of(yesterday, now)).build(),
Candidate.builder().personId(2).codeHswCandId("6").updateDate(LocalDateTime.of(beforeYesterday, now)).build()
);
I need to group them into this structure:
List<Candidate> output = List.of(
Candidate.builder().personId(1).codeHswCandId("1").codeHswCandIdRelated(List.of("2","3")).build(),
Candidate.builder().personId(2).codeHswCandId("4").codeHswCandIdRelated(List.of("5","6")).build()
);
Group by the most recent personId
with closest updateDate
. I let his codeHswCandId
and the other on codeHswCandIdRelated
list.
Assuming you are using Java 12 or higher,
personId
as key and mapping to the object having
the max updateDate
,personId
and mapping to a list of
codeHswCandId
Candidate
objects using personId
from the key of the entry, codeHswCandId
from the value of the entry and a filterd list of codeHswCandIdRelated
strings from the value of the second map having the same key as the entry in the first map.code snippet:
List<Candidate> result =
candidateList.stream()
.collect(Collectors.teeing(
Collectors.toMap(Candidate::getPersonId,
Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(Candidate::getUpdateDate))),
Collectors.groupingBy(Candidate::getPersonId,
Collectors.mapping(Candidate::getCodeHswCandId,Collectors.toList())),
(map1, map2) -> map1.entrySet()
.stream()
.map(entry -> Candidate.builder()
.personId(entry.getKey())
.codeHswCandId(entry.getValue().getCodeHswCandId())
.codeHswCandIdRelated(map2.get(entry.getKey())
.stream()
.filter(cd -> !cd.equals(entry.getValue().getCodeHswCandId()))
.collect(Collectors.toList()))
.build())
.collect(Collectors.toList())));
Update
For Java 11 you can still use a simillar approach, except you need to stream over the input twice to build the maps:
Map<Integer, Candidate> personIdToCandidates =
candidateList.stream()
.collect(Collectors.toMap(Candidate::getPersonId,
Function.identity(),
BinaryOperator.maxBy(
Comparator.comparing(Candidate::getUpdateDate))));
Map<Integer, List<String>> personIdToCodeHsw =
candidateList.stream()
.collect(Collectors.groupingBy(Candidate::getPersonId,
Collectors.mapping(Candidate::getCodeHswCandId,
Collectors.toList())));
List<Candidate> result =
personIdToCandidates.entrySet()
.stream()
.map(entry -> Candidate.builder()
.personId(entry.getKey())
.codeHswCandId(entry.getValue().getCodeHswCandId())
.codeHswCandIdRelated(personIdToCodeHsw.get(entry.getKey())
.stream()
.filter(cd -> !cd.equals(entry.getValue().getCodeHswCandId()))
.collect(Collectors.toList()))
.build())
.collect(Collectors.toList());