【问题标题】:Implementing pivot table in Java在 Java 中实现数据透视表
【发布时间】:2019-09-04 07:57:51
【问题描述】:

我需要用 Java 实现一个数据透视表,并且我知道如何使用 Java 8 Streams 功能。网上有很多很好的解决方案,但我需要更多的东西,但我不明白该怎么做:我需要创建一个更动态的表,理想情况下你不知道你必须聚合哪些列。 例如,如果我有列(“国家”、“公司”、“行业”、“员工人数”),我必须输入:

  • 度量的自定义聚合函数(例如 sum)
  • 可变的聚合顺序:例如,我想要 Nation 的第一个聚合,我将其作为参数“Nation”或 Nation 和 Company,并将其作为参数,例如“Nation->Company”。 换句话说,我不知道哪些是我的聚合字段,基本上我需要一种方法来实现一个通用的 GROUP BY SQL 子句,比如:
// Given an the Arraylist ("Nation", "Company", "Industry","Number of employes") called data with some rows

Map<String, List<Object[]>> map = data.stream().collect(
                Collectors.groupingBy(row -> row[0].toString() + "-" + row[1].toString()));

for (Map.Entry<String, List<Object[]>> entry : map.entrySet()) {
            final double average = entry.getValue().stream()
                    .mapToInt(row -> (int) row[3]).average().getAsDouble();

这不是我需要的,因为它太明确了。

我需要:

  • 按我从数据中提取的标题名称给出的值将输入 Arraylist 拆分到子列表中(或更多,这取决于我必须分组的列数)
  • 聚合每个子列表
  • 合并子列表

有人可以帮助或鼓励我吗?谢谢

【问题讨论】:

  • 是否只有两列来执行分组?
  • 不,我可以对任何列执行分组。问题是我不确定,所以 group by 必须是通用的
  • 提供您正在使用的数据结构、示例输入和所需输出(以代码而非文字形式),以及该输入的给定代码的输出。
  • 例如Map&lt;String, List&lt;Object[]&gt;&gt; output = pivot(data, "sum", "Nation-&gt;Company"),但输入和输出可以是任何东西(在我的示例中,输入data 是List&lt;Object[])。抛开自定义聚合函数,我不明白如何自定义最后一个参数,因为它可以由用户选择,所以我必须创建一个结构来动态选择分组依据的字段并将其放入 Collectors.groupingBy 功能中。
  • 非常不清楚。 Edit 我要求的信息的问题。逐步解释您的起点、中间状态和最终需要的输出。您提供了一些晦涩的 pivot 方法,但没有说明参数是什么以及从哪里读取它们,请在编辑中解释所有内容。

标签: java collections group-by pivot java-stream


【解决方案1】:
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

class Input {
    private String nation, company, industry;
    private int employees;

    public Input(String nation, String company, String industry, int employees) {
        super();
        this.nation = nation;
        this.company = company;
        this.industry = industry;
        this.employees = employees;
    }

    public String getNation() {
        return nation;
    }

    public void setNation(String nation) {
        this.nation = nation;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public String getIndustry() {
        return industry;
    }

    public void setIndustry(String industry) {
        this.industry = industry;
    }

    public int getEmployees() {
        return employees;
    }

    public void setEmployees(int employees) {
        this.employees = employees;
    }

    @Override
    public String toString() {

        return String.format(
                "Nation : %s, Company : %s, Industry : %s, Employees : %s",
                nation, company, industry, employees);
    }
}

public class CustomGroupBy {

    // Generic GroupBy
    static Map<String, List<Input>> groupBy(List<Input> input,
            Function<Input, String> classifier) {
        return input.stream().collect(Collectors.groupingBy(classifier));
    }

    public static void main(String[] args) {

        List<Input> input = Arrays.asList(new Input("India", "A", "IT", 12),
                new Input("USA", "B", "ELECTRICAL", 90), new Input("India",
                        "B", "MECHANICAL", 122), new Input("India", "B", "IT",
                        12), new Input("India", "C", "IT", 200));

        // You need to pass this in parameter
        Function<Input, String> groupByFun = i -> i.getNation() + "-"
                + i.getCompany();

        // Example-1
        Map<String, List<Input>> groupBy = groupBy(input, Input::getCompany);

        // Example-2
        Map<String, List<Input>> groupBy2 = groupBy(input, groupByFun);

        System.out.println(groupBy2);

        List<Double> averages = groupBy
                .entrySet()
                .stream()
                .map(entry -> entry.getValue().stream()
                        .mapToInt(row -> row.getEmployees()).average()
                        .getAsDouble()).collect(Collectors.toList());
        System.out.println(averages);
    }
}

您可以通过传递功能接口使其通用。仅供您参考。

【讨论】:

  • 实际上它应该比这更通用:我需要作为我的函数的参数给出分组字段。问题是我必须实现一些通用的 group by 语句,因为你可以给函数任何列分组,所以在代码中我们不能放 groupByFun = i -&gt; i.getNation() + "-"+ i.getCompany(); 因为我们是特定于那个聚合的,因为它是动态的(一次我想聚合一个字段,另一个聚合两个字段),所以我必须实现一些更抽象的东西
【解决方案2】:

我看到了两种使其通用的方法。第一个是使用反射从字段的字符串表示中发现要调用的方法。第二个是创建一个通用的 get 方法,该方法接受一个字符串作为参数并返回正确字段的值。第二个更安全,所以我将专注于那个。我将从已经完成大部分工作的@Anant Goswami 的答案开始。

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

class Scratch {
    // Input class from Anant Goswami in previous reply
    static class Input {
        private String nation, company, industry;
        private int employees;

        public Input(String nation, String company, String industry, int employees) {
            super();
            this.nation = nation;
            this.company = company;
            this.industry = industry;
            this.employees = employees;
        }

        public String getNation() {
            return nation;
        }

        public void setNation(String nation) {
            this.nation = nation;
        }

        public String getCompany() {
            return company;
        }

        public void setCompany(String company) {
            this.company = company;
        }

        public String getIndustry() {
            return industry;
        }

        public void setIndustry(String industry) {
            this.industry = industry;
        }

        public int getEmployees() {
            return employees;
        }

        public void setEmployees(int employees) {
            this.employees = employees;
        }

        @Override
        public String toString() {

            return String.format(
                    "Nation : %s, Company : %s, Industry : %s, Employees : %s",
                    nation, company, industry, employees);
        }

        public Object get(String field){
            switch (field.toLowerCase()){
                case "nation": return getNation();
                case "company": return getCompany();
                case "industry": return getIndustry();
                case "employees": return getEmployees();
                default: throw new UnsupportedOperationException();
            }
        }
    }

    private static Map<String, List<Input>> group(List<Input> inputs, String... fields){
        Function<Input, String> groupBy = i -> Arrays.stream(fields).map(f -> i.get(f).toString()).collect(Collectors.joining("-"));
        Map<String, List<Input>> result = inputs.stream().collect(Collectors.groupingBy(groupBy));
        System.out.println(result);
        return result;
    }

    public static void main(String[] args) {
        List<Input> input = Arrays.asList(new Input("India", "A", "IT", 12),
                new Input("USA", "B", "ELECTRICAL", 90), new Input("India",
                        "B", "MECHANICAL", 122), new Input("India", "B", "IT",
                        12), new Input("India", "C", "IT", 200));
        group(input, "company");
        group(input, "nation", "Company");
    }
}

作为输出给出

{A=[Nation : India, Company : A, Industry : IT, Employees : 12], B=[Nation : USA, Company : B, Industry : ELECTRICAL, Employees : 90, Nation : India, Company : B, Industry : MECHANICAL, Employees : 122, Nation : India, Company : B, Industry : IT, Employees : 12], C=[Nation : India, Company : C, Industry : IT, Employees : 200]}
{India-B=[Nation : India, Company : B, Industry : MECHANICAL, Employees : 122, Nation : India, Company : B, Industry : IT, Employees : 12], India-C=[Nation : India, Company : C, Industry : IT, Employees : 200], India-A=[Nation : India, Company : A, Industry : IT, Employees : 12], USA-B=[Nation : USA, Company : B, Industry : ELECTRICAL, Employees : 90]}

【讨论】:

    猜你喜欢
    • 2022-07-28
    • 2021-09-28
    • 1970-01-01
    • 2017-01-10
    • 1970-01-01
    • 2013-07-14
    • 2021-01-16
    • 2018-04-10
    • 2010-11-30
    相关资源
    最近更新 更多