scala observable使用没有中间数据结构更新的序列统一可观察

时间:2017-11-14 12:48:33

标签: scala rx-scala couchbase-java-api

我有一个代码调用couchbase获取如下行:

val gotValues: Observable[JsonDocument] = Observable.from(rowKeys).flatMap(id =>
      couchbaseBucket.async().get(id))

如果我有1,2,3,4,5,6作为输入行键,并且DB中只存在1,2,3行,那么observable只会得到1,2,3的通知。

我的要求是,我返回一个1,2,3为真(存在于db中)和4,5,6为false的映射(意味着DB中不存在)。我设法用scala observable做到了,但是我使用中间地图数据结构来返回包含所有id的总地图。下面是一个模拟我的问题的示例代码..

object Main extends App {
  import rx.lang.scala.Observable

  val idsToFetch = Seq(1,2,3,4,5,6)

  println(isInDBOrNot()) // {1=true, 2=true, 3=true, 4=false, 5=false, 6=false}

  private def isInDBOrNot(): ConcurrentHashMap[Int, Boolean] = {
    val inAndNotInDB = new java.util.concurrent.ConcurrentHashMap[Int, Boolean]
    // - How can I avoid the additional data structure?
    // - In this case a map, so that the function will return
    //   a map with all numbers and for each if exist in DB?
    // - I mean I want the function to return a map I don't 
    //   want to populate that map inside the observer,
    //   it's like a mini side effect I would rather simply 
    //   manipulate the stream.

    Observable.from(idsToFetch)
      .filterNot(x => x == 4 || x == 5 || x == 6) // Simulate fetch from DB, 4,5,6 do not exist in DB, so not returned.
      .subscribe(
      x => inAndNotInDB.put(x, true),
      e => println(e),
      () => idsToFetch.filterNot(inAndNotInDB.containsKey)
        .foreach(inAndNotInDB.put(_, false)) // mark all non-found as false.
    )

    inAndNotInDB
  }

}

无论如何在没有中间映射的情况下这样做(没有填充中间数据结构,只能通过操纵流)? 看起来不干净!! 。感谢。

3 个答案:

答案 0 :(得分:4)

您的问题似乎来自于您使用flatMap这一事实,因此如果数据库中没有针对给定id的数据而您获得空Observable,{{1只为这样的flatMap生成无输出。所以看起来你需要的是defaultIfEmpty,它被翻译成Scala的orElse。您可以使用idorElse中返回一些默认值。所以要修改你的例子:

flatMap

打印

  

List((1,true),(2,true),(3,true),(4,false),(5,false),(6,false))

或者您可以使用def fetchFromDb(id: Int): Observable[String] = { if (id <= 3) Observable.just(s"Document #$id") else Observable.empty } def gotValue(idsToFetch: Seq[Int]): Observable[(Int, Boolean)] = { Observable.from(idsToFetch).flatMap((id: Int) => fetchFromDb(id).map(_ => (id, true)).orElse((id, false))) } println(gotValue(Seq(1, 2, 3, 4, 5, 6)).toBlocking.toList) 返回OptionSome(JsonDocument),例如

None

打印

  

列表((1,Some(Document#1)),(2,Some(Document#2)),(3,Some(Document#3)),(4,None),(5,None), (6,无))

答案 1 :(得分:1)

执行此操作的一种方法如下:

(1)使用

将ID序列转换为resourcemanagerObservable
map

...这样您就可以获得id => (id, false) 类型的可观察对象(让我们调用这个新的可观察的Observable[(Int, Boolean)])。

(2)从数据库中获取数据,并将每个获取的行first从以下位置获取:

map

...在(some_id, true) 内(让我们调用这个可观察的Observable[(Int, Boolean)]

(3) concat lastfirst

(4) toMap(3)的结果。来自last的重复元素将被删除。 (这将是您的first

(5)(可能)收集observable的第一个也是唯一一个元素(你的地图)。您可能根本不想这样做,但如果这样做,您应该真正理解阻塞在此时收集结果的含义。在任何情况下,此步骤实际上取决于您的应用程序细节(如何组织线程\调度\ io),但是暴力方法看起来应该是这样的(有关更具体的信息,请参阅this demo):

resultObsrvable

答案 2 :(得分:1)

这个怎么样

DatePickerTextBox