将Java列表转换为Pivot格式

时间:2018-12-08 12:13:43

标签: java data-structures java-8 java-stream

我有一个below类,想用Java将数据对象列表转换为数据透视表格式。

public class Data {

    private String consultedOn;
    private String consultedBy;

    // Getters

    // Setters


}


List<Data> reports = new ArrayList<Data>();

reports.add(new Data("04/12/2018","Mr.Bob"));
reports.add(new Data("04/12/2018","Mr.Jhon"));
reports.add(new Data("04/12/2018","Mr.Bob"));
reports.add(new Data("05/12/2018","Mr.Jhon"));
reports.add(new Data("06/12/2018","Mr.Bob"));
reports.add(new Data("06/12/2018","Mr.Jhon"));
reports.add(new Data("07/12/2018","Mr.Bob"));

我想使用集合中的java将上面的列表转换成下面的表格格式。

consultedOn       Mr.Bob          Mr.Jhon  
---------------------------------------
04/12/2018           2              1
05/12/2018           0              1
06/12/2018           1              1
07/12/2018           1              0

请注意,consultedOn字段不限于两个值,该字段可以包含任何数据,因此集合应该是动态的。

我尝试将Java8流与下面的代码一起使用。

class DataMap {


 private String consultedOn;
    private String consultedBy;

    public DataMap(String consultedOn) {
        super();
        this.consultedOn = consultedOn;
    }

    public DataMap(String consultedOn, String consultedBy) {
        super();
        this.consultedOn = consultedOn;
        this.consultedBy = consultedBy;
    }


    public String getConsultedOn() {
        return consultedOn;
    }


    public void setConsultedOn(String consultedOn) {
        this.consultedOn = consultedOn;
    }


    public String getConsultedBy() {
        return consultedBy;
    }


    public void setConsultedBy(String consultedBy) {
        this.consultedBy = consultedBy;
    }


    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((consultedOn == null) ? 0 : consultedOn.hashCode());
        return result;
    }


    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof DataMap ))
            return false;
        DataMap other = (DataMap )obj;
        if (consultedOn == null) {
            if (other.consultedOn != null)
                return false;
        } else if (!consultedOn.equals(other.consultedOn))
            return false;
        return true;
    }


}


Map<DataMap, List<DataReport>> map = reports.stream()
            .collect(Collectors.groupingBy(x -> new DataMap(x.getConsultedOn(), x.getConsultedBy())));

但是地图没有按照我的期望给出预期结果。

我不确定如何处理此类数据,我们将不胜感激。

4 个答案:

答案 0 :(得分:2)

使用我在评论中解释的技术,这是一个完整的答案,即设计一个类Row来代表您想要为每一行生成的内容,即consultedOn字符串,并进行大量咨询每个人。

public class Pivot {
    private static final class Data {

        private final String consultedOn;
        private final String consultedBy;

        public Data(String consultedOn, String consultedBy) {
            this.consultedOn = consultedOn;
            this.consultedBy = consultedBy;
        }

        public String getConsultedOn() {
            return consultedOn;
        }

        public String getConsultedBy() {
            return consultedBy;
        }
    }

    private static final class Row {
        private final String consultedOn;
        private final Map<String, Integer> consultationsByPerson = new HashMap<>();

        public Row(String consultedOn) {
            this.consultedOn = consultedOn;
        }

        public void addPerson(String person) {
            consultationsByPerson.merge(person, 1, Integer::sum);
        }

        public int getConsultationsFor(String person) {
            return consultationsByPerson.getOrDefault(person, 0);
        }

        public String getConsultedOn() {
            return consultedOn;
        }
    }

    private static class PivotReport {

        private final Map<String, Row> rowsByConsultedOn = new HashMap<>();
        private SortedSet<String> persons = new TreeSet<>();

        private PivotReport() {}

        private void addData(Data d) {
            rowsByConsultedOn.computeIfAbsent(d.getConsultedOn(), Row::new).addPerson(d.getConsultedBy());
            persons.add(d.consultedBy);
        }

        public static PivotReport create(List<Data> list) {
            PivotReport report = new PivotReport();
            list.forEach(report::addData);
            return report;
        }

        public String toString() {
            String headers = "Consulted on\t" + String.join("\t", persons);
            String rows =  rowsByConsultedOn.values()
                                            .stream()
                                            .sorted(Comparator.comparing(Row::getConsultedOn))
                                            .map(this::rowToString)
                                            .collect(Collectors.joining("\n"));
            return headers + "\n" + rows;
        }

        private String rowToString(Row row) {
            return row.getConsultedOn() + "\t" +
                persons.stream()
                       .map(person -> Integer.toString(row.getConsultationsFor(person)))
                       .collect(Collectors.joining("\t"));
        }
    }


    public static void main(String[] args) {
        List<Data> list = createListOfData();
        PivotReport report = PivotReport.create(list);
        System.out.println(report);
    }

    private static List<Data> createListOfData() {
        List<Data> reports = new ArrayList<Data>();

        reports.add(new Data("04/12/2018","Mr.Bob"));
        reports.add(new Data("04/12/2018","Mr.Jhon"));
        reports.add(new Data("04/12/2018","Mr.Bob"));
        reports.add(new Data("05/12/2018","Mr.Jhon"));
        reports.add(new Data("06/12/2018","Mr.Bob"));
        reports.add(new Data("06/12/2018","Mr.Jhon"));
        reports.add(new Data("07/12/2018","Mr.Bob"));
        reports.add(new Data("07/12/2018","Mr.Smith"));

        return reports;
    }
}

请注意,由于您在consultedOn字段中使用的是String而不是LocalDate,因此日期将按字典顺序进行排序,而不是按时间顺序进行排序。您应该使用适当的类型:LocalDate。

答案 1 :(得分:1)

您可能正在寻找使用Collectors.groupingByList<DataMap>consultedOn分组,并进一步按consultedBy属性对其分组,其计数为:

Map<String, Map<String, Long>> finalMapping = reports.stream()
        .collect(Collectors.groupingBy(DataMap::getConsultedOn,
            Collectors.groupingBy(DataMap::getConsultedBy,Collectors.counting())));

这将为您提供输出:

{05/12/2018={Mr.Jhon=1}, 06/12/2018={Mr.Jhon=1, Mr.Bob=1},
07/12/2018={Mr.Bob=1}, 04/12/2018={Mr.Jhon=1, Mr.Bob=2}}

此外,如果您需要考虑所有对应的consultedBy值,则可以从初始Set中创建一个List<DataMap>,如下所示:

Set<String> consultedBys = reports.stream()
        .map(DataMap::getConsultedBy)
        .collect(Collectors.toSet());

您可以使用以下方法通过以下方式修改获得的现有地图以使其包含0个计数:

finalMapping.forEach((k, v) -> consultedBys.forEach(c -> v.putIfAbsent(c, 0L)));

这现在将为您提供输出:

{05/12/2018={Mr.Jhon=1, Mr.Bob=0}, 06/12/2018={Mr.Jhon=1, Mr.Bob=1},
07/12/2018={Mr.Jhon=0, Mr.Bob=1}, 04/12/2018={Mr.Jhon=1, Mr.Bob=2}}

答案 2 :(得分:0)

另一个是这样的:

Map<Pair<String, String>, Integer> map = reports
      .stream()
     .collect(toMap(data -> new Pair<>(data.getConsultedOn(),
                    data.getConsultedBy()), data -> 1, Integer::sum));

Map<String, DataMap> result= new HashMap<>();

-

class DataMap {
   private String consultedOn;
   private Map<String, Integer> map;
}

-

Set<String> persons = new HashSet<>();
persons = reports.stream().map(Data::getConsultedBy).collect(Collectors.toSet());

-

for (Map.Entry<Pair<String, String>, Integer> entry : map.entrySet()) {
      Map<String, Integer> val = new HashMap<>();
      for (String person : persons) {
          if (!person.equals(entry.getKey().getValue()))
              val.put(person, 0);
           else
              val.put(entry.getKey().getValue(), entry.getValue());
       }
        result.put(entry.getKey().getKey(), new DataMap(entry.getKey().getKey(), val));
}

和最终结果:

List<DataMap> finalResult = new ArrayList<>(result.values());

答案 3 :(得分:0)

除了使用单独的数据结构外,您还可以将键映射用作被咨询日期(日期或字符串),并将值作为(字符串或您自己定义的POJO的列表,并使用equals()方法覆盖)。使用了Map<String, List<String>>

这样的地图

您需要的是两种方法: 一个设置报告(addDataToReport):为每个被咨询的医生(关键)创建一个被咨询医生的列表。查看有关map.merge用法的评论

并以报告方式显示数据(printReport)。我们使用“%10s”给出正确的格式。格式不会代替println隐式地添加换行符

此外,要获取报告的列,我们需要有一个集合(唯一值列表),doctors.add(consultedBy);将为我们服务。 Java将确保保持医生独特的价值。

public class Application {
    Set<String> doctors = new LinkedHashSet<>();

    private void addDataToReport(Map<String, List<String>> reportMap, String consultedOn, String consultedBy) {
        doctors.add(consultedBy); // set the doctors Set
        reportMap.merge(consultedOn, Arrays.asList(consultedBy)// if key = consultedOn is not there add , a new list
                , (v1, v2) -> Stream.concat(v1.stream(), v2.stream()).collect(Collectors.toList()));//else merge previous and new values , here concatenate two lists
    }

    private void printReport(Map<String, List<String>> reportMap) {
        /*Set Headers*/
        String formatting = "%10s";//give a block of 10 characters for each string to print
        System.out.format(formatting, "consultedOn");
        doctors.forEach(t -> System.out.format(formatting, t));// print data on console without an implicit new line
        System.out.println("\n---------------------------------------");
        /*Set row values*/
        for (Map.Entry<String, List<String>> entry : reportMap.entrySet()) {
            Map<String, Integer> map = new LinkedHashMap<>();
            doctors.forEach(t -> map.put(t, 0)); // initialise each doctor count on a day to 0
            entry.getValue().forEach(t -> map.put(t, map.get(t) + 1));
            System.out.format(formatting, entry.getKey());
            map.values().forEach(t -> System.out.format(formatting, t));
            System.out.println();
        }
    }

    public static void main(String[] args) {
        Application application = new Application();
        Map<String, List<String>> reportMap = new LinkedHashMap<>();
        String MR_JHON = "Mr.Jhon";
        String MR_BOB = "Mr.Bob ";
        application.addDataToReport(reportMap, "04/12/2018", MR_BOB);
        application.addDataToReport(reportMap, "04/12/2018", MR_JHON);
        application.addDataToReport(reportMap, "04/12/2018", MR_BOB);
        application.addDataToReport(reportMap, "05/12/2018", MR_JHON);
        application.addDataToReport(reportMap, "06/12/2018", MR_BOB);
        application.addDataToReport(reportMap, "06/12/2018", MR_JHON);
        application.addDataToReport(reportMap, "07/12/2018", MR_BOB);
        application.printReport(reportMap);
    }
}

结果

consultedOn   Mr.Bob    Mr.Jhon
---------------------------------------
04/12/2018         2         1
05/12/2018         0         1
06/12/2018         1         1
07/12/2018         1         0