如何将混合类型的列表转换为相同类型的列表元组



两个对象扩展一个基本对象,如下所示:

trait Component
class ComponentA extends Component
class ComponentB extends Component

假设我实例化了一些对象:

val a = new ComponentA
val a2 = new ComponentA
val b = new ComponentB

我得到了一个类型为Component的列表,如下所示:

val components = List[Components](a, a2, b)

我想做的是有一个函数,它接受一个混合类型的列表,并将其转换为只包含一个类型的n元组列表。例如,components会像这样分解:

transform.apply(components) = (List(a, a2), List(b))

做到这一点的非功能、怪诞、弗兰肯斯坦式的方法是:

def apply(c: List[Component]) = {
var aComponents = new ListBuffer[ComponentA]()
var bComponents = new ListBuffer[ComponentB]()
for (c <- components) {
c match {
case aType: ComponentA => aComponents += aType
case bType: ComponentB => bComponents += bType
case _ => None
}
}
aComponents -> bComponents
}

显然,这种方法有很多缺点:

  1. 在我的例子中,我有两种组件类型。但这种情况并不能推广到产生n分量的n元组
  2. 它不起作用。(使用可变的ListBuffer,不利用Collections库。(
  3. 太难看了。就像真的很丑陋。我再怎么强调也不为过

有没有一种方法可以在Scala中使用FP来实现这一点?我最初的想法是使用某种带有内部事例/匹配的groupBy函数进行类型检查?

好吧,为什么不把groupBy和你的类一起使用呢

c.groupBy(_.getClass).values

这已经为您提供了ListsList,其中包含分组实例。获取元组更为复杂,因为您需要事先了解类型。有一些解决方案使用Shapeless(将Scala列表转换为元组?(,但不确定你真的必须走那么远,因为你的原始解决方案似乎在没有实际元组的情况下也能工作。

我会使用collect:

val aList = c.collect { case a: ComponentA => a }
val bList = c.collect { case b: ComponentB => b }
val res = (aList, bList)

考虑使用foldRight遍历components列表以组装(List[ComponentA], List[ComponentB])元组:

components.foldRight( (List[ComponentA](), List[ComponentB]()) )(
(x, acc) => x match {
case x: ComponentA => (x :: acc._1, acc._2)
case x: ComponentB => (acc._1, x :: acc._2)
case _ => acc
}
)
// res1: (List[ComponentA], List[ComponentB]) =
//   (List(ComponentA@24a298a6, ComponentA@74fe5966), List(ComponentB@2bfbffb2))

最新更新