Akka Streams:如何计算不同来源中的不同值

时间:2018-03-24 11:23:25

标签: scala akka-stream

我有N(每次实现可能会有所不同)有限排序的数字来源。我需要将结果作为这些数字的流,并计算它们出现的次数。

例如:

1,3,5,7 -> |   |
1,5,7   -> | ? | -> (1,2),(2,1),(3,1),(4,1),(5,3),(7,2)
2,4,5   -> |   |

如何实施?

2 个答案:

答案 0 :(得分:1)

另一个答案将所有来源连接成一个流......

1,3,5,7,1,5,7,2,4,5

...并通过折叠积累元素的地图及其各自的计数。必须使用整个流来计算计数:换句话说,在不消耗整个流的情况下,无法知道任何整数的出现次数。

另一种方法是使用Source#mergeSorted将预先排序的流合并为一个已排序的流...

1,1,2,3,4,5,5,5,7,7

...然后使用statefulMapConcat生成计数:

val src1 = Source(List(1, 3, 5, 7))
val src2 = Source(List(1, 5, 7))
val src3 = Source(List(2, 4, 5))

def mergeSortedSources(sources: List[Source[Int, NotUsed]]): Source[Int, NotUsed] =
  sources.foldLeft(Source.empty[Int])(_ mergeSorted _)
         .concat(Source.single(0)) // this ending element is needed to print the last pair

mergeSortedSources(List(src1, src2, src3))
  .statefulMapConcat { () =>
    var prev: Option[Int] = None
    var count = 0

    x =>
      prev match {
        case None | Some(`x`) =>
          count = count + 1
          prev = Some(x)
          Nil
        case Some(oldElem) =>
          val oldCount = count
          count = 1
          prev = Some(x)
          (oldElem -> oldCount) :: Nil
      }
  }.runForeach(println)

运行上面的代码会打印以下内容:

(1,2)
(2,1)
(3,1)
(4,1)
(5,3)
(7,2)

由于合并的流已排序,因此在处理流时会按连续顺序计算计数。也就是说,1的出现次数是在确定2的出现次数之前确定的,依此类推。

答案 1 :(得分:0)

基本上,您将多个源合并为一个,然后汇总数据。 这是一个帮助对象和方法,我用它来组合几个来源:

object ConcatSources {
  def apply[T](sources: Seq[Source[T, NotUsed]]): Source[T, NotUsed] = {

    sources match {
      case first :: second :: rest =>
        Source.combine(first, second, rest: _*)(Concat(_))
      case first :: _ =>
        first
      case Nil =>
        Source.empty
    }
  }
}

然后解决你的任务:

  val sources: Seq[Source[Int, NotUsed]] = Seq(
    Source[Int](List(1, 3, 5, 7)),
    Source[Int](List(1, 5, 7)),
    Source[Int](List(2, 4, 5))
    )
  ConcatSources(sources).fold(Map[Int, Int]()) { (map, i) =>
    map.updated(i, map.getOrElse(i, 0) + 1)
  }.runForeach(map => println(map.toList))