Сгруппировать список событий в словарь событий по дате

Я пытался изучить RxJava2, и я боролся с этим.

Итак, у меня есть структура, представляющая события, которые выглядят примерно так:

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

И где-то я запрашиваю список событий из репозитория, которые хочу сгруппировать по дате.

Итак, учитывая список событий, таких как:

  • Event1 в июне
  • Event2 в июне
  • Event3 в июле
  • Event4 в августе
  • Event5 в августе

Я хочу сгруппировать их так, чтобы

  • June
    • Event1
    • Событие2
  • July
    • Event3
  • August
    • Event4
    • Событие5

То, что у меня есть до сих пор, на мой взгляд, очень уродливо, и я почти уверен, что переусердствую с этим...

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() {

        }
    });

На данный момент это дает мне наблюдаемую, которая доставляет Pair>, но, как вы можете видеть, она преобразуется в Object, и я, честно говоря, не могу разобраться в этом аду дженериков -.-'

Любые советы о том, как я мог подойти к этому?

Спасибо


person João Gonçalves    schedule 21.03.2017    source источник


Ответы (2)


Вы можете добиться этого, просто используя оператор collect:

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

Без лямбд:

// 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);
        }}
    )
    ...

Способ поместить событие на карту:

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);
    }
}
person Maksim Ostrovidov    schedule 21.03.2017
comment
@João Я исправил некоторые ошибки и добавил код, отличный от лямда. - person Maksim Ostrovidov; 22.03.2017
comment
большое спасибо за ваш ответ, к сожалению, часть blockingGet() заставляет BiConsumer внезапно останавливаться, и последующие подписчики не выполняются. - person João Gonçalves; 23.03.2017
comment
Я нашел более простое решение, используя только collect. Я обновил свой ответ. Кстати, почему поток был заблокирован, описано здесь - github.com/ReactiveX/RxJava/issues/3611 - person Maksim Ostrovidov; 23.03.2017
comment
Спасибо за интересный вопрос ;) - person Maksim Ostrovidov; 23.03.2017

Основываясь на @Maxim Ostrovidov answer, я смог адаптировать его к следующему рабочему решению:

repository.getAllEvents()
    // Convert the Single<Events> into an Observable<Events>
    .toObservable()

    // Transform the stream Events into a List<Event> stream / observable
    .flatMapIterable(new Function<Events, List<Event>>() {
        @Override
        public List<Event> apply(@NonNull Events events) throws Exception {
            return events.getEvents();
        }
    })

    // Group each Event from the List<Event> by when (date)
    .groupBy(new Function<Event, Date>() {
        @Override
        public Date apply(@NonNull Event event) throws Exception {
            return event.when;
        }
    })

    // For each grouped stream (not sure if its correct to call it like this)
    // Lets generate a new stream that is a Pair<Date, List<Event>>
    .flatMap(new Function<GroupedObservable<Date, Event>, Observable<Pair<Date, List<Event>>>>() {
        @Override
        public Observable<Pair<Date, List<Event>>> apply(@NonNull GroupedObservable<Date, Event> dateEventGroupedObservable) throws Exception {
            final Date key = dateEventGroupedObservable.getKey();
            // toList() takes a big role here since it is forcing 
            // for the dateEventGroupedObservable to complete and only then 
            // streaming a Single<List<Event>> which is why I convert it back to observable
            return dateEventGroupedObservable.toList().toObservable().flatMap(new Function<List<Event>, Observable<Pair<Date, List<Event>>>>() {
                @Override
                public Observable<Pair<Date, List<Event>>> apply(@NonNull List<Event> events) throws Exception {
                    return Observable.just(new Pair<Date, List<Event>>(key, events));
                }
            });
        }
    })

    // We can now collect all streamed pairs of (Date, List<Event>) 
    // into an HashMap
    .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>>, Pair<Date, List<Event>>>() {
        @Override
        public void accept(@NonNull HashMap<Date, List<Event>> dateListHashMap, @NonNull Pair<Date, List<Event>> dateListPair) throws Exception {
            dateListHashMap.put(dateListPair.first, new ArrayList<Event>(dateListPair.second));
        }
    })
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribeWith(new SingleObserver<HashMap<Date, List<Event>>>() {
        @Override
        public void onSubscribe(Disposable d) {

        }

        @Override
        public void onSuccess(HashMap<Date, List<Event>> dateListHashMap) {

        }

        @Override
        public void onError(Throwable e) {

        }
    });

Да, он длинный и некрасивый, но я уверен, что с лямбда-выражениями это выглядело бы лучше. Теперь дело в том, что... Этот код используется для подачи адаптера представления переработчика, поэтому мне интересно, не будет ли проще сделать это императивным способом... да ладно, служит цели изучения :)

person João Gonçalves    schedule 22.03.2017