2015-09-14 10 views
8

私は数日間この問題に苦労しています。 Javaストリームを使用してPivot機能を作成しようとしています。私は SUM、COUNT、MAX、MIN、およびAVERAGEを実装する必要があります。入力には、ピボット列インデックス、ピボット行インデックスの配列、および計算する値が与えられています。Streamsを使用したJava Pivotテーブルの実装

キャッチはデータがリスト内にあること< List < Object >>、ここで、ObjectはString、Integer、Doubleのいずれかです。私は実行時まで知りません。そして結果をリストとして返す必要があります<リスト<オブジェクト>>

Iが(私はその平均値がMAXとMINと同様であろうと仮定しています)MAX/MINに問題が午前

複数のテーブル値に旋回するために、私は私の第二groupingBy

を使用するクラスを作成

これはコンパイルされません。私は何を比較するのか、オブジェクトをintに変換するのか、さらには必要なのかわかりません。 1つのストリームですべてこれを実行したいと思いますが、可能であるかどうかはわかりません。私は何が間違っているのですか、それとも違うのですか?前もって感謝します。すべての可能な値(StringIntegerDouble)として

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

これは*巨大な質問です。 * Minimal *についての質問を読むことができます。 http://stackoverflow.com/help/mcveを参照してください---私は遠くには行かなかったが、オブジェクトの 'List'が' String'、 'Integer'、または' Double'であり、実行時まではわかりませんが、その後完全に型定義された 'Row'クラスを表示することになります。あなたはそれを知っていますか? – Andreas

+0

ストリームになるための特別な理由は何ですか? – Andreas

+0

いいえストリームに関係しない他のソリューションには完全にオープンしています。 –

答えて

3

あなたはComparableインターフェイスに未チェックのキャストを行うことができる、Comparableであることが知られています。また、オプションの開梱を忘れないでください。

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}} 

あなたが見ることができるように、あなたは両方を扱うことができる私が正しく理解すれば、あなたの列が非整数値を有することができるよう最後に、結果がMap<Object, Map<Object, Object>> myList、ないMap<Object, Map<Object, Integer>> myList、する必要がありますIntegerおよびDoubleカラム。 Stringでも扱うことができます(辞書編集では最大値が選択されます)。

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); 
} 

出力:

平均化のために、あなたの列の値は、数字(Numberクラス、どちらかIntegerまたはDouble)があると仮定してもよいし、Doubleに集める(整数の平均は、非整数を指定できます) :

> 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)になります。あなたは基本的にSQLグループ・バイ文のJava実装欲しい

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つのクラスが必要です。列1、列2、...

第二のクラスは、実際に実装する二つのクラスであるAggregatorある:最初は、グループによって列の組み合わせ等号/ハッシュコードとしてequals()hashCode()を実装する必要がありGroupByクラス、あります共通のインターフェース、集約するための1つのクラスInteger、集約するための別のクラスDouble。アグリゲータには値(Object)が与えられ、合計/最小/最大/カウント値が累算されます。

第3のクラスがメインクラスで、あなたがPivotクラスと呼ばれています。所望のグループ化カラム(タイプ付き)および所望のアグリゲーションカラム(タイプ付き)について、好ましくはbuilder patternを使用することについて言及するべきである。その後、データを渡すことができ、そのデータをHashMap<GroupBy, Aggregator>で収集し、その結果を戻り値に必要な形式に変換します。ピボットクラスを呼び出す方法の

例:

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缶の実装列ごとおよび集約された列の任意の数を処理し、それを使用することは非常に直感的です。

関連する問題