10

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

問題は、データが List < List < Object>> にあることです。ここで、Object は String、Integer、または Double のいずれかです。しかし、実行時までわかりません。結果を List < List < Object>> として返す必要があります。

MAX/MIN に問題があります (AVERAGE は MAX と MIN に似ていると思います)

複数のテーブル値をピボットするために、2 番目の groupingBy を使用するクラスを作成しました

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

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(','));
    }

}
4

2 に答える 2

4

すべての可能な値 ( StringIntegerDouble) は であることがわかっているため、インターフェイス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クラス、またはのいずれIntegerDouble)であり、収集する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}}
于 2015-09-14T02:27:48.747 に答える
0

入力がList行であり、各行がList列であり、列がStringInteger、またはDoubleであり、グループ化する列と列の数がわからず、集計する列の種類と種類がわからない場合は、お勧めします独自のアグリゲーターを実装します。

おそらく、すべての行に同じ数の列があり、特定の列のすべての値は常に同じ型 (またはnull) になります。

基本的には、SQL group-by ステートメントの 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 つ目は、グループ化列の等号/ハッシュコードの組み合わせとして実装する必要があるクラスGroupByです: Column1、Column2、...equals()hashCode()

2 番目のクラスは ですAggregator。これは実際には、共通のインターフェイスを実装する 2 つのクラスです。1 つは集約用のクラスでInteger、もう1 つは集約用のクラスですDouble。アグリゲーターには値 ( ) が与えられObject、合計/最小/最大/カウントの値が累積されます。

3 番目のクラスは、クラスと呼ばれるメイン クラスPivotです。できればビルダーパターンを使用して、目的のグループ化列 (タイプ付き) と目的の集計列 (タイプ付き) について通知する必要があります。次に、データを与えることができ、そのデータを に収集し、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任意の数のグループ化列と集計列を処理でき、非常に直感的に使用できます。

于 2015-09-14T02:40:25.407 に答える