dmfrey
dmfrey

Reputation: 1240

rxJava transform List to Map

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.

Upvotes: 3

Views: 6767

Answers (3)

Buckstabue
Buckstabue

Reputation: 303

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

Upvotes: 3

MatBos
MatBos

Reputation: 2390

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.

    Upvotes: 0

    memoizr
    memoizr

    Reputation: 2099

    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.

    Upvotes: 0

    Related Questions