I have written the following code for finding pageCount of comics that lie within certain budget.
At first I tried to come up with code that would have architecture like this:
- Stream gives price of a MarvelComic object.
- I sum the price of the MarvelComic object from stream with prices of the previous comics that came down this stream and check if it's < BUDGET
- If Yes, then I sum the pageCount of the MarvelComic object with the pageCount sum of previous MarvelComic objects that came down the stream.
- If yes, then onNext of the subscriber is called.
Since I couldn't devise a way to write code like I mentioned in the steps above, I resorted to mashing imperative programming with Reactive Programming. As a result I wrote the following code:
Observable.fromIterable(getMarvelComicsList()).
map(new Function<MarvelComic, HashMap<String, Double>>() {
@Override
public HashMap<String, Double> apply(@NonNull MarvelComic marvelComic) throws Exception {
HashMap<String, Double> map = new HashMap<String, Double>();
map.put("price", Double.valueOf(marvelComic.getPrice()));
map.put("pageCount", Double.valueOf(marvelComic.getPageCount()));
map.put("comicCount", Double.valueOf(marvelComic.getPageCount()));
return map;
}
})
.scan(new HashMap<String, Double>(), new BiFunction<HashMap<String, Double>, HashMap<String, Double>, HashMap<String, Double>>() {
@Override
public HashMap<String, Double> apply(@NonNull HashMap<String, Double> inputMap, @NonNull HashMap<String, Double> newValueMap) throws Exception {
double sum = inputMap.get("price")+newValueMap.get("price");
double count = inputMap.get("pageCount")+newValueMap.get("pageCount");
double comicCount = inputMap.get("comicCount")+newValueMap.get("comicCount");
HashMap<String, Double> map = new HashMap<String, Double>();
map.put("price", sum);
map.put("pageCount", count);
map.put("comicCount", comicCount);
return map;
}
})
.takeWhile(new Predicate<HashMap<String, Double>>() {
@Override
public boolean test(@NonNull HashMap<String, Double> stringDoubleHashMap) throws Exception {
return stringDoubleHashMap.get("price") < budget;
}
})
.subscribe(new DisposableObserver<HashMap<String, Double>>() {
@Override
public void onNext(HashMap<String, Double> stringDoubleHashMap) {
double sum = stringDoubleHashMap.get("price");
double pageCount = stringDoubleHashMap.get("pageCount");
double comicCount = stringDoubleHashMap.get("comicCount");
Timber.e("sum %s pageCount %s ComicCount: %s", sum, pageCount, comicCount);
}
@Override
public void onError(Throwable e) {
Timber.e("onError %s", e.fillInStackTrace());
}
@Override
public void onComplete() {
Timber.e("onComplete");
}
});
My Reservations:
- Is it good idea to create a new Hashmap every time inside
map(), scan()
? - How can I improve this code further?
Issues:
This code gives NullPointerException in onError
because map.get("price")
returns null in scan()
. I'm not really sure of the reason.
Error:
onError java.lang.NullPointerException: Attempt to invoke virtual method 'double java.lang.Double.doubleValue()' on a null object reference
NOTE:
HashMap isn't null, the double field is being returned as NULL for some reason. I'm trying to figure out how.