2015-09-14 5 views
8

Я уже несколько дней борюсь с этой проблемой. Я пытаюсь создать функциональность Pivot, используя Java-потоки. Мне нужно реализовать только SUM, COUNT, MAX, MIN и AVERAGE. Для ввода мне присваивается индекс сводной столбцы, массив индексов сводных строк и значение для вычисления.Реализация таблицы Java Pivot с использованием потоков

Улов данных есть в списке < Список < Объект >>, где Объект с любой строкой, целым или двойным. но я не буду знать до времени исполнения. И я должен вернуть свои результаты как Список < Список < Объект >>.

У меня возникли проблемы с MAX/MIN (я предполагаю, что средняя будет похож на MAX и MIN)

Для того, чтобы поворачиваться на несколько табличных значений, я создал класс, используя мой мой второй groupingBy

Это не скомпилируется, я не уверен, что сравнивать с тем, где нужно преобразовать объект в int или мне даже нужно. Я хотел бы сделать все это одним потоком, но я не уверен, что это возможно. Что я делаю неправильно, или я могу сделать это по-другому. Заранее спасибо.

package pivot.test; 

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.Collections; 
import java.util.Comparator; 
import java.util.List; 
import java.util.Map; 
import java.util.Optional; 
import java.util.stream.Collectors; 

public class PivotTest { 

    List<List<Object>> rows = new ArrayList<List<Object>>(); 

    public PivotTest() throws Exception { 

     rows.add(Arrays.asList(new Object[]{ "East", "Boy", "Tee", 10, 12.00})); 
     rows.add(Arrays.asList(new Object[]{ "East", "Boy", "Golf", 15, 20.00})); 
     rows.add(Arrays.asList(new Object[]{ "East", "Girl", "Tee", 8, 14.00})); 
     rows.add(Arrays.asList(new Object[]{ "East", "Girl", "Golf", 20, 24.00})); 
     rows.add(Arrays.asList(new Object[]{ "West", "Boy", "Tee", 5, 12.00})); 
     rows.add(Arrays.asList(new Object[]{ "West", "Boy", "Golf", 12, 20.00})); 
     rows.add(Arrays.asList(new Object[]{ "West", "Girl", "Tee", 15, 14.00})); 
     rows.add(Arrays.asList(new Object[]{ "West", "Girl", "Golf", 10, 24.00})); 

    } 

    // Dynamic Max based upon Column, Value to sum, and an array of pivot rows 
    public void MaxTable(int colIdx, int valueIdx, int... rowIdx) { 

     Map<Object, Map<Object, Integer>> myList = newRows.stream().collect(
     Collectors.groupingBy(r -> ((List<Object>) r).get(colIdx), 
     Collectors.groupingBy(r -> new PivotColumns(r, rowIdx), 
     Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(???)), 
       r -> ((List<Object>) r).get(valueIdx))))); 

     System.out.println("Dynamic MAX PIVOT"); System.out.println(myList); 

    } 

    public static void main(String[] args) { 

     try { 
      PivotTest p = new PivotTest(); 
      System.out.println("\n\nStreams PIVOT with index values inside a List\n"); 
      p.MaxTable(0, 3, new int[] { 2 }); 
     } catch (Exception e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 

} 

class PivotColumns { 

    ArrayList<Object> columns; 

    public PivotColumns(
     List<Object> objs, int... pRows) { 
     columns = new ArrayList<Object>(); 

     for (int i = 0; i < pRows.length; i++) { 
      columns.add(objs.get(pRows[i])); 
     } 

    } 

    public void addObject(Object obj) { 
     columns.add(obj); 
    } 

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

    @Override 
    public boolean equals(Object obj) { 
     if (this == obj) 
      return true; 
     if (obj == null) 
      return false; 
     if (getClass() != obj.getClass()) 
      return false; 
     PivotColumns other = (PivotColumns) obj; 
     if (columns == null) { 
      if (other.columns != null) 
       return false; 
     } else if (!columns.equals(other.columns)) 
      return false; 
     return true; 
    } 

    public String toString() { 
     String s = ""; 
     for (Object obj : columns) { 
      s += obj + ","; 
     } 

     return s.substring(0, s.lastIndexOf(',')); 
    } 

} 
+0

Это * огромный * вопрос. Возможно, вы захотите прочитать о вопросах * Минимальный *. См. Http://stackoverflow.com/help/mcve --- Я не зашел далеко, но вы сказали, что у вас есть «Список» объектов, либо «String», «Integer», либо «Double», и это вы не узнаете до времени выполнения, но затем продолжаете показывать четко определенный, полностью типизированный класс «Row». Итак, что это, вы знаете или нет? – Andreas

+0

Любая конкретная причина, по которой он * имеет * потоки? – Andreas

+0

Нет. Я полностью открыт для других решений, не связанных потоками. –

ответ

3

Поскольку все возможные значения (String, Integer, Double), как известно, Comparable, вы можете выполнить непроверенной бросок к интерфейсу Comparable. Также не забудьте распаковать опцию. Наконец, если я правильно понимаю, то результат должен быть Map<Object, Map<Object, Object>> myList, не Map<Object, Map<Object, Integer>> myList, так как ваш столбец может иметь не целые значения:

public void MaxTable(int colIdx, int valueIdx, int... rowIdx) { 
    Map<Object, Map<Object, Object>> myList = newRows.stream().collect(
    Collectors.groupingBy(r -> r.get(colIdx), 
    Collectors.groupingBy(r -> new PivotColumns(r, rowIdx), 
    Collectors.collectingAndThen(Collectors.maxBy(
     Comparator.comparing(r -> (Comparable<Object>)(((List<Object>) r).get(valueIdx)))), 
     r -> r.get().get(valueIdx))))); 

    System.out.println("Dynamic MAX PIVOT"); System.out.println(myList); 
} 

Результаты:

> p.MaxTable(0, 3, new int[] { 1 }); 
{West={Girl=15, Boy=12}, East={Girl=20, Boy=15}} 

> p.MaxTable(0, 4, new int[] { 1 }); 
{West={Girl=24.0, Boy=20.0}, East={Girl=24.0, Boy=20.0}} 

Как вы можете видеть, вы можете обрабатывать как Integer и Double колонка. Можно обрабатывать даже String (будет выбрано лексикографическое максимальное значение).

Для усреднения можно предположить, что ваши значения столбцов чисел (Number класс, либо Integer или Double) и собрать в Double (в среднем целых чисел может быть не целым, а):

public void AverageTable(int colIdx, int valueIdx, int... rowIdx) { 
    Map<Object, Map<Object, Double>> myList = newRows.stream().collect(
      Collectors.groupingBy(r -> r.get(colIdx), Collectors 
        .groupingBy(r -> new PivotColumns(r, rowIdx), 
          Collectors.averagingDouble(r -> ((Number) (r 
            .get(valueIdx))).doubleValue())))); 

    System.out.println("Dynamic AVG PIVOT"); System.out.println(myList); 
} 

Выход :

> p.AverageTable(0, 3, new int[] { 1 }); 
{West={Girl=12.5, Boy=8.5}, East={Girl=14.0, Boy=12.5}} 

> p.AverageTable(0, 4, new int[] { 1 }); 
{West={Girl=19.0, Boy=16.0}, East={Girl=19.0, Boy=16.0}} 
0

с входными будучи List строк, каждая строка будучи List столбцов, и столбец будучи String , Integer или Double, и не зная, сколько и сколько столбцов нужно группировать, и не зная, какой и какой тип столбца заполнить, я бы предложил реализовать собственный агрегатор.

Предположительно, все строки имеют одинаковое количество столбцов, и все значения определенного столбца всегда будут одного и того же типа (или null).

То, что вы хотите, это в основном реализация Java из SQL группы-заявлением:

SELECT Column1, Column2, ... 
    , SUM(Column5), MIN(Column5), MAX(Column5), COUNT(Column5) 
    , SUM(Column6), MIN(Column6), MAX(Column6), COUNT(Column6) 
    , ... 
    FROM List<List<Object>> 
GROUP BY Column1, Column2, ... 

Вам нужно 3-х классов.Первым из них является GroupBy класс, который должен реализовать equals() и hashCode() как объединенные равными/хэш-код группы-по столбцам: COLUMN1, COLUMN2 ...

второй класс является Aggregator, что на самом деле два класса, реализующие общий интерфейс, один класс для агрегирования Integer и другой для агрегирования Double. Агрегатору будет присвоено значение (Object) и будет накапливать значения суммы/min/max/count.

Третий класс - это основной класс, который вы называете классом Pivot. Следует сказать о желаемых столбцах группировки (с типом) и желаемых столбцах агрегации (с типом), предпочтительно используя builder pattern. Затем ему могут быть предоставлены данные и будут собирать эти данные в HashMap<GroupBy, Aggregator>, а затем преобразовать этот результат обратно в формат, необходимый для возвращаемого значения.

Пример того, как вызвать класс Pivot:

List<List<Object>> input = /*constructed elsewhere*/; 

List<List<Object>> output = new Pivot() 
    .addGroupByString(0) // Column1 
    .addGroupByString(1) // Column2 
    .addGroupByInteger(2) // Column3 a group by column can be be a number 
    .addIntegerAggregation(4) // Column5 
    .addDoubleAggregation(5) // Column6 
    .process(input); 

Или, если вы не всегда хотят все агрегирование, это может быть:

 .addIntegerSum(4) // SUM(Column5) 
    .addDoubleMin(5) // MIN(Column6) 
    .addDoubleMax(5) // MAX(Column6) 

с этим, реализация Pivot может обрабатывать любое количество групп по столбцам и агрегированным столбцам, а использование его очень интуитивно.

Смежные вопросы