Сопоставление одного объекта другому с агрегацией и группировкой - PullRequest
0 голосов
/ 20 декабря 2018

Отображение одного объекта в другой с агрегацией и группировкой

У меня есть класс

class Q {
    String username;
    int taskId;
    double timediff;

   // constructor
}

, и мне нужно преобразовать его в

 class ToQ {
        String username;
        int noOfTasks;
        double sum;

       // constructor
    }

Так что намерениеявляется группой по имени пользователя, не содержит никаких идентификаторов задач, суммы timediff.

Список этого

List<Q> qs = new ArrayList<>();
    qs.add(new Q("tom", 2, 2.0));
    qs.add(new Q("tom", 6, 1.0));
    qs.add(new Q("harry", 8, 0.03));

Должен иметь вывод ToQ как

new ToQ("tom",2,3);
new ToQ("harry",1,0.03);

Я пытался сФункция группировки, но она генерирует HashMap, но не знает, как преобразовать в объект.

Map<String, Double> collect = qs
            .stream()
            .collect(groupingBy(Q::getUsername, summingDouble(Q::getLocalDateTime)));

Ответы [ 4 ]

0 голосов
/ 20 декабря 2018

Вы можете попробовать это с одним вкладышем тоже:

List<ToQ> toQS = qs.stream()
    .map(Q::getUserName)
    .distinct()
    .map(userName -> new ToQ(
        userName,
        qs.stream()
            .map(Q::getUserName)
            .filter(userName::equals)
            .count(),
        qs.stream()
            .map(Q::getUserName)
            .filter(q -> Objects.equals(userName, q.getUserName()))
            .mapToDouble(Q::getTimeDiff)
            .sum()
    ))
    .collect(toList())
0 голосов
/ 20 декабря 2018

Вы можете сделать это следующим образом:

Группировать по имени для TimeDiff

Map<String, Double> nameToTimeDiffSumMap = qs.stream()
        .collect(Collectors.groupingBy(Q::getUsername, 
                Collectors.summingDouble(Q::getTimediff)));

Группировать по имени для счета

Map<String, Long> nameToCountMap = qs.stream()
        .collect(Collectors.groupingBy(Q::getUsername, 
                Collectors.counting()));

Форма a List<ToQ>

List<ToQ> toQS = qs.stream().map(a -> 
        new ToQ(a.getUsername(), nameToCountMap.get(a.getUsername()), nameToTimeDiffSumMap.get(a.getUsername())))
        .collect(toList());

, который предполагает, что классы имеют геттеры и сеттеры и соответствующий конструктор , а также

class ToQ {
    String username;
    long noOfTasks;
    double sum;
}

class Q {
    String username;
    int taskId;
    double timediff;
}
0 голосов
/ 20 декабря 2018

простой способ будет выглядеть так: используйте toMap() с функцией слияния.

 Map<String, ToQ> map = qs.stream()
      .collect(toMap(Q::getUsername, q -> new ToQ(q.username, q.taskId, q.timediff), ToQ::merge));
 List<ToQ> toQList =  new ArrayList<>( map.values());

и определите метод в ToQ:

public ToQ merge(ToQ q){
     this.noOfTasks+=q.noOfTasks;
     this.sum+=q.sum;
     return this;
}
0 голосов
/ 20 декабря 2018

Это может лучше сделать с помощью коллектора toMap:

Collection<ToQ> values = qs.stream().collect(toMap(Q::getUsername,
            v -> new ToQ(v.getUsername(), 1, v.getTimediff()),
            (l, r) -> {
                l.setNoOfTasks(l.getNoOfTasks() + r.getNoOfTasks());
                l.setSum(l.getSum() + r.getSum());
                return l;
            })).values();

Предполагается, что у вас есть Q конструктор следующим образом:

public ToQ(String username, int noOfTasks, double sum) {...}

если вы хотите сохранить с помощью groupingBy, то вы можете сделать это следующим образом:

List<ToQ> result = qs.stream()
                .collect(groupingBy(Q::getUsername))
                .values()
                .stream()
                .map(l -> new ToQ(l.get(0).getUsername(), l.size(), l.stream().mapToDouble(Q::getTimediff).sum()))
                .collect(toList());
...