0

我有一个场景,我有一个函数 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,那么它应该返回一个 1 和 2 的 Observable。

4

1 回答 1

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()
}

注意: aCompletable是一种特殊类型的发布者,只能发出信号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它们自己。

于 2018-05-28T12:40:14.577 回答