MohantaD
MohantaD

Reputation: 43

Retrieve the first items of each group in Java

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

Answers (4)

KevinBui
KevinBui

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

epox
epox

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

MohantaD
MohantaD

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

TemaTre
TemaTre

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

Related Questions