使用转置以完整的外部连接样式合并列表



我正在尝试返回一个连接列表。但是联接必须像数据库完全外联接一样。

例如,给定以下内容:

def x = [ [a:1, b:2], [a:1, b:3], [a:2, b:4], [a:3, b:5] ]
def y = [ [f:10, b:2, g:7], [f:100, b:3, g:8], [f:20, b:4, g:9], [f:20, b:6, g:9]  ]

我想返回:

[[a:1, b:2, f:10, g:7], [a:1, b:3, f:100, g:8], [a:2, b:4, f:20, g:9], [a:3, b:5], [a:3, b:6, f:20, g:9]]

在上一个问题中,有人向我展示了如何使用转置来加入列表。

def z = [x,y].transpose().collect { a, b -> a + b }
println z​

但是,其输出与预期输出相差[a:3, b:5]。见下文。

[[a:1, b:2, f:10, g:7], [a:1, b:3, f:100, g:8], [a:2, b:4, f:20, g:9], [a:3, b:6, f:20, g:9]]

有人可以帮助我使用所需的表达式并帮助我理解为什么原始表达式不起作用吗?

首先,让我们解释一下GroovyCollections.transpose(List lists)如何解决这个问题。对于给定的输出

def x = [ [a:1, b:2], [a:1, b:3], [a:2, b:4], [a:3, b:5] ]
def y = [ [f:10, b:2, g:7], [f:100, b:3, g:8], [f:20, b:4, g:9], [f:20, b:6, g:9] ]

表达

[x,y].transpose()

创建对列表:

[
[[a:1, b:2], [f:10, b:2, g:7]],
[[a:1, b:3], [f:100, b:3, g:8]],
[[a:2, b:4], [f:20, b:4, g:9]],
[[a:3, b:5], [f:20, b:6, g:9]]
]

如果我们将其与您期望获得的列表进行比较

def expected = [[a:1, b:2, f:10, g:7], [a:1, b:3, f:100, g:8], [a:2, b:4, f:20, g:9], [a:3, b:5], [a:3, b:6, f:20, g:9]]

我们可以看到它包含的不是 4 个,而是 5 个元素。在调查所需结果后,可以找到一个隐藏的要求:如果有一对两个映射至少有一个公共键,但每个对元素有两个不同的值,则不要合并这两个对,而是创建两个映射,其中第一个映射是来自x并且保持不变的映射,第二个映射是合并来自xy.

如何检查两个映射是否至少有一个不同值的公用键?

我们可以为此使用Collection.intersect(Collection right)。但我们必须比较两个交叉点:

  1. Map.keySet()键的交集
  2. 两个地图中Map.Entry元素的交集

第一个交叉点将告诉我们两个映射中是否有相同的键,而第二个交叉点将告诉我们它们是否存储相同的值。如果两个表达式的计算结果都为true,我们将将它们与您提到的示例中的a + b合并(如果两个映射没有任何共同键,我们也将使用相同的方法)。但是,如果两个映射都有非空键的交集,而映射条目的交集不等于第一个交集的结果,我们将使用[a, a+b]合并这些映射,我们最终将.flatten()结果。下面你可以找到一个Groovy代码,它完成了我刚才描述的操作:

def x = [[a: 1, b: 2], [a: 1, b: 3], [a: 2, b: 4], [a: 3, b: 5]]
def y = [[f: 10, b: 2, g: 7], [f: 100, b: 3, g: 8], [f: 20, b: 4, g: 9], [f: 20, b: 6, g: 9]]
def expected = [[a: 1, b: 2, f: 10, g: 7], [a: 1, b: 3, f: 100, g: 8], [a: 2, b: 4, f: 20, g: 9], [a: 3, b: 5], [a: 3, b: 6, f: 20, g: 9]]
def shareSameKeyWithSameValue(Map<String, ?> a, Map<String, ?> b) {
final Set<String> keysIntersectionFromEntries = (a.entrySet().intersect(b.entrySet())).key as Set
final Set<String> keysIntersection = a.keySet().intersect(b.keySet())
return !keysIntersectionFromEntries.isEmpty() && keysIntersectionFromEntries.containsAll(keysIntersection)
}
def result = [x, y].transpose().collect { a, b ->
shareSameKeyWithSameValue(a, b) ? a + b : [a, a + b]
}.flatten()
assert result == expected

我希望它有所帮助。

最新更新