如果在RxJava / RxKotlin中找不到所有条目,则检查列表并超时

时间:2018-05-25 20:46:04

标签: rx-java2 rx-kotlin

我有一个场景,我有一个函数scanForTargets,它返回一个类型为FoundNumber的Observable。在FoundNumber中,我只需要一个我可以从中获取的ID字段。当每个元素返回scanResults Observable时,我想检查名称字段是否与目标列表中的某个名称匹配。如果是这样,那么我想要发出它。例如,如果我正在寻找数字1和2,并且scanForTargets()向后发出1,2,3和4,那么我希望scanForValues只发回1和2。

需要注意的是,我只想继续这样做,直到:  1)一段时间过去了(在这种情况下我扔了错误)  2)在超时之前找到字符串列表中的所有项目。

到目前为止我看到的是这样,但是我无法让它为我工作主要是因为在超时之前找到所有目标的快捷方式是停止一次/如果找到所有目标。

fun scanForValues(targetList: List<String>): Observable<FoundNumber> {
    val scanResult = scanForTargets()

    return scanResult.doOnNext {scanResult -> Log.d(TAG, "Found potential target: " + scanResult.name) }
            .filter(TargetPredicate(targetList)) //See if it's one of those we want
            .timeout(5, TimeUnit.SECONDS) //Wait a max of 5 seconds to find all items
            .doOnError { Log.w(TAG, "Failed to scan"}") }
            .map{s->scanResult.name}  
}

class TargetPredicate(private val targetList: List<String>) : Predicate<ScanResult> { override fun test(scanResult: ScanResult): Boolean {
        if(scanResult == null) {
            return false
        }
        return scanResult.name in targetList 
    }
}

如果找到列表中的所有项目,如何添加检查以停止?我不能只是添加另一个谓词吗?

感谢。

更新:根据要求,这里有一些数据可以显示我的意思。

假设scanForTargets()和支持代码如下所示:

var emittedList: List<String?> = listOf(null, "0", "1", "2", "3")


fun scanForTargets(): Observable<FoundNumber> = Observable
    .intervalRange(0, emittedList.size.toLong(), 0, 1, TimeUnit.SECONDS)
    .map { index -> FoundNumber(emittedList[index.toInt()]) }

data class FoundNumber(val targetId: String?)

现在如果使用列表1和2调用scanForValues,那么它应该发回一个Observable为1然后是2。

1 个答案:

答案 0 :(得分:1)

不,它不像添加另一个filter那么简单。

一种可能的解决方案是使用scan从包含目标的集合中删除项目,并在集合变空时完成。

示例:

val targets = listOf("a", "b", "c")

fun scanForTarget(): Observable<String> = Observable.just("a", "b")

fun scanForValues(targets: List<String>): Completable {
    val initial = targets.toMutableSet()
    return scanForTarget()
            .timeout(5, TimeUnit.SECONDS)
            .scan(initial) { acc, next -> acc.remove(next); acc }
            .filter { it.isEmpty() }
            .singleOrError()
            .toCompletable()
}

注意:Completable是一种特殊类型的发布商,只能发出onCompleteonError信号。

更新:对问题更新的回复。

您问题中的新示例无法使用,因为null中不允许使用RxJava2值。

假设您已修复此问题,以下解决方案可能对您有所帮助。

fun scanForValues(targets: List<String>): Observable<String> {
    val accumulator: Pair<Set<String>, String?> = targets.toSet() to null
    return scanForTarget()
            .timeout(5, TimeUnit.SECONDS)
            .scan(accumulator) { acc, next -> 
                val (set, previous) = acc
                val item = if (next in set) next else null
                (set - next) to item     // return set and nullable item
            }
            .filter { it.second != null } // item not null
            .take(initial.size)           // limit to the number of items
            .map { it.second }            // unwrap the item from the pair
            .map { FoundNumber(it) }      // wrap in your class
}

现在我们还添加了项目,而不是仅使用Set<String>作为累加器。

该项目可以为空,这样我们就可以检查某个项目是否存在。

请注意,没有null值通过可观察流传递。在这种情况下,null值会包含在Pair<Set<String>, String?>内,而这些值永远不会null