RxJava: как конвертировать список объектов в список других объектов
У меня есть список объектов SourceObjects, и мне нужно преобразовать его в список объектов ResultObjects.
Я могу выбрать один объект для другого с помощью метода ResultObject:
convertFromSource(srcObj);
конечно, я могу сделать это вот так:
public void onNext(List<SourceObject> srcObjects) {
List<ResultsObject> resObjects = new ArrayList<>();
for (SourceObject srcObj : srcObjects) {
resObjects.add(new ResultsObject().convertFromSource(srcObj));
}
}
но я буду очень благодарен тому, кто может показать, как сделать то же самое с помощью rxJava.
Ответы
Ответ 1
Если ваш Observable
испускает List
, вы можете использовать следующие операторы:
-
flatMapIterable
(преобразуйте список в наблюдаемый элемент)
-
map
(преобразуйте свой элемент в другой элемент)
Операторы -
toList
(преобразование завершенного наблюдаемого в наблюдаемое, которое испускает список элементов из завершенного Observable)
Observable<SourceObjet> source = ...
source.flatMapIterable(list -> list)
.map(item -> new ResultsObject().convertFromSource(item))
.toList()
.subscribe(transformedList -> ...);
Ответ 2
Если вы хотите сохранить Lists
, испускаемый источником Observable
, но преобразовать содержимое, т.е. Observable<List<SourceObject>>
в Observable<List<ResultsObject>>
, вы можете сделать что-то вроде этого:
Observable<List<SourceObject>> source = ...
source.flatMap(list ->
Observable.fromIterable(list)
.map(item -> new ResultsObject().convertFromSource(item))
.toList()
.toObservable() // Required for RxJava 2.x
)
.subscribe(resultsList -> ...);
Это обеспечивает пару вещей:
- Сохраняется число
Lists
, излучаемое Observable
. т.е. если источник испускает 3 списка, на другом конце будет 3 преобразованных списка.
- Использование
Observable.fromIterable()
гарантирует, что внутренний Observable
завершается так, что toList()
можно использовать
Ответ 3
метод Observable.from() factory позволяет конвертировать коллекцию объектов в поток Observable. Когда у вас есть поток, вы можете использовать оператор map для преобразования каждого испускаемого элемента. Наконец, вам нужно будет подписаться на полученный Observable, чтобы использовать преобразованные элементы:
// Assuming List<SourceObject> srcObjects
Observable<ResultsObject> resultsObjectObservable = Observable.from(srcObjects).map(new Func1<SourceObject, ResultsObject>() {
@Override
public ResultsObject call(SourceObject srcObj) {
return new ResultsObject().convertFromSource(srcObj);
}
});
resultsObjectObservable.subscribe(new Action1<ResultsObject>() { // at this point is where the transformation will start
@Override
public void call(ResultsObject resultsObject) { // this method will be called after each item has been transformed
// use each transformed item
}
});
Сокращенная версия, если вы используете lambdas, будет выглядеть так:
Observable.from(srcObjects)
.map(srcObj -> new ResultsObject().convertFromSource(srcObj))
.subscribe(resultsObject -> ...);
Ответ 4
Не разрушайте цепочку, точно так же.
Observable.from(Arrays.asList(new String[] {"1", "2", "3", }))
.map(s -> Integer.valueOf(s))
.reduce(new ArrayList<Integer>, (list, s) -> {
list.add(s);
return list;
})
.subscribe(i -> {
// Do some thing with 'i', it a list of Integer.
});
Ответ 5
Если вам нужно просто List<A>
в List<B>
не манипулируя результатами List<B>
.
Самая чистая версия:
List<A> a = ... // ["1", "2", ..]
List<B> b = Observable.from(a)
.map(a -> new B(a))
.toList()
.toBlocking()
.single();
Ответ 6
В качестве дополнения к Ноэлю отличный ответ. Допустим, преобразование также зависит от некоторых данных сервера, которые могут измениться во время подписки. В этом случае используйте flatMap + scan.
В результате при изменении списка идентификаторов преобразования будут перезапущены заново. И когда данные сервера изменяются, связанные с определенным идентификатором, отдельный элемент также будет преобразован.
fun getFarmsWithGroves(): Observable<List<FarmWithGroves>> {
return subscribeForIds() //may change during subscription
.switchMap { idList: Set<String> ->
Observable.fromIterable(idList)
.flatMap { id: String -> transformId(id) } //may change during subscription
.scan(emptyList<FarmWithGroves>()) { collector: List<FarmWithGroves>, candidate: FarmWithGroves ->
updateList(collector, candidate)
}
}
}
Ответ 7
Вы можете использовать map оператор. Например, если у вас есть список s целых чисел, и вы хотите преобразовать в список s удвоений:
List<Integer> li = new ArrayList<>();
Observable.just(li).map( l -> {
List<Double> lr = new ArrayList<Double>();
for(Integer e:l) {
lr.add(e.doubleValue());
}
return lr;
});
Но все это намного естественнее, если вы можете контролировать наблюдаемое и изменять его, чтобы наблюдать отдельные элементы вместо коллекций. Тот же код, который преобразует одиночные целые элементы в двойные:
Observable.just(1,2,3).map( elem -> elem.doubleValue())