Реализация сводной таблицы в Java - PullRequest
1 голос
/ 14 апреля 2019

Мне нужно реализовать сводную таблицу в Java, и я знаю, как работать с функциями Java 8 Streams. В Интернете есть много хороших решений, но мне нужно что-то большее, и я не понимаю, как это сделать: мне нужно создать более динамичную таблицу, в которой в идеале вы не знаете, для каких столбцов нужно агрегировать. Например, если у меня есть столбцы («Нация», «Компания», «Промышленность», «Количество сотрудников»), я должен указать в качестве ввода:

  • Пользовательская функция агрегирования (например, сумма) для меры
  • Переменный порядок агрегирования: например, я хочу сначала агрегировать для 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 в подсписке по значению, указанному в имени заголовка, которое я извлекаю из своих данных (или больше, это зависит от того, по какому столбцу я должен сгруппировать)
  • Агрегировать каждый подсписок
  • Объединение подсписка

Может ли кто-нибудь помочь или поддержать меня? Спасибо

Ответы [ 2 ]

0 голосов
/ 17 апреля 2019

Я вижу два способа сделать его универсальным. Первый - использовать отражение, чтобы обнаружить метод для вызова из строкового представления поля. Второй - создать универсальный метод get, который принимает аргумент String и возвращает значение соответствующего поля. Второй безопаснее, поэтому я сосредоточусь на этом. Я начну с ответа @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]}
0 голосов
/ 14 апреля 2019
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);
    }
}

Вы можете сделать его универсальным, передав функциональный интерфейс.Это только для справки.

...