あけましておめでとうございます。書き初めならぬ、エントリ初め。今年もよろしくお願いします。みなさまにとって飛躍の年になりますように。
さて、Groovy 1.8.1以降で追加されたGDKメソッド(主にコレクション関係)を解説するシリーズ第2弾、「複数クロージャによる多段groupBy」です。
通常のgroupBy
「多段groupBy」を説明するまえに、従来の(単段)groupByの動作について説明しておきましょう。groupByは「コレクションを分類する」ものです。「要素がどんな分類に所属するか」を定義するクロージャ(ここでは「分類クロージャ」とでも呼んでおきます)を渡してやると、「分類」と、「その分類に所属する要素」をマップのキー、バリューで表現して返してくれます。図にするとこうです。
バリュー部分はリストになります。
たとえば、文字列のコレクションを「先頭文字のアルファベット(大文字)」で分類したいのであれば、文字列の先頭文字を返すようなクロージャを与えるだけです。
collection = ['dog','doctor', 'cat', 'ash', 'april'] result = collection.groupBy{ it[0].toUpperCase() } assert [D:['dog', 'doctor'], C:['cat'], A:['ash', 'april']] == result
groupByは、一言で言ってSQLのSELECT文でGROUP BY句を指定することに相当するような機能です。
複数クロージャによる多段groupBy
さて本題ですが、上のようなgroupByに対してgroovy 1.8.1で追加されたのは以下のバリエーションです。
- public Map groupBy(Object closures)
- public Map groupBy(List closures)
1つめのObjectを渡す方は、クロージャの配列、もしくは複数のクロージャを可変個数引数で渡すことができます。2番目のリストを渡す方でも、いずれも複数のクロージャを渡すことができます。
複数のクロージャを渡す場合、2番目以降のクロージャは、1番目の分類結果のバリュー部分をさらに2番目以降のクロージャで分類することができます。ネストしたマップが返ってきます。
例えば、以下のようなPersonクラスがあったとします。
class Person { String name, String gender }
以下のようにPersonのリストpeopleに対して、まず性別で分類し、次に名前で分類する、ということができます。
class Person { String name; String gender; String toString(){ name } } people = [ new Person(name:'Yamada Tarou', gender:'male'), new Person(name:'Yamada Hanako', gender:'female'), new Person(name:'Mine Fujiko', gender:'female'), new Person(name:'Jigen Daisuke', gender:'male'), new Person(name:'Lupin III', gender:'male') ] result = people.groupBy { it.gender } { it.name[0] } print result //以下が表示される // [male:[Y:[Yamada Tarou], J:[Jigen Daisuke], L:[Lupin III]], female:[Y:[Yamada Hanako], M:[Mine Fujiko]]]
3段以上のネストも可能です。