太郎くん、次郎くん、三郎くんはテストの点数を競争していました。
しかし、総合点ではなく各教科の点数でそれぞれ順位を決めたくなったため、
集計の方法を変更しました。
このとき、このデータをデータベースに登録していたとすると、
いままで主キーが「人」であったのが「人+教科」に変更されることになります。
つまり、3行の情報を1行にまとめるという作業が発生するわけです。
これはOracleでいうPivat関数にあたりそうです。
ただ、OracleのPivotの文法をみればわかりますが、
教科が増えるとSQLを書き直さなければいけないことに気付きます。
めんどうです。
というわけでJava側でできたりしないかなと。
/** * ピボット用ユーティリティ */ public class PivotUtil { /** * 旋回する */ public static <S, T, U, V extends Pivotable<S, T, U, V>> List<V> pivot(List<V> table) { // グループ化する Map<S, List<V>> groupMap = table.stream().collect(Collectors.groupingBy(v -> v.groupBy())); // 旋回する List<V> resList = new ArrayList<V>(); for (List<V> list : groupMap.values()) { // グループ代表的な1件にピボット対象をMap化して設定する V pivotable = list.get(0); Map<T, U> eleMap = pivotable.getMap(); for (V p : list) { eleMap.put(p.getFor(), p.getValue()); } // 1件だけを追加 resList.add(pivotable); } return resList; } /** * 旋回を解除する */ public static <S, T, U, V extends Pivotable<S, T, U, V>> List<V> unPivot(List<V> table) { List<V> resList = new ArrayList<V>(); for (V p : table) { // グループごとに旋回されている要素をすべて展開する for (Entry<T, U> entry : p.getMap().entrySet()) { // 行を複製 V pp = p.copy(); // 旋回されている要素を展開 pp.setFor(entry.getKey()); pp.setValue(entry.getValue()); // 全件を追加 resList.add(pp); } } return resList; } /** * ピボット可能インタフェース */ public static interface Pivotable<S, T, U, V extends Pivotable<S, T, U, V>> { /** * pivotしない部分 */ public S groupBy(); /** * 集約条件列を取得 */ public T getFor(); /** * 集約値を取得 */ public U getValue(); /** * 集約条件列を設定 */ public void setFor(T t); /** * 集約値を設定 */ public void setValue(U u); /** * pivotされた部分 */ public Map<T, U> getMap(); /** * 自要素をコピーする */ public V copy(); } }
↑こんなユーティリティを作って
public class TestBo implements Pivotable<String, String, Integer, TestBo> { public String userNm = null; public String kamokuNm = null; public Integer score = 0; public Map<String, Integer> scoreMap = new HashMap<String, Integer>(); public static TestBo of(String userNm, String kamokuNm, Integer score) { TestBo bo = new TestBo(); bo.userNm = userNm; bo.kamokuNm = kamokuNm; bo.score = score; return bo; } @Override public String groupBy() { return userNm; } @Override public String getFor() { return kamokuNm; } @Override public void setFor(String t) { kamokuNm = t; } @Override public Integer getValue() { return score; } @Override public void setValue(Integer u) { score = u; } @Override public Map<String, Integer> getMap() { return scoreMap; } @Override public TestBo copy() { return of(userNm, kamokuNm, score); } public String toString() { if (scoreMap.isEmpty()) { return userNm + ":" + kamokuNm + "=" + score; } else { return userNm + ":" + scoreMap.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(",")); } } }
↑こんなエンティティを
List<TestBo> list = new ArrayList<TestBo>(); list.add(TestBo.of("太郎", "国語", 82)); list.add(TestBo.of("太郎", "数学", 76)); list.add(TestBo.of("太郎", "英語", 88)); list.add(TestBo.of("次郎", "国語", 65)); list.add(TestBo.of("次郎", "数学", 98)); list.add(TestBo.of("次郎", "英語", 70)); list.add(TestBo.of("三郎", "国語", 55)); list.add(TestBo.of("三郎", "数学", 85)); list.add(TestBo.of("三郎", "英語", 100)); list.forEach(System.out::println); System.out.println(); list = PivotUtil.pivot(list); list.forEach(System.out::println);
↑こんな分に展開するわけです
太郎:国語=82 太郎:数学=76 太郎:英語=88 次郎:国語=65 次郎:数学=98 次郎:英語=70 三郎:国語=55 三郎:数学=85 三郎:英語=100 次郎:国語=65,数学=98,英語=70 三郎:国語=55,数学=85,英語=100 太郎:国語=82,数学=76,英語=88
↑と、結果はこうなる
どうかなぁ。