だから本質的に私はこのようなものを持っています:
[a: ["c","d"], b: ["e","f"]]
各リストの項目数は任意です。項目が 1 つしかない場合、リストはリストではなくなり、文字列になります。
私はそれを次のように変えたい:
[ [a:"c", b:"e"], [a:"d",b:"f"] ]
ソリューションが Groovy メソッドを使用するかどうかはあまり気にしません。ご協力いただきありがとうございます!
これを行う別の方法は、かなり簡潔でありながら、あまり曖昧ではないと思います。
def ml = [a: ["c","d"], b: ["e","f"]]
// Create an empty list that creates empty maps as needed
def lm = [].withDefault{ [:] }
ml.each{ k, values ->
[values].flatten().eachWithIndex { value, index ->
lm[index][k] = value
}
}
assert lm == [[a:"c", b:"e"], [a:"d", b:"f"]]
使用したくない、または使用できない場合withDefault
(リストを自動的に拡大したくないため)、これも機能します。
def ml = [a: ["c","d"], b: ["e","f"]]
def lm = []
ml.each{ k, values ->
[values].flatten().eachWithIndex { value, index ->
lm[index] = lm[index] ?: [:]
lm[index][k] = value
}
}
assert lm == [[a:"c", b:"e"], [a:"d", b:"f"]]
編集:リストに含まれていない文字列を処理するコードを追加しました。
[values].flatten().eachWithIndex{...}
与えられたトリック( )は必ずしも非常に効率的ではないことに注意してください。速度が重要な場合は、読みやすさを犠牲にして、これを使用すると少し速くなります。
(values instanceof List ? values : [values]).eachWithIndex{...}
[a: ["c","d"], b: ["e","f"]]
x =または x =を想定したワンライナー[a: "b", c: "d"]
:
[x*.key, x*.value].transpose()*.combinations().transpose()*.flatten()*.toSpreadMap()
仕組み:
まず、キーと値を分割します。
[x*.key, x*.value]
=[[a, b], [[c, d], [e, f]]]
それらを転置して、キーと値をペアにします。
[[a, b], [[c, d], [e, f]]].transpose()
=[[a, [c, d]], [b, [e, f]]]
combinations
キーとその値をペアにするために使用します (ここでは、各リスト要素に適用するために使用されるスプレッド演算子)。組み合わせは両方[a:b]
または[a:[b,c]]
正しく処理されることに注意してください。
[[a, [c, d]], [b, [e, f]]]*.combinations()
=[[[a, c], [a, d]], [[b, e], [b, f]]]
aabb の代わりに abab になるようにリストを転置します (多少入れ子になっていますが)。
[[[a, c], [a, d]], [[b, e], [b, f]]].transpose()
=[[[a, c], [b, e]], [[a, d], [b, f]]]
ネストされたリストをフラット化します (ネストされたリストをフラット化するために再度スプレッドを使用しますが、リスト全体ではありません):
[[[a, c], [b, e]], [[a, d], [b, f]]]*.flatten()
=[[a, c, b, e], [a, d, b, f]]
toSpreadMap
このリストをマップのリストに変換するには、スプレッドを使用します。
[[a, c, b, e], [a, d, b, f]]*.toSpreadMap()
=[*:[b:e, a:c], *:[b:f, a:d]]
いくつかの関数を定義します:
// call a 2-element list a "pair"
// convert a map entry (where entry.value can be
// a single string or a list of strings) into a list of pairs
def pairs(entry) {
if (entry.value instanceof String)
return [[entry.key, entry.value]]
entry.value.collect { [entry.key, it]}
}
// convert list of pairs to a map
def toMap(pairs) {
pairs.inject([:]){ m,i -> m[i[0]] = i[1]; m }
}
// kind of like transpose but doesn't stop with shortest list.
// (would like to find a less ugly way of doing this)
def mytranspose(lists) {
def retval = []
def mx = lists.inject(0){x, i -> i.size() > x ? i.size() : x}
for (int i = 0; i < mx; i++) {
def row = []
lists.each { lst ->
if (lst.size() > i) row << lst[i]
}
retval << row
}
retval
}
次に、それをまとめてテストします。
groovy:000> m = [a: ["c","d"], b: ["e","f"]]
groovy:000> mytranspose(m.entrySet().collect{pairs(it)}).collect{toMap(it)}
===> [{a=c, b=e}, {a=d, b=f}]
文字列のマップ エントリは機能し、マップ エントリ リストはさまざまな長さにすることができます。
groovy:000> m['g'] = 'h'
===> h
groovy:000> m['x'] = ['s', 't', 'u', 'v']
===> [s, t, u, v]
groovy:000> m
===> {a=[c, d], b=[e, f], g=h, x=[s, t, u, v]}
groovy:000> mytranspose(m.entrySet().collect{pairs(it)}).collect{toMap(it)}
===> [{a=c, b=e, g=h, x=s}, {a=d, b=f, x=t}, {x=u}, {x=v}]