How to merge 2 Enumerators in one, based on merge rule

旧时模样 提交于 2019-12-10 21:57:24

问题


We have a small Scala project on Playframework. I'm trying to do everything reactive, and stumbled on a problem.

I have two Enumerator[A] instances, representing values from a DB ordered by date. I need to return them as a single Enumerator[A] keeping date ordering. I have not found any solution for that in Enumerator[A], so I'm accumulating A's in single collection, and ordering them afterwards.

case class A(
   created: Date,
   data: String
)

val as: Enumerator[A] = findByAOrderedByCreated()
val bs: Enumerator[A] = findByBOrderedByCreated()

What is reactive way to deal with this?


回答1:


Here is a solution that will work for any number of Enumerator values using any Ordering on the elements:

import play.api.libs.iteratee._
import scala.concurrent._

object MergeEnums {
  def apply[E: Ordering](enums: Enumerator[E]*)(implicit executor: ExecutionContext) = new Enumerator[E] {
    def apply[A](iter: Iteratee[E, A]) = {
      case class IterateeReturn(o: Option[(Promise[Promise[IterateeReturn]], E)])

      val failP = Promise()
      val failPF = failP.future
      val initState = Future.traverse(enums) { enum =>
        val p = Promise[IterateeReturn]()
        enum.run(Iteratee.foldM(p) { (oldP: Promise[IterateeReturn], elem: E) =>
          val p = Promise[Promise[IterateeReturn]]()
          oldP success IterateeReturn(Some(p, elem))
          p.future
        } map { promise =>
          promise success IterateeReturn(None)
        }) onFailure { case t => failP failure t }
        p.future
      } map (_.map(_.o).flatten.toList)

      Enumerator.unfoldM(initState) { fstate =>
        Future.firstCompletedOf(Seq(fstate, failPF)) map { state =>
          state.sortBy(_._2) match {
            case Nil => None
            case (oldP, elem) :: tail =>
              val p = Promise[IterateeReturn]()
              oldP success p
              val newState = p.future.map(_.o.map(_ :: tail).getOrElse(tail))
              Some(newState, elem)
          }
        }
      } apply iter
    }
  }
}

It creates an Iteratee to apply to each Enumerator that is passed in, and an Enumerator to give the sorted elements. The Iteratee instances and the Enumerator communicate by sending Promise instances to each other (hence the Promise[Promise[IterateeReturn]] and such).



来源:https://stackoverflow.com/questions/23110082/how-to-merge-2-enumerators-in-one-based-on-merge-rule

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!