Reputation: 43
In Java, how to get list down the latest/first item of each group after grouping by field(/s)? e.g From the given list of medical records, how to retrieve the latest record of each patient.
Sample Data --------------------------------------------------------------------------- PatientName | ReportDate | ReadingNo | Measurement --------------------------------------------------------------------------- X | 2020-01-02 | 1 | 255 Y | 2020-01-02 | 1 | 250 X | 2020-01-02 | 2 | 266 Y | 2020-01-02 | 2 | 270 X | 2020-01-02 | 3 | 298 Y | 2020-01-02 | 3 | 259 X | 2020-01-02 | 4 | 280 Y | 2020-01-02 | 4 | 285 X | 2020-01-03 | 1 | 260 Y | 2020-01-03 | 1 | 265 X | 2020-01-03 | 2 | 280 Y | 2020-01-03 | 2 | 260 X | 2020-01-03 | 3 | 285 Y | 2020-01-03 | 3 | 290 X | 2020-01-03 | 4 | 290 Y | 2020-01-03 | 4 | 280 --------------------------------------------------------------------------- Expected result --------------------------------------------------------------------------- PatientName | ReportDate | ReadingNo | Measurement --------------------------------------------------------------------------- X | 2020-01-03 | 4 | 290 Y | 2020-01-03 | 4 | 280 ---------------------------------------------------------------------------
I can do it C# as below. Any one can help to translate it in Java?
using System.Collections.Generic; using System.Linq; public class Program { public static void Main() { Console.WriteLine("---------------------------------------------------------------------------"); Console.WriteLine(String.Format("{0} |\t{1}\t| {2} |\t{3}", "PatientName", "ReportDate", "ReadingNo", "Measurement")); Console.WriteLine("---------------------------------------------------------------------------"); List lst = new List { new Patient(){Name="X", ReportDate="2020-01-02", ReadingNo=1, Measurement=255 }, new Patient(){Name="Y", ReportDate="2020-01-02", ReadingNo=1, Measurement=250 }, new Patient(){Name="X", ReportDate="2020-01-02", ReadingNo=2, Measurement=266 }, new Patient(){Name="Y", ReportDate="2020-01-02", ReadingNo=2, Measurement=270 }, new Patient(){Name="X", ReportDate="2020-01-02", ReadingNo=3, Measurement=298 }, new Patient(){Name="Y", ReportDate="2020-01-02", ReadingNo=3, Measurement=259 }, new Patient(){Name="X", ReportDate="2020-01-02", ReadingNo=4, Measurement=280 }, new Patient(){Name="Y", ReportDate="2020-01-02", ReadingNo=4, Measurement=285 }, new Patient(){Name="X", ReportDate="2020-01-03", ReadingNo=1, Measurement=260 }, new Patient(){Name="Y", ReportDate="2020-01-03", ReadingNo=1, Measurement=265 }, new Patient(){Name="X", ReportDate="2020-01-03", ReadingNo=2, Measurement=280 }, new Patient(){Name="Y", ReportDate="2020-01-03", ReadingNo=2, Measurement=260 }, new Patient(){Name="X", ReportDate="2020-01-03", ReadingNo=3, Measurement=285 }, new Patient(){Name="Y", ReportDate="2020-01-03", ReadingNo=3, Measurement=290 }, new Patient(){Name="X", ReportDate="2020-01-03", ReadingNo=4, Measurement=290 }, new Patient(){Name="Y", ReportDate="2020-01-03", ReadingNo=4, Measurement=280 } }; lst.ForEach(p=>{ Console.WriteLine(p.toString()); }); Console.WriteLine("---------------------------------------------------------------------------"); var lstLatest = from p in lst group p by p.Name into g select g.OrderByDescending(p => p.ReportDate).ThenByDescending(p=>p.ReadingNo).ToList(); foreach(var p in lstLatest) { Console.WriteLine(p.First().toString()); } } } public class Patient { public string Name { get; set;} public string ReportDate {get;set;} public int ReadingNo {get;set;} public int Measurement {get;set;} public string toString() { return String.Format("{0}\t\t\t|\t{1}\t|\t{2} \t|\t{3}", this.Name, this.ReportDate, this.ReadingNo, this.Measurement); } }
Upvotes: 1
Views: 1211
Reputation: 1139
Use Collectors.reducing to get one element each group
var dummyPatient = new Patient();
dummyPatient.setReportDate("2000-01-01");
dummyPatient.setReadingNo(-999);
Map<String, Patient> map = lst.stream().collect(
Collectors.groupingBy(
Patient::getName,
Collectors.reducing(
dummyPatient,
BinaryOperator.maxBy(
Comparator.comparing(Patient::getReportDate).thenComparing(Patient::getReadingNo)
)
)
)
);
Upvotes: 1
Reputation: 10978
import static java.util.Comparator.comparing;
import static java.util.Comparator.reverseOrder;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.groupingBy;
Map<String, List<Patient>> map = lst.stream()
.collect(groupingBy(Patient::getName, toList()));
for (List<Patient> group : map.values()) {
Patient first = group.stream()
.sorted(comparing(Patient::getReportDate).thenComparing(Patient::getReadingNo, reverseOrder()))
.findFirst()
.orElseThrow();
System.out.println(first);
}
Upvotes: 0
Reputation: 43
Thanks @TemaTre. Your input helped. The working code looks like
Map map = lst.stream().collect( Collectors.groupingBy( Patient::getName, Collectors.collectingAndThen( Collectors.toList(), values -> values.stream() .sorted(Comparator.comparing(Patient::getReportDate, Comparator.nullsFirst(Comparator.naturalOrder())) .thenComparing(Patient::getReadingNo, Comparator.nullsFirst(Comparator.naturalOrder())).reversed()) .collect(toList()).get(0) )));
And the complete working code in Java is as below
import java.util.*;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.*;
public class Main{
public static void main(String []args){
List<Patient> lst = new ArrayList<>();
lst.add(new Patient(){{setName("X");setReportDate("2020-01-02");setReadingNo(1);setMeasurement(255);}});
lst.add(new Patient(){{setName("Y");setReportDate("2020-01-02");setReadingNo(1);setMeasurement(250);}});
lst.add(new Patient(){{setName("X");setReportDate("2020-01-02");setReadingNo(2);setMeasurement(266);}});
lst.add(new Patient(){{setName("Y");setReportDate("2020-01-02");setReadingNo(2);setMeasurement(270);}});
lst.add(new Patient(){{setName("X");setReportDate("2020-01-02");setReadingNo(3);setMeasurement(298);}});
lst.add(new Patient(){{setName("Y");setReportDate("2020-01-02");setReadingNo(3);setMeasurement(259);}});
lst.add(new Patient(){{setName("X");setReportDate("2020-01-02");setReadingNo(4);setMeasurement(280);}});
lst.add(new Patient(){{setName("Y");setReportDate("2020-01-02");setReadingNo(4);setMeasurement(285);}});
lst.add(new Patient(){{setName("X");setReportDate("2020-01-03");setReadingNo(1);setMeasurement(260);}});
lst.add(new Patient(){{setName("Y");setReportDate("2020-01-03");setReadingNo(1);setMeasurement(265);}});
lst.add(new Patient(){{setName("X");setReportDate("2020-01-03");setReadingNo(2);setMeasurement(280);}});
lst.add(new Patient(){{setName("Y");setReportDate("2020-01-03");setReadingNo(2);setMeasurement(260);}});
lst.add(new Patient(){{setName("X");setReportDate("2020-01-03");setReadingNo(3);setMeasurement(285);}});
lst.add(new Patient(){{setName("Y");setReportDate("2020-01-03");setReadingNo(3);setMeasurement(290);}});
lst.add(new Patient(){{setName("X");setReportDate("2020-01-03");setReadingNo(4);setMeasurement(290);}});
lst.add(new Patient(){{setName("Y");setReportDate("2020-01-03");setReadingNo(4);setMeasurement(280);}});
for(Patient p: lst){
System.out.println(p.toString());
}
System.out.println("---------------------------");
Map<String, Patient> map =
lst.stream().collect(
Collectors.groupingBy(
Patient::getName,
Collectors.collectingAndThen(
Collectors.toList(),
values -> values.stream()
.sorted(Comparator.comparing(Patient::getReportDate, Comparator.nullsFirst(Comparator.naturalOrder()))
.thenComparing(Patient::getReadingNo, Comparator.nullsFirst(Comparator.naturalOrder())).reversed())
.collect(toList()).get(0)
)));
for (Map.Entry<String,Patient> entry : map.entrySet()) {
System.out.println(entry.getValue().toString());
}
}
}
class Patient{
private String Name;
public String getName(){
return Name;
}
public void setName(String name){
this.Name=name;
}
private String ReportDate;
public String getReportDate(){
return ReportDate;
}
public void setReportDate(String reportDate){
this.ReportDate=reportDate;
}
private int ReadingNo;
public int getReadingNo(){
return ReadingNo;
}
public void setReadingNo(int readingNo){
this.ReadingNo=readingNo;
}
private int Measurement;
public int getMeasurement(){
return Measurement;
}
public void setMeasurement(int measurement){
this.Measurement=measurement;
}
public String toString(){
return String.format("%s\t%s\t%d\t%d", Name, ReportDate, ReadingNo, Measurement);
}
}
Upvotes: 2
Reputation: 1424
In java you should use streams api.
If you need group by you can use this example:
Map<String, Patient> map =
getValute().stream().collect(
Collectors.groupingBy(
Patient::getName,
Collectors.collectingAndThen(
Collectors.toList(),
values -> values.get(0))));
As example if you want to use FirstOrDefault (only for education):
Animal cheetah = animals.stream()
.filter((animal) -> animal.getNumber() == cheetahNumber)
.findFirst()
.orElse(Animal.DEFAULT);
Upvotes: 1