groovy でリストからマップを作成するためのショートカット?
-
09-06-2019 - |
質問
これについて何らかの解決策が欲しいのですが:
Map rowToMap(row) {
def rowMap = [:];
row.columns.each{ rowMap[it.name] = it.val }
return rowMap;
}
GDK の仕組みを考えると、次のようなことができると期待されます。
Map rowToMap(row) {
row.columns.collectMap{ [it.name,it.val] }
}
しかし、ドキュメントでは何も見ていません...私は何かを見逃していますか?それとも私が怠け者なのでしょうか?
解決
私は最近、まさにそれを行う必要があることに気づきました。リストをマップに変換します。この質問は Groovy バージョン 1.7.9 がリリースされる前に投稿されたものであるため、この方法は collectEntries
まだ存在していませんでした。それはまさに次のように機能します collectMap
方法 それが提案された:
Map rowToMap(row) {
row.columns.collectEntries{[it.name, it.val]}
}
何らかの理由で古い Groovy バージョンを使用しなくなった場合は、 inject
方法も使用できます(提案されているように) ここ)。これはわずかに変更されたバージョンで、クロージャ内の式を 1 つだけ取ります (文字を節約するためだけです!)。
Map rowToMap(row) {
row.columns.inject([:]) {map, col -> map << [(col.name): col.val]}
}
の +
演算子の代わりに演算子を使用することもできます。 <<
.
他のヒント
「注入」を調べてください。実際の関数型プログラミングの専門家は、これを「フォールド」と呼んでいます。
columns.inject([:]) { memo, entry ->
memo[entry.name] = entry.val
return memo
}
そして、その作業中に、メタクラス上で直接メソッドを定義するのではなく、カテゴリとしてメソッドを定義したいと思うかもしれません。こうすることで、すべてのコレクションに対して一度定義できます。
class PropertyMapCategory {
static Map mapProperty(Collection c, String keyParam, String valParam) {
return c.inject([:]) { memo, entry ->
memo[entry[keyParam]] = entry[valParam]
return memo
}
}
}
使用例:
use(PropertyMapCategory) {
println columns.mapProperty('name', 'val')
}
だった グループ化 この質問が行われた時点ではこの方法は利用できませんでしたか?
また、Google コレクションを使用している場合 (http://code.google.com/p/google-collections/)、次のようなことができます。
map = Maps.uniqueIndex(list, Functions.identity());
わかりました...これをもう少し試してみましたが、これはかなり素晴らしい方法だと思います...
def collectMap = {Closure callback->
def map = [:]
delegate.each {
def r = callback.call(it)
map[r[0]] = r[1]
}
return map
}
ExpandoMetaClass.enableGlobally()
Collection.metaClass.collectMap = collectMap
Map.metaClass.collectMap = collectMap
現在、Map または Collection のサブクラスにはこのメソッドがあります...
ここでは、マップ内のキー/値を反転するために使用します。
[1:2, 3:4].collectMap{[it.value, it.key]} == [2:1, 4:3]
ここではそれを使用してリストからマップを作成します
[1,2].collectMap{[it,it]} == [1:1, 2:2]
ここでは、アプリの起動時に呼び出されるクラスにこれをポップするだけで、このメソッドはコード全体で使用できます。
編集:
すべての配列にメソッドを追加するには...
Object[].metaClass.collectMap = collectMap
必要なものが単純なキーと値のペアである場合、メソッドは collectEntries
十分なはずです。例えば
def names = ['Foo', 'Bar']
def firstAlphabetVsName = names.collectEntries {[it.charAt(0), it]} // [F:Foo, B:Bar]
ただし、キーごとに複数の値があるマルチマップに似た構造が必要な場合は、 groupBy
方法
def names = ['Foo', 'Bar', 'Fooey']
def firstAlphabetVsNames = names.groupBy { it.charAt(0) } // [F:[Foo, Fooey], B:[Bar]]
内蔵されているものが見つからない...ただし、ExpandoMetaClass を使用すると、これを行うことができます。
ArrayList.metaClass.collectMap = {Closure callback->
def map = [:]
delegate.each {
def r = callback.call(it)
map[r[0]] = r[1]
}
return map
}
これにより、collectMap メソッドがすべての ArrayList に追加されます...リストまたはコレクションに追加しても機能しなかった理由はわかりません。それは別の質問になると思います...でも今ならこれができるよ…
assert ["foo":"oof", "42":"24", "bar":"rab"] ==
["foo", "42", "bar"].collectMap { return [it, it.reverse()] }
1 つのクロージャーを使用して、List から計算された Map に変換します...まさに私が探していたもの。
編集:List インターフェイスと Collection インターフェイスにメソッドを追加できなかった理由は、これを実行しなかったためです。
List.metaClass.enableGlobally()
そのメソッド呼び出しの後、インターフェイスにメソッドを追加できます。これは、この場合、collectMap メソッドが次のような範囲で機能することを意味します。
(0..2).collectMap{[it, it*2]}
これによりマップが得られます。[0:0、1:2、2:4]
こんなものはどうでしょうか?
// setup
class Pair {
String k;
String v;
public Pair(def k, def v) { this.k = k ; this.v = v; }
}
def list = [ new Pair('a', 'b'), new Pair('c', 'd') ]
// the idea
def map = [:]
list.each{ it -> map.putAt(it.k, it.v) }
// verify
println map['c']