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

时间:2018-07-18 16:05:14

标签: scala collections functional-programming

两个对象扩展了这样的基础对象:

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函数进行类型检查?

3 个答案:

答案 0 :(得分:3)

好吧,为什么不将groupBy与类似的类一起使用:

c.groupBy(_.getClass).values

这将为您提供List中的Lists,其中包含已分组的实例。获取元组更为复杂,因为您需要事先了解类型。有一些使用Shapeless(Convert a Scala list to a tuple?)的解决方案,但是不确定原始的解决方案似乎在没有实际元组的情况下确实可以工作。

答案 1 :(得分:1)

考虑使用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))

答案 2 :(得分:1)

我将使用collect:

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