Расширьте вывод потока с groupingBy & summingInt в Java8 - PullRequest
0 голосов
/ 10 сентября 2018

У меня есть тема:

@Entity
public class Topic {
    @Id
    private int id;
    private LocalDate date;
    private String name;
    private int points;
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
}

Я получаю список тем в данную дату по методу jpa данных весны:

Список тем = topicRepository.findByDateBetween (начало, конец);который на выходе имеет, например:

Topic(id=1, date="2018-01-01", name="Java examples", User(...), 12)
Topic(id=2, date="2018-02-02", name="Java examples", User(...), 34)
Topic(id=3, date="2018-02-02", name="Java examples", User(...), 56)
Topic(id=4, date="2018-03-03", name="Java examples", User(...), 78)
Topic(id=5, date="2018-03-03", name="Java examples", User(...), 90)

То, что я пытаюсь достичь, это отфильтровать вывод результатов как (если дата && Пользователь имеет те же точки добавления)

Topic(id=1, date="2018-01-01", name="Java examples", User(...), 12)
Topic(id=2, date="2018-02-02", name="Java examples", User(...), 90)
Topic(id=4, date="2018-03-03", name="Java examples", User(...), 168)

Мое реальное решениевозвращает карту с датой в качестве ключа и суммированными точками в качестве значения, но мне нужно, чтобы в выводе было больше данных, таких как «Пользователь» или «Имя».

return topics.stream()
        .collect(Collectors.groupingBy(Topic::getDate,
                Collectors.summingInt(Topic::getPoints)));

Может быть, есть другой способ, вместо того, чтобы карта возвращала созданный dto для этогодело?например,

@Data
public class ResultDto {
    private LocalDate date;
    private String name;
    private int points;
    private User user;
}

Ответы [ 2 ]

0 голосов
/ 10 сентября 2018

Простой способ сгруппировать по подмножеству полей - это использовать TreeMap с пользовательским Comparator. Предположим, вы должны определить

Comparator<Topic> byDateAndUser = Comparator.comparing(Topic::getDate)
            .thenComparing(t -> t.getUser().getUserId());

Map<Topic,...> map = new TreeMap<>(byDateAndUser);

Получившаяся карта будет использовать предоставленный компаратор вместо equals для определения равенства и, таким образом, будет относиться ко всем темам с одинаковой датой и пользователем как к одному.

Эта функция TreeMap позволяет вам вычислить карту темы по сумме баллов, и она будет содержать только одну запись для каждой комбинации даты / пользователя:

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.summingInt;

Comparator<Topic> byDateAndUser = Comparator.comparing(Topic::getDate)
            .thenComparing(t -> t.getUser().getUserId());

Map<Topic, Integer> pointTotals = topics.stream()
        .collect(groupingBy(
                topic -> topic, 
                () -> new TreeMap<>(byDateAndUser), 
                summingInt(Topic::getPoints)
        ));
0 голосов
/ 10 сентября 2018

Предполагая, что User реализует hashCode и equals последовательно , вы можете сгруппировать по карте составной ключ даты / пользователя и значения ResultDto. Для этого вам понадобятся две операции: одна для сопоставления to, а другая для агрегирования точек для каждой группы. (Вы можете поместить эти операции либо в ResultDto, либо в служебный класс и т. Д., Здесь я предполагаю, что первая из них находится в Mapper служебном классе, а вторая - в ResultDto):

public final class Mapper {

    private Mapper() { }

    public static ResultDto fromTopic(Topic topic) {
        ResultDto result = new ResultDto();
        result.setDate(topic.getDate());
        result.setName(topic.getName());
        result.setPoints(topic.getPoints());
        result.setUser(topic.getUser());
        return result;
    }
}

В ResultDto:

public ResultDto merge(ResultDto another) {
    this.points += another.points;
    return this;
}

Обратите внимание, что я назначаю первый Topic.name, найденный в операции сопоставления Mapper.fromTopic. Я предполагаю, что это несоответствие в вашем примере, пожалуйста, примите это во внимание, если вы используете этот подход в реальном сценарии.

Теперь мы можем передавать темы и группировать по дате / пользователю:

Map<List<Object>, ResultDto> groups = topics.stream()
    .collect(Collectors.toMap(
        topic -> Arrays.asList(topic.getDate, topic.getUser()), // Java 9: List.of
        Mapper::fromTopic,
        ResultDto::merge));

Собирает карту, ключ которой - List, первый элемент - дата, а второй - пользователь. Эти списки из 2 элементов не очень полезны для последующего использования, здесь мы используем их только для создания составного ключа и группирования тем по этому ключу. Значения карты - это экземпляр ResultDto, который изначально создается из темы, а затем объединяется с другими темами, принадлежащими к той же группе (та же дата и пользователь). В этой операции ResultDto.merge сумма points суммируется.

Нужные вам результаты - это значения карты:

Collection<ResultDto> results = groups.values(); // or new ArrayList<>(groups.values())

РЕДАКТИРОВАТЬ: Вот несколько более краткий вариант без потоков:

Map<List<Object>, ResultDto> groups = new HashMap<>();

topics.forEach(topic -> groups.merge(
    List.of(topic.getDate(), topic.getUser()), // Java 8: Arrays.asList
    Mapper.fromTopic(topic),
    ResultDto::merge));

Collection<ResultDto> results = groups.values();
...