Filter sublist using RxJava - android

Using the following objects :
Order {
int orderId;
List<Item> items;
}
Item {
int price;
String description;
boolean free;
}
The goal is to keep non-free items starting from an Observable<0rder> and still return an Observable<0rder>.
I'm doing the following for now, but my items doesn't get filtered :
getMyOrder() // returns Observable<Order> from the network
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.flatMap(
order -> Observable.from(order.items)
.filter(item -> !item.isFree())
.map(items -> order)
);

This doesn't feel like something that should be done using RxJava (especially if you want to emit Order after the filtering step), could be an instance method in Order, for example:
getMyOrder().subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.map(Order::removeFreeItems)
.subscribe();
class Order {
int orderId;
List<Item> items;
public Order removeFreeItems() {
for (Iterator<Item> iterator = items.iterator(); iterator.hasNext(); ) {
if (iterator.next().free) {
iterator.remove();
}
}
return this;
}
}

Related

Why list changes fires on whole list

I`m using mvp with repository to update items on recyclerview when item at firestore database are changing.
As asked, here is some more code from SharedModelClass:
public LiveData<List<Task>> tasksListening() {
return repository.tasksListening(false);
}
which lead to:
public LiveData<List<Task>> tasksListening(boolean b) {
return new FirestoreTasksData(tasksReference, b);
}
Here is FirestoreTasksData extends LiveData<List>:
public class FirestoreTasksData extends LiveData<List<Task>> {
List<Task> tasks;
for (QueryDocumentSnapshot doc : value) {
Task item = doc.toObject(Task.class);
tasks.add(item);
}
setValue(tasks);
};
}
All works perfect except that list is updated in whole even when updating one item.
sharedViewModel.tasksListening().observe(getViewLifecycleOwner(), tasks -> {
tasksAdapter.submitList(tasks);
});
and here is adapter code:
public class MyTasksAdapter extends RecyclerView.Adapter<MyTasksAdapter.TaskHolder> {
private final AsyncListDiffer<Task> mDiffer = new AsyncListDiffer<>(this, DIFF_CALLBACK);
private static final DiffUtil.ItemCallback<Task> DIFF_CALLBACK = new DiffUtil.ItemCallback<Task>() {
#Override
public boolean areItemsTheSame(#NonNull Task oldItem, #NonNull Task newItem) {
return oldItem.getId().equals(newItem.getId());
}
#Override
public boolean areContentsTheSame(#NonNull Task oldItem, #NonNull Task newItem) {
return oldItem.geteDate().equals(newItem.geteDate()) && (new HashSet<>(oldItem.getRoles().values()).equals(new HashSet<>(newItem.getRoles().values())));
}
};
}
public void submitList(List<Task> list) {
mDiffer.submitList(list);
}
#Override
public int getItemCount() {
return mDiffer.getCurrentList().size();
}
Is this a bug or a feature? I was using Firestore UI recycler adapter before, just decided to refactor code.
That is expected behavior. When there's any change to the results of a query/collection, your code gets called with a QuerySnapshot object of all changes that match the query/collection.
If you want to see what has changed, you can look at the getDocumentChanges() of the snapshot to see those. For more on this (and an example) see the documentation on viewing change between snapshots.
Rather than implement custom adapter and DiffUtil Callback.
Take a look at LiveAdapter.
You just need to add the latest dependency in Gradle.
dependencies {
implementation 'com.github.RaviKoradiya:LiveAdapter:1.3.2-1608532016'
// kapt 'com.android.databinding:compiler:GRADLE_PLUGIN_VERSION' // this line only for Kotlin projects
}
and bind adapter with your RecyclerView
LiveAdapter(
data = liveListOfItems,
lifecycleOwner = this#MainActivity,
variable = BR.item )
.map<Header, ItemHeaderBinding>(R.layout.item_header) {
areContentsTheSame { old: Header, new: Header ->
return#areContentsTheSame old.text == new.text
}
}
.map<Point, ItemPointBinding>(R.layout.item_point) {
areContentsTheSame { old: Point, new: Point ->
return#areContentsTheSame old.id == new.id
}
}
.into(recyclerview)
That's it. Not need to write extra code for adapter implementation, observe LiveData and notify the adapter.
Wanted to delete post but the rules.
I solved the problem. The problem is that all working good. Feel free to use code if needed.
I just realized that inside observer called another method, which is the reason I see rendering effect as if there were some bug with adapter.
sharedViewModel.tasksListening().observe(getViewLifecycleOwner(), tasks -> {
tasksAdapter.submitList(tasks);
checkEmpty(tasks.size());
});
private void checkEmpty(int size) {
if (size == 0) {
crossFade(noDataLayout, mTasksRecycler);
} else {
crossFade(mTasksRecycler, noDataLayout);
}
}
I fixed that and now all is fine.

Get Data from Two api using RxJava

I have two request of api
// to get all products list inside specific category
1. getCategoryProducts : Observable<List<CategoryProductsModel>
class CategoryProductsModel {
int categoryId ;
String sku ;
// sku is variable I used it to get the details for each product (name , images , colors , sizes , price ..etc
}
// to get product details by sku based on first api getCategoryProducts
2.getProductDetails(var sku:String): Observable<ProductDetailsModel>
class ProductDetailsModel {
float price ;
List<Images> images ,
List<Colors> colors ,
etc ...
}
first request is getCategoryProducts then getProductDetails based on the first request to get the sku for each product and pass it to getProductDetails
after I make the second request I want to add the data response in ProductModel to get finally a list of ProductModel to pass it to recycler view
ProductModel
{
CategoryProductsModel mCategoryProductsModel ;
ProductDetailsModel mProductDetailsModel ;
}
I user RxJava to request API. How can I do that?
public void getProductsList() {
getCategoryProductsObservable()
.subscribeOn(Schedulers.io())
.flatMap(new Function<CategoryProductsModel, ObservableSource<List<ProductModel>>>() {
#Override
public ObservableSource<List<ProductModel>> apply(CategoryProductsModel mCategoryProductsModel) throws Exception {
return getProductDetails(mCategoryProductsModel);
}
}).observeOn(AndroidSchedulers.mainThread())
.subscribe(this::fillProducts);
}
public Observable<CategoryProductsModel> getCategoryProductsObservable() {
return Repository.Companion.getStoreInstance().getCategoryProducts(categoryId).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Function<List<CategoryProductsModel>, ObservableSource<CategoryProductsModel>>() {
#Override
public ObservableSource<CategoryProductsModel> apply(List<CategoryProductsModel> mCategoryProductsList) throws Exception {
return Observable.fromIterable(mCategoryProductsList).subscribeOn(Schedulers.io());
}
}).subscribeOn(Schedulers.io());
}
#SuppressLint("CheckResult")
public Observable<List<ProductModel>> getProductDetails(CategoryProductsModel mCategoryProducts) {
List<ProductModel> mList = new ArrayList<>() ;
return Repository.Companion.getStoreInstance().getProductDetails(mCategoryProducts.getSku())
.map(new Function<ProductDetailsModel, List<ProductModel>>() {
#Override
public List<ProductModel> apply(ProductDetailsModel mProductDetailsModel) throws Exception {
Log.d("mProductDetailsModel", mProductDetailsModel.getName());
ProductModel productModel = new ProductModel() ;
productModel.setProductDetailsModel(mProductDetailsModel);
productModel.setCategoryProductsModel(mCategoryProducts);
mList.add(productModel);
return mList;
}
}).subscribeOn(Schedulers.io());
list of operators you need: switchMap() fromIterable() and toList(). So your code will look like:
getCategoryProductsObservable()
.subscribeOn(Schedulers.io())
.switchMap { listOfProducts -> Observable.fromIterable(listOfProducts)
.map { it.sku }
.flatMap { sku -> getProductDetails(sku) }
.toList()
.map { listOfProductDetails -> /* combine with listOfProducts to Model you need */ }
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::fillProducts);
I used lambdas for shorter answer, can rewrite to Function style, but i guess it's self explanatory.

Best way to get List from Observable in Rxjava

I'm just exploring Rxjava in one of my android application, and got stuck at one place, honestly speaking I'm very new to this library so don't mind if my question frustrate someone;-)
So I'm trying to access the Room Database using RxJava where I'm returning the Observable List, once I get this Observable I'm trying to use map operator to get a list of ids & query again the database, which again returns me the Observable List but the map operator expects List as a return type. How can I tackle this please suggest?
Below is the code snippet:
private void getAllPcbs() {
isLoading.setValue(true);
getCompositeDisposable().add(
getRepositoryManager().loadAllPcbDetails()
.flatMap((Function<List<PcbDetails>, ObservableSource<?>>) pcbDetails -> {
List<Long> pcbList = new ArrayList<>();
for (PcbDetails details : pcbDetails)
pcbList.add(details.getPcbId());
return getRepositoryManager().loadAllPcbs(pcbList);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSuccess, this::onError)
);
}
private void onError(Throwable throwable) {
isLoading.setValue(false);
}
private void onSuccess(Object o) {
isLoading.setValue(false);
pcbList.setValue((List<Pcb>) o);
}
public interface DbHelper {
Observable<List<PcbDetails>> loadAllPcbDetails();
Observable<List<Pcb>> loadAllPcbs(List<Long> pcbIdList);
}
Go like
getRepositoryManager().loadAllPcbDetails()
.flatMapIterable {
listPcbDetail-> listPcbDetail
// listPcbDetail is ArrayList<PcbDetails>
// Converts your list of ids into an Observable
// which emits every item in the list
}
.flatMap { pcbDetail ->
// pcbDetail is PcbDetails
getRepositoryManager().loadAllPcbs(pcbDetail.pcbIdList)
}.subscribe { listPcb ->
// listPcb is ArrayList<Pcb>
}

How I can use socket.io and rxjava 2 android

I develop an app with mpv (mosby3) + socket.io.
I want to use rxjava 2 to relate provider and repository.
I have CategoryManager
public class CategoryManager {
private List<Category> list = null;
...
}
If list not null i can do it
public Single<List<Category>> getList() {
return Single.just(this.list);
}
But if I need load the list I have do it async like a it
socket.on("category", (data) -> {
Type founderListType = new TypeToken<ArrayList<Category>>() {}.getType();
list = gson.fromJson(data[0].toString(), founderListType);
// here i need to generate event to single subscriber
})
I think i should use to
public Single<List<Category>> getList(int count) {
return Single.create(s -> {
if (list == null) {
socket.emit("category");
// i need async load list
} else {
s.onSuccess(list.subList(0, count));
}
});
}
And CategoryPresenter should have code like
disposable.add(session.getCategoryManager()
.getList(5)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
if (isViewAttached()) {
getView().setData(data);
}
}, throwable -> {
throwable.printStackTrace();
})
);
}
I think i can keep subscribers in class property
private List<SingleEmmiter> subscribers;
and remove subscribers in setCancellable method, but i don't think it good idea.
Help me please :)

How to create an Rx Search view that will filter a recycler view with a query

I want to implement a Rx Search view that will filter a recycler view with the string and also check the local Realm db and make a retrofit request, then combines all the results without duplicates ?? So, in other words: I would like to on text change event in a search view, to use the input string to make a network request, a db query and combine with results without duplicates
This is my initial code:
RxSearchView.queryTextChanges(searchView)
.filter(charSequence -> !TextUtils.isEmpty(charSequence))
.throttleLast(100, TimeUnit.MILLISECONDS)
.debounce(200, TimeUnit.MILLISECONDS)
.onBackpressureLatest()
.flatMap(charSequence -> userListPresenter.search(charSequence))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.onErrorResumeNext(Observable.empty())
.subscribe((Action1) response -> userListPresenter.showSearchResult((List<UserModel>) response));
The userListPresenter.search(charSequence)) should return an observable of the concatenated response without duplicates, thanks :)
userListPresenter.search(charSequence)):
Observable.create(subscriber -> {
if (Utils.isNetworkAvailable(getContext())) {
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(restApi.search(query));
subscriber.onCompleted();
}
}).mergeWith(
Observable.create(subscriber -> {
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(realmManager.getWhere(query));
subscriber.onCompleted();
}
})).collect(HashSet::new, HashSet::add)
.flatMap(Observable::from);
if I correctly understood your question. There is good way to combine the emissions of multiple Observables, look at zip method.
So, Your search method would be look like that:
public Observable<List<MyItem>> search(CharSequence query) {
return Observable.zip(
observeNetwork(query),
observeRealm(query),
new Func2<List<MyItem>, List<MyItem>, List<MyItem>>() {
#Override
public List<MyItem> call(List<MyItem> networkResult, List<MyItem> databaseResult) {
return merge(networkResult,databaseResult);
}
}
);
}
private Observable<List<MyItem>> observeRealm(CharSequence searchString) {
return Observable.create( /* do your realm stuff */);
}
private Observable<List<MyItem>> observeNetwork(CharSequence searchString) {
return return Observable.create( /* do your network stuff */);
}
private List<MyItem> merge(List<MyItem> networkResult, List<MyItem> databaseResult) {
List<MyItem> result = new ArrayList<>();
result.addAll(databaseResult);
for(MyItem newItem : networkResult){
if(!databaseResult.contains(newItem)){
result.add(newItem);
}
}
return result;
}

Categories

Resources