list 列表的 Groovy 映射到映射列表

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15578281/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-11 01:59:12  来源:igfitidea点击:

Groovy Map of Lists into List of Maps

listmapgroovy

提问by James Kleeh

So essentially I have something like this:

所以基本上我有这样的事情:

[a: ["c","d"], b: ["e","f"]]

The amount of items in each list is arbitrary. If there is only one item the list is no longer a list and it is a string.

每个列表中的项目数量是任意的。如果只有一项,则列表不再是列表,而是字符串。

I want to turn it into:

我想把它变成:

[ [a:"c", b:"e"], [a:"d",b:"f"] ]

I don't really care if the solution uses Groovy methods or not. Thanks for your help!

我真的不在乎解决方案是否使用 Groovy 方法。谢谢你的帮助!

回答by OverZealous

Here's another way to do it, that I think is less obscure while still being fairly concise:

这是另一种方法,我认为它不那么晦涩,同时仍然相当简洁:

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

If you don't want or cannot use withDefault(because you don't want the list to grow automatically), then this works too:

如果您不想或不能使用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"]]


Edit: Added code to handle strings not contained within a list.

编辑:添加了处理列表中未包含的字符串的代码。

Note, the given trick ([values].flatten().eachWithIndex{...}) is not necessarily very efficient. If speed is essential, then using this would be slightly faster at the expense of readability:

请注意,给定的技巧 ( [values].flatten().eachWithIndex{...}) 不一定非常有效。如果速度是必不可少的,那么使用它会以牺牲可读性为代价稍微快一点:

(values instanceof List ? values : [values]).eachWithIndex{...}

回答by slipheed

One-liner, assuming x = [a: ["c","d"], b: ["e","f"]]or x = [a: "b", c: "d"]:

单行,假设 x =[a: ["c","d"], b: ["e","f"]]或 x = [a: "b", c: "d"]

[x*.key, x*.value].transpose()*.combinations().transpose()*.flatten()*.toSpreadMap()

How this works:

这是如何工作的:

First, split the keys and values:

首先,拆分键和值:

[x*.key, x*.value]= [[a, b], [[c, d], [e, f]]]

[x*.key, x*.value]= [[a, b], [[c, d], [e, f]]]

Transpose them to pair up keys and values:

转置它们以配对键和值:

[[a, b], [[c, d], [e, f]]].transpose()= [[a, [c, d]], [b, [e, f]]]

[[a, b], [[c, d], [e, f]]].transpose()= [[a, [c, d]], [b, [e, f]]]

Use combinationsto pair up the key with its values (spread operator used here to apply it to each list element). Note that combinations will deal with both [a:b]or [a:[b,c]]correctly:

用于combinations将键与其值配对(此处使用扩展运算符将其应用于每个列表元素)。请注意,组合将同时处理[a:b][a:[b,c]]正确处理:

[[a, [c, d]], [b, [e, f]]]*.combinations()= [[[a, c], [a, d]], [[b, e], [b, f]]]

[[a, [c, d]], [b, [e, f]]]*.combinations()= [[[a, c], [a, d]], [[b, e], [b, f]]]

Transpose the lists so that we end up with abab instead of aabb (though nested somewhat):

转置列表,以便我们最终得到 abab 而不是 aabb(尽管有些嵌套):

[[[a, c], [a, d]], [[b, e], [b, f]]].transpose()= [[[a, c], [b, e]], [[a, d], [b, f]]]

[[[a, c], [a, d]], [[b, e], [b, f]]].transpose()= [[[a, c], [b, e]], [[a, d], [b, f]]]

Flatten the nested lists (using spread again to flatten nested lists,but not the whole list):

展平嵌套列表(再次使用传播来展平嵌套列表,但不是整个列表):

[[[a, c], [b, e]], [[a, d], [b, f]]]*.flatten()= [[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]]

Spread toSpreadMapto convert this list into a list of maps.

展开toSpreadMap以将此列表转换为地图列表。

[[a, c, b, e], [a, d, b, f]]*.toSpreadMap()= [*:[b:e, a:c], *:[b:f, a:d]]

[[a, c, b, e], [a, d, b, f]]*.toSpreadMap()= [*:[b:e, a:c], *:[b:f, a:d]]

回答by Nathan Hughes

Define some functions:

定义一些函数:

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

then put it together and test it:

然后把它放在一起并测试它:

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

Map entries that are strings work, and map entry lists can be different lengths:

作为字符串的映射条目有效,并且映射条目列表的长度可以不同:

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

回答by James Kleeh

Here is what I ended up doing. If anyone has a better solution, let me know and I will accept it as the answer.

这是我最终做的。如果有人有更好的解决方案,请告诉我,我会接受它作为答案。

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