groovy - 使用转置以完全外连接样式合并列表

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

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

最佳答案

首先我们来解释一下 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 个元素。在调查所需结果后可以发现一个隐藏的要求:如果有一对两个映射至少具有一个公共(public)键,但每个对元素有两个不同的值,则不要合并这两对,而是相反,创建两个 map ,其中第一个 map 是来自 x 的 map 和 不变,第二个是合并 x 中的 map 的结果和y .

如何检查两个映射是否至少有一个具有不同值的公共(public)键?

我们可以使用 Collection.intersect(Collection right) 为了那个原因。但我们必须比较两个交集:

  1. Map.keySet() 的交集键
  2. Map.Entry 的交集两张 map 中的元素

第一个交集将告诉我们两个映射中是否有相同的键,而第二个交集将告诉我们它们是否存储相同的值。如果两个表达式的计算结果均为 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

希望对您有所帮助。

关于groovy - 使用转置以完全外连接样式合并列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45905405/

相关文章:

java - 客户端Socket作为监听者?

Java 脚本 API (JSR223) Groovy 语言限制

java - Groovy Classpath 上的 Jenkins 包?

java - Spring Security ACL继承: How does it really works?

groovy - "find: missing argument to ` -exec '"在 Java 进程构建器中运行

groovy - Kotlin 替换 groovy XmlSlurper 和 MarkupBuilder

groovy - ical4j 2.2.0 使用 Grape,抛出 java.lang.NoClassDefFoundError : javax/cache/configuration/Configuration when loading a calendar

groovy - 所有 Groovy 代码都对 Groovy++ 有效吗?

groovy - 在 Sublime Text 3 中运行 Groovy

list - groovy 中的 [c 类