Groovyマップに関する私の質問。現在のエントリを上書きせずに、プログラムで新しいエントリをGroovyマップに追加する方法を探しています。例えば
def editsMap = [:]
lineEdits.flag.each
{ lineEdits_Flag ->
editsMap.put('FlagId',lineEdits_Flag.id)
editsMap.put('FlagMnemonic',lineEdits_Flag.mnemonic)
editsMap.put('Action',lineEdits_Flag.action)
println "editsMap: ${editsMap}"
}
最初のパスはこのマップを生成します:
editsMap:[FlagId:10001、FlagMnemonic:TRA、Action:review]
しかし、2番目のパスは最初のパスをeditsMap:[FlagId:10002、FlagMnemonic:REB、Action:deny]で上書きします。
私がやろうとしていることは、1つのマップ内に複数のエントリを作成することです。次のようなものを入力するためにマップが必要です。
editsMap: [FlagId:10001, FlagMnemonic:TRA, Action:review]
editsMap: [FlagId:10002, FlagMnemonic:REB, Action:deny]
editsMap: [FlagId:10003, FlagMnemonic:UNB, Action:deny]
editsMap: [FlagId:20001, FlagMnemonic:REB, Action:deny]
editsMap: [FlagId:20002, FlagMnemonic:ICD, Action:review]
editsMap: [FlagId:30001, FlagMnemonic:REB, Action:deny]
editsMap: [FlagId:40001, FlagMnemonic:ICD, Action:review]
editsMap: [FlagId:40002, FlagMnemonic:MPR, Action:review]
editsMap: [FlagId:50001, FlagMnemonic:CPT, Action:deny]
editsMap: [FlagId:60001, FlagMnemonic:DTU, Action:deny]
editsMap: [FlagId:70001, FlagMnemonic:ICD, Action:review]
editsMap: [FlagId:70002, FlagMnemonic:MPR, Action:review]
マップにデータを入力したら、メッセージを処理するために特定の値を見つける必要があります。私は次のようなものを使用できると信じています:
def thisValue = appliedEditsMap[FlagId, '10001'] ?: "default"
クイックルックアップを行います。
マップに既にある値を上書きせずに、プログラムでGroovyマップに値を追加する方法を誰かが理解できるようにしてください。
GuavaのMultiMap のようなものが必要です。
Multimap<String, String> myMultimap = ArrayListMultimap.create();
// Adding some key/value
myMultimap.put("Fruits", "Bannana");
myMultimap.put("Fruits", "Apple");
myMultimap.put("Fruits", "Pear");
myMultimap.put("Vegetables", "Carrot");
// Getting values
Collection<string> fruits = myMultimap.get("Fruits");
System.out.println(fruits); // [Bannana, Apple, Pear]
この男 は、マルチマップの純粋なGroovyエミュレーションを作成します。
class GroovyMultimap {
Map map = [:]
public boolean put(Object key, Object value) {
List list = map.get(key, [])
list.add(value)
map."$key" = list
}
}
putAt
とgetAt
を使用して、マップ操作で合成糖を使用できます。マップオブジェクトで mixin を試すこともできます。
彼はGuavaのマルチマップでGroovyも使用しています。
List properties = ['value1', 'value2', 'value3']
Multimap multimap = list.inject(LinkedListMultimap.create()) {
Multimap map, object ->
properties.each {
map.put(it, object."$it")
}
map
}
properties.each {
assertEquals (multimap.get(it), list."$it")
}
私はこれを数年前に別のサイトで同様の質問への回答として見つけました。私はそれがどこから来たのかを見つけることができないので、誰かがソースを知っているなら、ここに投稿してください。
LinkedHashMap.metaClass.multiPut << { key, value ->
delegate[key] = delegate[key] ?: []; delegate[key] += value
}
def myMap = [:]
myMap.multiPut("a", "1")
myMap.multiPut("a", "2")
myMap.multiPut("a", "3")
myMap.each {key, list ->
println "${key} -> $value.list(",")
}
与える:
a -> 1,2,3
注入されたmultiPut()メソッドを使用すると、魔法がかかります。
次のようなこともできます:
// Dummy map for testing
lineEdits = [ flag:[
[id:10001, mnemonic:'TRA', action:'review'],
[id:10002, mnemonic:'REB', action:'deny'],
[id:10003, mnemonic:'UNB', action:'deny'],
[id:20001, mnemonic:'REB', action:'deny'],
[id:20002, mnemonic:'ICD', action:'review'],
[id:30001, mnemonic:'REB', action:'deny'],
[id:40001, mnemonic:'ICD', action:'review'],
[id:40002, mnemonic:'MPR', action:'review'],
[id:50001, mnemonic:'CPT', action:'deny'],
[id:60001, mnemonic:'DTU', action:'deny'],
[id:70001, mnemonic:'ICD', action:'review'],
[id:70002, mnemonic:'MPR', action:'review'] ] ]
def editsMap = lineEdits.flag
.groupBy { it.id } // Group by id
.collectEntries { k, v ->
[ k, v[ 0 ] ] // Just grab the first one (flatten)
}
assert editsMap[ 60001 ] == [ id:60001, mnemonic:'DTU', action:'deny' ]
外部クラスなしでマルチマップを実行したい場合は、代わりにリストのマップを格納するだけで済みます。構文は面倒なものではありません。
def editsMap = [:].withDefault{[]} lineEdits.flag.each { lineEdits_Flag -> editsMap.FlagId << lineEdits_Flag.id editsMap.FlagMnemonic << lineEdits_Flag.mnemonic editsMap.Action << lineEdits_Flag.action println "editsMap: ${editsMap}" }
または、元の構文を本当に優先した場合、次のようになります。editsMap.get('FlagId').add(lineEdits_Flag.id)
またはこれでも機能するはずです:editsMap.get('FlagId') << lineEdits_Flag.id
このソリューションの利点は、何をしているのかがより明確になる傾向があることです...たとえば、それはマジックマップではありません。これは、単一のアイテムをリスト(標準のマップコントラクトではありません)に変換しますが、常にリストのマップであり、単にリストのマップとして使用します。
.getは常にマルチマップが記述されたのと同じように機能します。マップ内のそのアイテムのリストを常に返します。
マップはキーと値のマッピングのセットであり、キーを使用して後で検索できるように、キーごとに異なる値をプラグインします。あなたの例は同じキーの値を何度もプラグインすることです。一意のキーを選択する必要があります。
マップの1つのエントリの値を格納するクラスを作成します。
class Stuff {
String flagMnemonic
String action
}
キーとしてflagIdを使用するマップを作成します(フラグを一意に識別するためです)。値としてStuffを使用します(検索するデータであるため)。
def editsMap = [:]
ここで型宣言を使用し、flagIdが文字列の場合、マップの型はMap<String, Stuff>
になります。
これで、マップにアイテムを配置できます。
lineEdits.flag.each { lineEdits_Flag ->
editsMap[lineEdits_Flag.id] =
new Stuff(
flagMnemonic: lineEdits_Flag.mnemonic,
action: lineEdits_Flag.action)
}
そしてそれを取り戻す
def myStuffFor10001 = editsMap['10001']
println myStuffFor10001.flagMnemonic // should equal 'TRA'
println myStuffFor10001.action // should equal 'review'
また、?: "default"
を使用してデフォルト値を設定する代わりに簡単な方法があります。マップの作成時にwithDefault
を使用できます。
def defaultStuff = new Stuff(
flagMnemonic: "defaultMnemonic", action:"defaultAction")
def editsMap = [:].withDefault { defaultStuff }
そのため、マップに存在しないものを要求すると、指定されたデフォルトオブジェクトが取得されます。
次のようなリストとクロージャを使用しないのはなぜですか。
editsList = [
[FlagId:10001, FlagMnemonic:TRA, Action:review],
[FlagId:10002, FlagMnemonic:REB, Action:deny],
[FlagId:10003, FlagMnemonic:UNB, Action:deny],
[FlagId:20001, FlagMnemonic:REB, Action:deny],
[FlagId:20002, FlagMnemonic:ICD, Action:review],
[FlagId:30001, FlagMnemonic:REB, Action:deny],
[FlagId:40001, FlagMnemonic:ICD, Action:review],
[FlagId:40002, FlagMnemonic:MPR, Action:review],
[FlagId:50001, FlagMnemonic:CPT, Action:deny],
[FlagId:60001, FlagMnemonic:DTU, Action:deny],
[FlagId:70001, FlagMnemonic:ICD, Action:review],
[FlagId:70002, FlagMnemonic:MPR, Action:review]
]
def appliedEditsMap = {property,idValue->
return editsList.find{it[property] == idValue}
}
def thisValue = appliedEditsMap(FlagId, '10001') ?: "default"
私は最近同様の問題を抱えていましたが、一部の同僚がそれを行っていたため、それが可能であることがわかっていました。ここで答えを読んで実験して、ようやく、自分のユースケースで機能し、読むのが難しくない簡単な答えを見つけました。 「一般的なコード」の回答を書くと、適切な列名などを含むコードよりも読みにくくなります。
私の場合、レポクエリからList<Map>
が返されました。 Map<String, List<Object>>
のようなものが必要で、結果セットのキーが以前のキーと一致した場合はList
に追加する必要があります。もちろん、私のObject
はPOJOではありませんでしたが、どのクラスでも使用できます。さらに複雑にするために、いくつかの結果値から複合キーを作成する必要がありました(質問しないで、作成しませんでした)andこれらのキーを元のマップから削除して、残りのエントリを使用してビジネスオブジェクトを作成できます。
これが私がしたことです:
List<Map> listMap = repo.findWhateverItWas()
Map<String, List<Object>> resultMap = [:].withDefault {[]} //required to avoid NPE
listMap.each { Map<String, Object> it ->
String compKey = it['col1'] + it['col2'] + it['col3']
Map tempMap =[:]
it.keySet.each { k ->
if (!(k in ['col1','col2','col3'])) {
tempMap << [(k): it[k]] // this is the business Object result map
}
}
// createEntity is a static Entity Factory
// the simple += is the magic
resultMap[(compKey)] += createEntity(Entity.class, tempMap)
}
return resultMap
これはあなたの特定のシナリオに対応していないと思いますが、それは質問に答え、より複雑な状況に対する答えを提供すると信じています。
簡単なテストケースで、これが期待する機能を証明することができました。私たちはスポックを使います...
def "Map of Lists test"() {
given:
Map<String, List<String>> map = [:].withDefault { [] }
when:
map['key1'] += 'item1'
map['key1'] += 'item2'
map['key2'] += 'item3'
map['key1'] += 'item4'
map['key2'] += 'item5'
then:
map['key1'] == ['item1', 'item2', 'item4']
map['key2'] == ['item3', 'item5']
}
これをクラスに配置してから、そのクラスをマップに追加する必要があります。あなたの情報は関連していると私が思うところから、何かを見逃していない限り、保存するクラスを持つことは理にかなっています
あなたができることはあなたのクラスを次のように定義することです
class Flag {
String flagID
String flagMnemonic
String action
}
Now Put your Flag in to your map as
editsMap.put(10000,newFlag(flagID:'10000',flagMnemonic:'TES',action:'tes'))