3

I have list coming back from a REST endpoint. I need to break that list down into categories (category is an item in each entry of the list). Individual categories will be written to a cache for faster lookup later.

I didn't know if I could .map() the entries and supply multiple filter() or some type of case statement to put the category entries in the right bucket.

Does something like this sound reasonable to implement with rxJava?

UPDATE: Non-working version

private Map<String, List<VideoMetadataInfoEntity>> buildCategories( Observable<List<VideoMetadataInfoEntity>> videoList ) {

    Map<String, List<VideoMetadataInfoEntity>> categoryMap = new HashMap<>();
    videoList
            .flatMap( Observable::from )
            .subscribe( videoMetadataInfoEntity -> mapCategory(videoMetadataInfoEntity, categoryMap ) );

    Observable.just( categoryMap )
            .doOnNext( saveCategoriesToCacheAction );

    return categoryMap;
}

These fire in sequence, however, and this is my understanding, the second observable is not sending anything the saveCategoriesToCacheAction since it hasn't subscribed to the result of the first observable.

I am starting to think I should modify my cache strategy. The list will always have all the details. The service doesn't provide me a subset that I can use for listing and then another call to get the full details. It is either full list or full details for one item. It might be a better approach to just cache each one individually and into their own category caches right now. I was trying to do the map so that this network call could return the requested category, but subsequent calls would come from the cache, until such time as the cache has expired and a new network call refreshes it.

dmfrey
  • 1,230
  • 1
  • 17
  • 33

3 Answers3

3

My solution is:

Observable.range(1, 20)
            .groupBy(number -> number % 2)
            .flatMap(groupedObservable -> groupedObservable.toList())
            .toMap(list -> list.get(0) % 2);

As a result I have [{0=[2, 4, 6, 8, 10, 12, 14, 16, 18, 20], 1=[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]}]

Explanation:

  • range(1, 20) - creates an observable which emits first twenty numbers
  • groupBy(number -> number % 2) - creates an observable that emits group observables where each group observable holds items grouped with the grouping function(here it is x % 2)
  • flatMap(groupedObservable -> groupedObservable.toList()) - turns each group into an observable that emits all its items as a list
  • toMap(list -> list.get(0) % 2) - creates the map
Buckstabue
  • 283
  • 1
  • 13
0

RxJava is more for asynchronous message processing, but as it also espouses functional programming principles it could be used as a poor man's stream api. If you are using Java 8 consider using streams to do this job, but as you are asking this question I assume you are using Java 7.

To do what you want you could try (forgive the lambda, substitute it with an anonymous inner class if you are not using Retrolambda):

Observable.from(list).subscribe(item -> groupItemInCategoryBucket(item));

where groupItemInCategoryBucket is your method that contains the switch statement or whatever other way you have of caching the items.

Please note that this is the equivalent of a for loop, and although it is idiomatic to use this style in many other nice languages, a lot of Java developers might be a bit puzzled when they see this code.

memoizr
  • 2,081
  • 2
  • 18
  • 24
  • I am using retrolambda and it is java7. This will be in an android app. I will give this a try. I need to do some other tasks on the individual categories, such as dumping it to a disk and memory cache so that it is not constantly called over the network. I am hoping I can build out this chain cleanly, or through other subscriptions. In some cases just the list will be required, in other cases, the groups of categories are needed. Stuff like that. Thanks. – dmfrey Nov 20 '15 at 16:13
  • You are welcome. Hope it will work out; if not, please update your question with more specific details regarding the other operations you'd like to perform so that my answer can be more helpful. – memoizr Nov 20 '15 at 16:37
  • I updated my question. I think I need to change my approach. – dmfrey Nov 20 '15 at 19:19
  • The second observable is not subscribed, so it won't do anything. Try calling .subscribe() on the second observable as well. – memoizr Nov 21 '15 at 10:01
0

Generally grouping of items can be achieved using a groupBy operator (for more information about it visit this page).

Map<Integer, List<Integer>> groupedValues = new HashMap<>(4);
    Observable.range(1, 20)
            .groupBy(i -> i % 2, i -> i)
            .subscribe(go -> {
                List<Integer> groupValues = new ArrayList<>();
                groupedValues.put(go.getKey(), groupValues);
                go.subscribe(t -> add(t, groupValues));
            });

How it works:

  • Firstly, observable emits items 1 through 20 (this happens in range method)
  • Which then are emitted to separate observables based on their parity(groupBy method, after this method you operate on GroupedObservable)
  • You then subscribe to the grouped observable, receiving (in subscribers onNext) separate observables that will contain grouped items and the key they were grouped by.
  • Remember to either subscribe to the grouped observables or issue take(0) on them if their content does not interest you to prevent memory leaks.

    I am not sure whether it is the most efficient way or not and would welcome some input about this solution.

    MatBos
    • 2,380
    • 24
    • 34