リストを項目ごとに集計する
2015/5/3 に続きっぽいのを書きました → リストを項目ごとに集計する - Java8ばーじょん - 日々常々
データをコードごとに集計することってのは結構よくあります。
例えばこんなデータを…
code | name | value |
---|---|---|
A01 | hoge | 100 |
A01 | piyo | 200 |
A02 | hoge | 300 |
A03 | hoge | 400 |
A03 | piyo | 500 |
codeごとに集計してこうしたい。
code | value |
---|---|
A01 | 300 |
A02 | 300 |
A03 | 900 |
現場でよく見るのはこんな感じになってます。
// データはこれのリストに入ってるてことで。 class Data { String code; String name; int value; } public List<Data> summary(List<Data> list) { List<Data> result = new ArrayList<>(); Data data = null; for (Data d : list) { if (data == null || !data.code.equals(d.code)) { if (data != null) { result.add(data); } data = new Data(); data.code = d.code; } data.value += d.value; } if (data != null) { result.add(data); } return result; }
一般的にどういうのかは知らないですが、私の現場では「ブレイク処理」とか言われてます。コードが変わるタイミングでブレイクするとかそんな意味らしいです。
前のDataを保持しながらcodeが変わっているかを比較し、変わっていたら結果リストに前のDataを追加して新しいDataを作り、codeが変わっていなかったら前のDataに、valueを加算する。ループが終わったら結果に最後まで残ってたDataを追加する。言葉で書くとわけわかんない…。
データは元々RDBに入ってたりするので、「集計はSQLでやれば良いのにー」と思わなくはないのですが、なんかあんまりやらないですね。取得したデータをさらに加工しながら集計するとかもあるので、この辺は一概には言えないところかも。まぁそれはとりあえず置いておきます。
この処理はだいたい定形的に書かれるのですけど、正直意味が分かりにくいし、よくバグってるのを見ます。こんな面倒なことやってたらバグっても仕方ないと思うのです。でも、こういうのの典型的なのってどう書くものなんだろう。
私はこんなふうに書いたりします。
public static List<Data> summary(List<Data> list) { Map<String, Data> map = new LinkedHashMap<>(); for (Data d : list) { if (!map.containsKey(d.code)) { Data data = new Data(); data.code = d.code; map.put(d.code, data); } map.get(d.code).value += d.value; } return new ArrayList<Data>(map.values()); }
入力値がコードでソートされていない場合の挙動が違いますが、そこは同じコードを集計するのが目的だとかなんとか言ってごまかす感じで。なんかこう、スマートな書き方あったら、教えて下さると、とても嬉しい…。