如何对具有持续时间的对象列表进行分组和减少

问题描述 投票:0回答:2

我想创建一个按

LocalDate
String
排序并按持续时间聚合的人员列表。

我做了一些关于流中持续时间的使用的研究:

以及使用流进行分组并将对象列表减少为子组:

但是,我不可能解决我的问题,类

Person
看起来像这样:

class Person { 
    private LocalDate startDate; 
    private String personType;
    private Duration time1;
    private Duration time2;//constructor, getter+setter, other methods
}

创建的列表的示例如下所示:

List<Person> personList = Arrays.asList(
          new Person("2023-02-02","member1","08:00","4:00"),
        new Person("2023-02-02","member1","50:00","0:45"),  
        new Person("2023-02-02","member2","10:00","0:40"),
        new Person("2023-02-02","member2","01:00","1:20"),
        new Person("2023-02-03","member1","08:00","2:00"),
        new Person("2023-02-03","member1","10:00","0:45"),  
        new Person("2023-02-03","member2","10:00","1:40"),
        new Person("2023-02-03","member2","02:00","1:20"),//... 
 );

我想创建一个按开始日期和人员类型排序的人员列表,以及持续时间的总和。

所需输出:

("2023-02-02","member1","58:00","4:45"),
("2023-02-02","member2","11:00","2:00"),
("2023-02-03","member1","18:00","2:45"),
("2023-02-03","member2","12:00","3:00"),
...

我的方法是使用这样的东西。但是,我无法映射持续时间和字符串值:

Map<LocalDate,List<Person>> result=personList.stream()
.collect(Collectors.groupingBy(Person::getstartDate))
        .entrySet().stream()
        .collect(Collectors.toMap(x -> {
            //how to sum duration values in here?
           // Duration duration1 = x.getValue().stream() ...;
            //how to use String values in here?
           // String string = x.getValue().stream()....
            return new Person(x.getKey(), string, duration1,duration2);
        }, Map.Entry::getValue));
java java-stream java-time duration localdate
2个回答
0
投票

我发现您熟悉 Java 流 groupingBy API。根据您提供的所需输出,我会尝试以下方法。

首先使用 groupingBy 收集器通过 startDatepersonType 的组合键对人员进行分组。然后,对于每个组,它对持续时间求和并使用求和值创建一个新的 Person 对象。最后,将生成的 Person 对象收集到一个列表中。 Person.java 类中的代码应该是这样的:

class Person {
.
.
.
    public static Person sumDurationsAndTypes(List<Person> persons) {
        LocalDate startDate = persons.get(0).getStartDate();
        String personType = persons.get(0).getPersonType();
        Duration sumTime1 = Duration.ZERO;
        Duration sumTime2 = Duration.ZERO;

        for (Person person : persons) {
            sumTime1 = sumTime1.plus(person.getTime1());
            sumTime2 = sumTime2.plus(person.getTime2());
        }

        return new Person(startDate, personType, sumTime1, sumTime2);
    }

    @Override
    public String toString() {
        return "(" + startDate + "," + personType + "," + formatDuration(time1) + "," + formatDuration(time2) + ")";
    }

    private String formatDuration(Duration duration) {
        long hours = duration.toHours();
        long minutes = duration.minusHours(hours).toMinutes();
        return String.format("%02d:%02d", hours, minutes);
    }

sumDurationsAndTypes 方法计算每个人员组的持续时间总和,toString 方法格式化持续时间以进行打印。

然后您可以使用它来检查一切是否按您想要的方式工作:

public class Main {
    public static void main(String[] args) {
        List<Person> personList = Arrays.asList(
                new Person(LocalDate.parse("2023-02-02"), "member1", Duration.ofHours(8), Duration.ofMinutes(4)),
                // Add more persons here...
        );

        Map<String, List<Person>> grouped = personList.stream()
                .collect(Collectors.groupingBy(person -> person.getStartDate() + person.getPersonType()));

        List<Person> resultList = grouped.values().stream()
                .map(Person::sumDurationsAndTypes)
                .collect(Collectors.toList());

        resultList.forEach(System.out::println);
    }
}

如果您需要更多帮助,请告诉我!

祝你好运:)

==============

编辑:

要获取每日分组,您需要按 startDate 进一步分组,并使用 flatMapstartDatepersonType 的每个组合创建单独的条目。我还通过删除不必要的地图来改进您的代码。

public class Main {
    public static void main(String[] args) {
        List<Person> personList = Arrays.asList(
            new Person(LocalDate.parse("2023-02-02"), "member1", Duration.ofHours(8), Duration.ofMinutes(4)),
            new Person(LocalDate.parse("2023-02-02"), "member1", Duration.ofHours(0), Duration.ofMinutes(45)),
            new Person(LocalDate.parse("2023-02-02"), "member2", Duration.ofHours(10), Duration.ofMinutes(40)),
            new Person(LocalDate.parse("2023-02-02"), "member2", Duration.ofHours(1), Duration.ofMinutes(20)),
            new Person(LocalDate.parse("2023-02-03"), "member1", Duration.ofHours(8), Duration.ofMinutes(2)),
            new Person(LocalDate.parse("2023-02-03"), "member1", Duration.ofHours(10), Duration.ofMinutes(45)),
            new Person(LocalDate.parse("2023-02-03"), "member2", Duration.ofHours(10), Duration.ofMinutes(100)),
            new Person(LocalDate.parse("2023-02-03"), "member2", Duration.ofHours(2), Duration.ofMinutes(20))
        );

        List<Person> resultList = personList.stream()
            .collect(Collectors.groupingBy(
                p -> Arrays.asList(p.getStartDate(), p.getPersonType()),
                Collectors.reducing(
                    new Person(LocalDate.now(), "", Duration.ZERO, Duration.ZERO),
                    (p1, p2) -> new Person(
                        p1.getStartDate(),
                        p1.getPersonType(),
                        p1.getTime1().plus(p2.getTime1()),
                        p1.getTime2().plus(p2.getTime2())
                    )
                )
            ))
            .values()
            .stream()
            .flatMap(Collection::stream)
            .collect(Collectors.toList());

        // Print the result
        resultList.forEach(p -> {
            System.out.println(
                p.getStartDate() + "," +
                p.getPersonType() + "," +
                formatDuration(p.getTime1()) + "," +
                formatDuration(p.getTime2())
            );
        });
    }

    public static String formatDuration(Duration duration) {
        long hours = duration.toHours();
        long minutes = duration.minusHours(hours).toMinutes();
        return String.format("%02d:%02d", hours, minutes);
    }
}

我得到了这个结果列表:

(2023-02-02,member1,08:00,00:49)
(2023-02-02,member2,11:00,02:00)
(2023-02-03,member1,18:00,02:47)
(2023-02-03,member2,12:00,03:20)

每行对应于 startDatepersonType 的唯一组合,并且对每个组合的持续时间进行求和。


0
投票

一种方法是进行级联分组并减少持续时间的总和。以下结果是一个 2 级映射,其中包含每个日期和类型的总和:

Map<LocalDate, Map<String, Optional<Person>>> perDateAndTypeAggregated = 
    personList.stream().collect(Collectors.groupingBy(
        Person::getStartDate,
        Collectors.groupingBy(
                Person::getType,
                Collectors.reducing((p1, p2) ->
                    // this is where we sum the durations
                    new Person(p1.getStartDate(), p1.getType(), p1.getTime1().plus(p2.getTime1()), p1.getTime2().plus(p2.getTime2()))
                )
        )
    ));

如果你想要一个列表(地图提供了更丰富的信息,但这是你的应用程序),你可以按如下方式对之前的地图进行展平和排序:

Comparator<Person> byDateThenType =
    Comparator.comparing(Person::getStartDate).thenComparing(Person::getType);

List<Person> result =
    perDateAndTypeAggregated.values().stream()
        .flatMap(m -> m.values().stream())
        .filter(Optional::isPresent)
        .map(Optional::get)
        .sorted(byDateThenType)
        .toList();
© www.soinside.com 2019 - 2024. All rights reserved.