У меня есть три объекта (скажем, A, B, C), и для получения C мне нужно B, а для получения A мне нужно B. На экране мне нужно отобразить свойство A вместе со свойством C. Хотя я могу получить все необходимые данные, так как я использую flatMap, у которых нет onComplete, toList () не выполняется. Вот мой код.
Для каждого a в List мне нужно получить c, и мне нужно вернуть список типа ResultMode, который включает свойства a и c.
override fun methodICall(): LiveData<MutableList<ResultModel>> {
return mySdk
.getAllA() //Returns Flowable<List<A>>
.flatMap { Flowable.fromIterable(it) }
.flatMap { helperMethod(it) }
.toList() // Does not get executed as flatMap isnt completed
.toFlowable()
.onErrorReturn { Collections.emptyList() }
.subscribeOn(Schedulers.io())
.to { LiveDataReactiveStreams.fromPublisher(it) }
}
private fun helperMethod(a:A): Flowable<ResultModel> {
return mySdk
.getB(a.propertyOne!!) // Returns Single<B>
.flatMap { mySdk.getC(it.property!!) } // get C returns Single<C>
.map {
ResultModel(name= a.name,
date = c.date.toString(),
message = it.messageId!!
)
}.toFlowable()
}
Примечание: сегодня я задал аналогичный вопрос, но он не требовал использования плоской карты более одного раза. Вы можете просмотреть мое решение по этой ссылке
RxJava - Отображение результата списка в другой список
Мои усилия (что, вероятно, неправильно) Вот мои усилия по преобразованию первого метода (для второго метода я просто удаляю его в Flowable и возвращаю single), но ему предстоит долгий путь, и я думаю, что я на неправильном пути.
override fun methodICall(): LiveData<MutableList<ResultModel>> {
return mySdk
.getAllA()
.concatMapSingle { Flowable.fromIterable(it)
.map { helperMethod(it) }
.toList()
}
.onErrorReturn { Collections.emptyList() }
.subscribeOn(Schedulers.io())
.to { LiveDataReactiveStreams.fromPublisher(it) // here it is single. I think it is because two maps are applied both to helperMethod itself and inside helper method to result model}
}
Это бесконечно, поэтому я не могу использовать toList и решил использовать concatMapSingle. Это источник моей проблемы
Да, ты прав. Для бесконечных наблюдаемых toList() не работает, потому что его (операторная) логика основана на onComplete(). С Effort вы идете по правильному пути. Пожалуйста, обратите внимание, что если вы не хотите преобразовывать Observable в Single, вы можете вызвать toList().toObservable() - это позволит вам не только прослушивать первое излучение бесконечного источника, но и слушать до тех пор, пока оно не будет удалено.
Привет, Конст, я достиг своей цели, но мое решение действительно беспорядочное, и я не думаю, что это правильный способ решения, и может быть много ошибок. Выкладываю через 10 минут. Вы можете просмотреть это?
Попробую в течении часа
Я попытался сделать это еще одним методом, но после выполнения toList мой возвращаемый тип - LiveData <MutableList <Single <MyModel> >>. Я хочу избавиться от одиночного возвращаемого типа, могу ли я это сделать
Еще одна проблема, которая присутствует как в моем, так и в ответе Боба. Если bItem.propertyOfB имеет значение null, в результате onErrorReturn возвращается emptyList. Если есть 3 A и даже если одно из их свойств B имеет значение null, в настоящее время я получаю пустой список в качестве результата. В этом случае я хочу получить результат двух других в моем списке, как я могу это сделать
Вы можете использовать filter() раньше, чтобы пропустить все элементы с bItem.propertyOfB == null
Обновлено: я нашел другое решение
override fun methodICall(): LiveData<MutableList<ResultModel>> {
return mySdk
.getAllA()
.concatMapSingle {
Flowable.fromIterable(it)
.flatMap { a ->
mySdk.getB(a.propertyOfA!!)
.flatMap { b -> chatbotSdk.getC(b.propertyOfB!!) }
.map { it ->
ResultModel(name = a.name,
message = it.body!!)
}.toFlowable()
} .toList() }
.onErrorReturn { Collections.emptyList() }
.subscribeOn(Schedulers.io())
.to { LiveDataReactiveStreams.fromPublisher(it) }
}
Исходное решение
Это мое решение, но я думаю, что это решение действительно беспорядочное, и его можно значительно улучшить.
data class AtoBDTO(var name: String, var b: Flowable<B>) // I wanted to map one object to more than one so I created this. Probably there is a way to do it with rx functions.
data class BtoCDTO(var name: String, var c: Flowable<C>)
override fun methodICall(): LiveData<MutableList<ResultModel>> {
return mySdk
.getAllA() // Returns Flowable<List<A>>
.concatMapSingle {
Flowable.fromIterable(it)
.map { AtoBDTO(it.name!!,
mySdk.getB(it.propertyOfA!!).toFlowable()) } //getB returns Single B
.toList()
}
.concatMapSingle {
Flowable.fromIterable(it)
.map {
BtoCDTO(it.name,
it.b.concatMapSingle { mySdk.getC(it.propertyOfB!!) }) // getC returns Single C
}
.toList()
}
.concatMapSingle {
Flowable.fromIterable(it)
.map {
ResultModel(name = it.name,
message = it.c.blockingFirst().body!!) // I use blocking first because otherwise I can't get rid of flowable
}.toList()
}
.onErrorReturn { Collections.emptyList() }
.subscribeOn(Schedulers.io())
.to { LiveDataReactiveStreams.fromPublisher(it) }
}
Ответ от @Bob Dalgleish выглядит неплохо, и я полагаю, что это то, что вам нужно
Его ответ - getAllA (). FlatMapIterable {it} вместо getAllA (). FlatMap {Flowable.fromIterable (it)}. Это имеет какое-то значение? Этот код по-прежнему не выполняет toList. Поскольку getAllA бесконечно, использование toList после flatMapIterable {it} также должно завершиться ошибкой
Да, ты прав. Но вся остальная цепочка, как он пишет, достаточно хороша. Используйте getAllA().flatMap { Flowable.fromIterable(it).flatMap { mySdk.getB(item.propertyOfA!!) .flatMap( bItem => mySdk.getC( bItem.propertyOfB!! ) ) .map( ResultModel( name=item.name, message=it.body!! ) ) }.toList().toObservable() }
У меня не получилось заставить работать, может я что то не так делаю. Кстати, я решил проблему лучше (по сравнению со старым решением). Это нормальное решение?
Кажется, нет веской причины постоянно разбирать и восстанавливать списки. Предполагая, что нет:
override fun methodICall(): LiveData<MutableList<ResultModel>> {
return mySdk
.getAllA() // Returns Flowable<List<A>>
.flatMapIterable(it)
.concatMapSingle( item => {
mySdk.getB(item.propertyOfA!!)
.flatMap( bItem => mySdk.getC( bItem.propertyOfB!! ) )
.map( ResultModel( name=item.name, message=it.body!! ) )
})
.toList()
.onErrorReturn { Collections.emptyList() }
.subscribeOn(Schedulers.io())
.to { LiveDataReactiveStreams.fromPublisher(it) }
}
Поскольку оператор concatMapSingle() знает о каждом элементе, его имя может быть известно, когда придет время построить ResultModel. Теперь вам больше не нужно так часто разбирать вещи.
Привет, я редактирую ваш ответ из-за синтаксических ошибок и типа возвращаемого значения, но это не работает. Я не вижу разницы между использованием flatMap и использованием итеративного внутреннего действия и использованием flatMapIterable {it}. Не стесняйтесь предупредить меня, если вы считаете, что я неправильно отредактировал или почему этот метод должен работать
mySdk.getAllA()бесконечен? или он завершается сразу после публикации значения?