使用转置以完全外部联接样式合并列表

时间:2017-08-27 13:23:20

标签: 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]  ]

我想回复:

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

有人可以帮助我解决所需的表达方式并帮助我理解原始表达式无效的原因吗?

1 个答案:

答案 0 :(得分:2)

首先让我们解释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元素的交叉点
  3. 第一个交叉点将告诉我们两个地图中是否有相同的键,而第二个交叉点将告诉我们它们是否存储相同的值。如果两个表达式都计算为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
    

    我希望它有所帮助。