Проверка списка и тайм-аут, если все записи не найдены в RxJava / RxKotlin - PullRequest
0 голосов
/ 25 мая 2018

У меня есть сценарий, где у меня есть функция, scanForTargets, которая возвращает Observable типа FoundNumber.В FoundNumber мне просто нужно поле ID, которое я могу извлечь из него.Поскольку каждый элемент возвращается в ScanResults Observable, я хочу проверить, соответствует ли поле имени одному из имен в списке целей.Если так, то я хочу это испустить.Например, если я ищу цифры 1 и 2, а scanForTargets () отправляет обратно 1, 2, 3 и 4, то я хочу, чтобы scanForValues ​​отправляла обратно только 1 и 2.

Предупреждениечто я только хочу продолжать делать это до тех пор, пока: 1) не истечет период времени (в этом случае я выбрасываю и выдает ошибку) 2) все элементы в списке строк найдены до истечения времени ожидания.

Что у меня такFar выглядит примерно так, но я не могу заставить его работать на меня, в основном из-за ярлыка остановки один раз / если все цели найдены до истечения времени ожидания.

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?)

Теперь, если scanForValues ​​был вызван со списком 1 и 2, он должен выдать Observable 1, а затем 2.

1 Ответ

0 голосов
/ 28 мая 2018

Нет, это не так просто, как добавить еще один 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 - это специальный тип издателя, который может сигнализировать только onComplete или onError.


Обновление : ответ на обновление вопроса.

Новый пример в вашем вопросе не будет работать, поскольку значения 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.

...