将List [A]转换为List [B]并进行展平的流程,给定另一个将A转换为List [B]的Flow

时间:2016-12-03 08:49:20

标签: scala akka akka-stream

我有一个"内部"将A转换为List[B]的流程。我想通过调用" inner"来创建一个将List[A]转换为List[B]的Flow。在每个List[A]的每个元素上流动,然后展平结果。

为了便于说明,请参阅下面的测试用例(依赖项是最新的scalatestakka-streamA此处为StringB此处为Char,"内部"流程为stringToCharacters)。

测试通过,但实现不是惯用的Akka Streams代码,因为它实现/运行子流。

请建议一个更好的,惯用的实现,不涉及运行子流。

import akka.NotUsed
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Flow, Sink, Source}
import org.scalatest.FlatSpec
import org.scalatest.Matchers._
import org.scalatest.concurrent.ScalaFutures.whenReady
import scala.concurrent.ExecutionContext.Implicits.global

class TestSpec extends FlatSpec {
  implicit val system = ActorSystem()
  implicit val materializer = ActorMaterializer()

  /* This is just for illustration and testing purposes, `flattenFlows` should support various inner flows */
  val stringToCharacters: Flow[String, List[Char], NotUsed] =
    Flow[String].map(x => x.toList)

  /* FIXME: I'm looking for a better implementation of the following function, please don't change the signature */
  def flattenFlows[A, B](innerFlow: Flow[A, List[B], NotUsed]): Flow[List[A], List[B], NotUsed] = {
    Flow[List[A]]
      .mapAsync(Int.MaxValue) { x =>
        Source(x).via(innerFlow).runWith(Sink.seq).map(_.flatten.toList)
      }
  }

  it should "flatten flows" in {
    val input = List(
      List("ab", "cd"),
      List("ef", "gh")
    )

    val result = Source(input).via(flattenFlows(stringToCharacters)).runWith(Sink.seq)

    val expected = List(
      List('a', 'b', 'c', 'd'),
      List('e', 'f', 'g', 'h')
    )

    whenReady(result) { x =>
      x.toList shouldEqual expected
    }
  }
}

1 个答案:

答案 0 :(得分:2)

您可以使用flatMapConcatfold

的组合
  def flattenFlows[A, B](innerFlow: Flow[A, List[B], NotUsed]): Flow[List[A], List[B], NotUsed] =
    Flow[List[A]].flatMapConcat{ x => Source(x).via(innerFlow).fold(List.empty[B])(_ ::: _)}