Groovyのリストからマップを作成するためのショートカット?


106

私はこれのためにいくつかの並べ替えをお願いします:

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] }
}

しかし、ドキュメントで何も見ていません...何か不足していますか?それとも私はあまりにも怠惰ですか?


2
アミールのコメントが正解になりました:stackoverflow.com/a/4484958/27561
Robert Fischer

回答:


119

私は最近、まさにそれを行う必要性に出くわしました:リストをマップに変換します。この質問は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]}
}

+オペレータはまた、代わりに使用することができます<<


28

「注入」をチェックしてください。実際の関数型プログラミングは、それを「フォールド」と呼びます。

columns.inject([:]) { memo, entry ->
    memo[entry.name] = entry.val
    return memo
}

そして、その間、おそらくmetaClassではなく、カテゴリとしてメソッドを定義したいと思うでしょう。これにより、すべてのコレクションに対して一度定義できます。

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')
}

Smalltalkのinject:into:に触発された可能性があるため、Groovyではinjectという名前になっていると思います。リスト合計| list:= OrderedCollection new add:1; 追加:2; 追加:3; あなた自身。sum:= list inject:0 into:[:a:b | a + b]。トランスクリプトcr; 表示:合計。「プリント6」
OlliP 2013年

13

GROUPBYのこの質問を頼まれたとき、この方法は使用できませんか?


いいえのようです-それは2011年1月8日からです。質問は2008年に尋ねられました。しかし、いずれにしても、groupByが実際に進む方法です。
mvmn 2014

groupByのドキュメントでわかるように、基本的に要素をグループにグループ化します。各グループには特定のキーに一致する要素が含まれます。したがって、その戻り値の型はMap<K, List<V>>OPが戻り値の型を持つメソッドを探しているMap<K, V>ようなので、この場合groupByは機能しません。
Krzysiek Przygudzki

6

必要なのが単純なキーと値のペアである場合は、メソッド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]]


5

わかりました...これをもう少し試してみましたが、これはかなりクールな方法だと思います...

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

1

何も組み込まれていません...しかし、ExpandoMetaClassを使用してこれを行うことができます。

ArrayList.metaClass.collectMap = {Closure callback->
    def map = [:]
    delegate.each {
        def r = callback.call(it)
        map[r[0]] = r[1]
    }
    return map
}

これにより、collectMapメソッドがすべてのArrayListsに追加されます... ListまたはCollectionに追加できなかった理由がわかりません。これは別の質問だと思います...しかし、今、これを行うことができます...

assert ["foo":"oof", "42":"24", "bar":"rab"] ==
            ["foo", "42", "bar"].collectMap { return [it, it.reverse()] }

リストから1つのクロージャを使用して計算されたマップまで...まさに私が探していたもの。

編集:リストとコレクションのインターフェイスにメソッドを追加できなかったのは、これを行わなかったためです。

List.metaClass.enableGlobally()

そのメソッド呼び出しの後、インターフェースにメソッドを追加できます。この場合、私のcollectMapメソッドは次のような範囲で機能します。

(0..2).collectMap{[it, it*2]}

これはマップを生成します:[0:0、1:2、2:4]


0

このようなものはどうですか?

// 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']

基本的には私の質問と同じです... .putAtの代わりにmap [it.k] = it.vを使用しました。1つのライナーを探していました。
danb 2008年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.