João Gonçalves João Gonçalves -4 years ago 79
Java Question

Group a list of events into a dictionary of events by date

I've been trying to learn RxJava2 and I've been struggling with this one..

So, I have a structure that represents an events that goes something like the following:

class Event{
public Date when;
public String eventName;
}


And somewhere I query a list of events from the repository that I want to group by date.

So, given a list of events like:


  • Event1 at June

  • Event2 at June

  • Event3 at July

  • Event4 at August

  • Event5 at August



I want to group them so that


  • June


    • Event1

    • Event2


  • July


    • Event3


  • August


    • Event4

    • Event5




What I have so far is, in my opinion, very ugly and I am pretty sure I am over-"engineering" this...

repository.getAllEvents()
.toObservable()
.flatMap(new Function<Events, Observable<Event>>() {
@Override
public Observable<Event> apply(@NonNull Events events) throws Exception {
return Observable.fromIterable(events.getEvents());
}
})
.groupBy(new Function<Event, Date>() {
@Override
public Date apply(@NonNull Event event) throws Exception {
return event.when;
}
})
.flatMap(new Function<GroupedObservable<Date, Event>, Observable<Object>>() {
@Override
public Observable<Object> apply(@NonNull GroupedObservable<Date, Event> dateEventGroupedObservable) throws Exception {
final Date key = dateEventGroupedObservable.getKey();
return dateEventGroupedObservable.toList().toObservable().flatMap(new Function<List<Event>, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(@NonNull List<Event> events) throws Exception {
return Observable.just(new Pair<Date, List<Event>>(key, events));
}
});
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new Observer<Object>() {
@Override
public void onSubscribe(Disposable d) {

}

@Override
public void onNext(Object o) {

}

@Override
public void onError(Throwable e) {

}

@Override
public void onComplete() {

}
});


So far, this gives me an observable that delivers a Pair> but as you can see it gets converted to Object and I honestly can't make sense out of the generics hell -.-'

Any tips on how I could approach this?

Thanks

Answer Source

You can achieve this simply by using collect operator:

repository.getAllEvents()
    .flatMapIterable(events -> events.getEvents())
    .collect(() -> new HashMap<Date, List<Event>>(), 
             (map, event) -> putEventIntoMap(map, event)
    )
    ...

Without lambdas:

// I assume that getAllEvents returns Events class
repository.getAllEvents()
    .flatMapIterable(new Function<Events, Iterable<? extends Event>>() {
        @Override
        public Iterable<? extends Event> apply(@NonNull Events events) throws Exception {
           return events.getEvents();
        }
    })
    .collect(new Callable<HashMap<Date, List<Event>>>() {
        @Override
        public HashMap<Date, List<Event>> call() throws Exception {
            return new HashMap<Date, List<Event>>();
        }}, new BiConsumer<HashMap<Date, List<Event>>, Event>() {
        @Override
        public void accept(@NonNull HashMap<Date, List<Event>> map, @NonNull Event event) throws Exception {
            putEventIntoMap(map, event);
        }}
    )
    ...

Method to put event into map:

private void putEventIntoMap(HashMap<Date, List<Event>> map, Event event) {
    if (map.containsKey(event.when)) {
        map.get(event.when).add(event);
    } else {
        List<Event> list = new ArrayList<>();
        list.add(event);
        map.put(event.when, list);
    }
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download