如何欺骗Scala映射方法,使每个输入项产生多个输出



相当复杂的算法正在应用于Spark数据集的行列表(该列表是使用groupByKey和flatMapGroups获得的(。大多数行从输入到输出是1:1转换的,但在某些情况下,每个输入需要一个以上的输出。输入行模式可以随时更改。map()非常适合1:1转换的要求,但有没有方法使用它来产生1:n输出?

我发现的唯一解决方案依赖于foreach方法,该方法通过创建初始空列表来产生令人不快的溢出原因(记住,与下面的简化示例不同,现实生活中的列表结构是随机变化的(。

我最初的问题太复杂了,不能在这里分享,但这个例子演示了这个概念。让我们有一个整数列表。每个都应该转换为其平方值,如果输入是偶数,也应该转换为原始值的一半:

val X = Seq(1, 2, 3, 4, 5)
val y = X.map(x => x * x) //map is intended for 1:1 transformation so it works great here
val z = X.map(x => for(n <- 1 to 5) (n, x * x)) //this attempt FAILS - generates list of five rows with emtpy tuples
// this work-around works, but newX definition is problematic
var newX = List[Int]() //in reality defining as head of the input list and dropping result's tail at the end
val za = X.foreach(x => {
newX = x*x :: newX
if(x % 2 == 0) newX = (x / 2) :: newX
})
newX

有比foreach结构更好的方法吗?

.flatMap从单个输入中产生任意数量的输出。

val X = Seq(1, 2, 3, 4, 5)
X.flatMap { x => 
if (x % 2  == 0) Seq(x*x, x / 2) else Seq(x / 2) 
}
#=> Seq[Int] = List(0, 4, 1, 1, 16, 2, 2)

更详细的flatMap

X.map(f)中,f是将每个输入映射到单个输出的函数。相反,在X.flatMap(g)中,函数g将每个输入映射到输出的序列flatMap然后取所有产生的序列(f中的每个元素一个(并将它们连接起来。

简洁的是.flatMap不仅适用于序列,而且适用于所有类似序列的对象。例如,对于一个选项,Option(x)#flatMap(g)将允许g返回一个Option。类似地,Future(x)#flatMap(g)将允许g返回Future。

无论何时返回的元素数量取决于输入,都应该想到flatMap

最新更新