列表的Groovy地图到地图列表
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了列表的Groovy地图到地图列表相关的知识,希望对你有一定的参考价值。
所以基本上我有这样的事情:
[a: ["c","d"], b: ["e","f"]]
每个列表中的项目数量是任意的。如果只有一个项目,则列表不再是列表,而是一个字符串。
我想把它变成:
[ [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{...}
单线,假设x = [a: ["c","d"], b: ["e","f"]]
或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]]]
转置列表以便我们最终得到abab而不是aabb(虽然有点嵌套):
[[[a, c], [a, d]], [[b, e], [b, f]]].transpose()
= [[[a, c], [b, e]], [[a, d], [b, f]]]
展平嵌套列表(再次使用spread来展平嵌套列表,但不是整个列表):
[[[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}]
这就是我最终做的事情。如果有人有更好的解决方案,请告诉我,我会接受它作为答案。
Map xyz = [a: ["c","d"], b: ["e","f"]]
List result = []
Closure updateMap = { list, index, key, value ->
if ( !(list[index] instanceof Map) ) {
list[index] = [:]
}
list[index]."$key" = value
}
xyz.each { k, v ->
if (v instanceof ArrayList) {
v.eachWithIndex { val, idx ->
updateMap(result, idx, k, val)
}
}
else {
updateMap(result, 0, k, v)
}
}
以上是关于列表的Groovy地图到地图列表的主要内容,如果未能解决你的问题,请参考以下文章