【问题标题】:Cumulative merge of two lists两个列表的累积合并
【发布时间】:2022-11-02 04:03:56
【问题描述】:

假设我有两个名为“Record”的对象的排序列表,并且该对象有 2 个属性,它们是“epochDate”(长)和“计数”(int)。一个列表表示给定日期通过的记录计数,第二个列表表示取消的记录计数。

public class Record {
    long epochDate;
    int count;

    public Record(long epochDate, int value) {
        this.epochDate= epochDate;
        this.value = value;
    }
}

因此,我需要返回一个名为“数据”的对象的排序列表,其中包含如下所示的 3 个属性和每个日期的累积结果,这意味着必须将给定日期的通过/取消记录的数量添加到通过/取消的总数中列表中所有过去日期的记录。

public class Data {
    long epochDate;
    int passed;
    int cancelled;

    public Data(long epochDate, int passed, int cancelled) {
        this.epochDate = epochDate;
        this.passed = passed;
        this.cancelled = cancelled;
    }
}

给定列表和预期结果如下所示:

List<Record> passed = Arrays.asList(
                new Record(1666224000, 5),  // Date: 20.10.2022
                new Record(1666396800, 10), // Date: 22.10.2022
                new Record(1666483200, 20), // Date: 23.10.2022
                new Record(1666656000, 50), // Date: 25.10.2022
                new Record(1666828800, 30));// Date: 27.10.2022

List<Record> cancelled = Arrays.asList(
                new Record(1666310400, 1),  // Date: 21.10.2022
                new Record(1666396800, 2),  // Date: 22.10.2022
                new Record(1666483200, 3),  // Date: 23.10.2022
                new Record(1666569600, 4),  // Date: 24.10.2022
                new Record(1666742400, 6)); // Date: 26.10.2022

List<Data> expectedResult = Arrays.asList(
                new Data(1666224000, 5, 0),     // Date: 20.10.2022
                new Data(1666310400, 5, 1),     // Date: 21.10.2022
                new Data(1666396800, 15, 3),    // Date: 22.10.2022
                new Data(1666483200, 35, 6),    // Date: 23.10.2022
                new Data(1666569600, 35, 10),   // Date: 24.10.2022
                new Data(1666656000, 85, 10),   // Date: 25.10.2022
                new Data(1666742400, 85, 16),   // Date: 26.10.2022
                new Data(1666742400, 115, 16)); // Date: 27.10.2022

现在我仍然得到不正确的结果,但感觉整个方法可能是错误的,我正在迭代和累积第一个列表的结果,然后迭代第二个以填充重复日期的值。我使用 TreeMap 作为返回排序结果的工具。

我在处理每个列表的唯一日期时遇到问题,我可能必须执行一些双 ^2 迭代才能得到我想要的。

我目前的方法看起来或多或少像那样,我被困住了。

private static List<Data> generateResult(List<Record> passed, List<Record> cancelled) {
        Map<String, Data> temporaryResult = new TreeMap<>();
        List<Data> result;
        int cumulation = 0;

        for (Record record : passed) {
            cumulation += record.getCount();
            temporaryResult.put(String.valueOf(record.getEpochDate()), new Data(record.getEpochDate(), cumulation, 0));
        }

        cumulation = 0;
        String lastPassedDate = "";
        int passedNumber = 0;

        for (Record record : cancelled) {
            if (temporaryResult.containsKey(String.valueOf(record.getEpochDate()))) {
                lastPassedDate = String.valueOf(record.getEpochDate());
                cumulation += record.getCount();
                Data reference = temporaryResult.get(String.valueOf(record.getEpochDate()));
                reference.setCancelled(cumulation);
            } else {
                if (!lastPassedDate.equals("")) {
                    passedNumber = temporaryResult.get(lastPassedDate).getPassed();
                }
                cumulation += record.getCount();
                temporaryResult.put(String.valueOf(record.getEpochDate()),
                        new Data(record.getEpochDate(), passedNumber, cumulation));
            }
        }

        result = new ArrayList<>(temporaryResult.values());

        return result;
    }

有谁知道这种方法是否正确,如果不是,有没有更好的方法来解决它?

编辑:

我想我实际上解决了它,方法如下:

private static List<Data> generateResult(List<Record> passed, List<Record> cancelled) {
        Map<Long, Data> treeMap = new TreeMap<>();
        List<Data> result;

        for (Record record : passed) {
            treeMap.put(record.getEpochDate(), new Data(record.getEpochDate(), record.getCount(), 0));
        }

        for (Record record : cancelled) {
            if (treeMap.containsKey(record.getEpochDate())) {
                Data reference = treeMap.get(record.getEpochDate());
                reference.setCancelled(record.getCount());
            } else {
                treeMap.put(record.getEpochDate(), new Data(record.getEpochDate(), 0, record.getCount()));
            }
        }

        int passedCount = 0;
        int cancelledCount = 0;

        for (Data data : treeMap.values()) {
            if (treeMap.values().stream().findFirst().equals(data)) {
                passedCount = data.getPassed();
                cancelledCount = data.getCancelled();
            } else {
                passedCount += data.getPassed();
                cancelledCount += data.getCancelled();
                data.setPassed(passedCount);
                data.setCancelled(cancelledCount);
            }
        }

        result = new ArrayList<>(treeMap.values());

        return result;
    }

【问题讨论】:

  • “Java 中的最佳解决方案是什么?”- 首先,您能分享一下您提出的解决方案吗? StackOverflow 上的每个问题都有望展示一项研究工作。见How do I ask a good question?Why is "Can someone help me?" not an actual question?
  • 好吧,伙计们,我已经编辑了整篇文章来回答你的问题,我昨天急着写这篇文章,我承认这是一个表述不当的问题。现在应该会更好,如果可以,请看一下。

标签: java algorithm


【解决方案1】:

您可以使用 Stream 的强大功能

请注意,我更改了预期结果,最后一个“数据”在 epochDate 上的值错误,日期 27.10.2022 是 1666828800 而不是 1666742400。

@ExtendWith(SpringExtension.class)
class StackOverflowTest {
    
    private List<Record> passed = Arrays.asList(
            new Record(1666224000, 5),  // Date: 20.10.2022
            new Record(1666396800, 10), // Date: 22.10.2022
            new Record(1666483200, 20), // Date: 23.10.2022
            new Record(1666656000, 50), // Date: 25.10.2022
            new Record(1666828800, 30));// Date: 27.10.2022

    private List<Record> cancelled = Arrays.asList(
            new Record(1666310400, 1),  // Date: 21.10.2022
            new Record(1666396800, 2),  // Date: 22.10.2022
            new Record(1666483200, 3),  // Date: 23.10.2022
            new Record(1666569600, 4),  // Date: 24.10.2022
            new Record(1666742400, 6)); // Date: 26.10.2022

    private List<Data> expectedResult = Arrays.asList(
            new Data(1666224000, 5, 0),     // Date: 20.10.2022
            new Data(1666310400, 5, 1),     // Date: 21.10.2022
            new Data(1666396800, 15, 3),    // Date: 22.10.2022
            new Data(1666483200, 35, 6),    // Date: 23.10.2022
            new Data(1666569600, 35, 10),   // Date: 24.10.2022
            new Data(1666656000, 85, 10),   // Date: 25.10.2022
            new Data(1666742400, 85, 16),   // Date: 26.10.2022
            new Data(1666828800, 115, 16)); // Date: 27.10.2022

    @Test
    void testExecute() {
        
        // Convert ArrayList to Map
        Map<Long, Data> passedMap = passed
                .stream()
                .collect(Collectors.toMap(Record::getEpochDate, record -> new Data(record.getEpochDate(), record.getCount(), 0)));

        Map<Long, Data> cancelledMap = cancelled
                .stream()
                .collect(Collectors.toMap(Record::getEpochDate, record -> new Data(record.getEpochDate(), 0, record.getCount())));

        AtomicInteger passedCount = new AtomicInteger(0);
        AtomicInteger cancelledCount = new AtomicInteger(0);

        // Create result Map
        Map<Long, Data> resultMap = Stream.of(passedMap, cancelledMap)
                .flatMap(map -> map.entrySet().stream())
                .sorted(Map.Entry.comparingByKey())
                .collect(Collectors.toMap(
                    // Key
                    Map.Entry::getKey,
                    // Value
                    map -> {
                        Data record = map.getValue();
                        passedCount.set(passedCount.get() + record.getPassed());
                        cancelledCount.set(cancelledCount.get() + record.getCancelled());
                        return new Data(record.getEpochDate(), passedCount.get(), cancelledCount.get());
                    },
                    // manage merge conflicts due to common key
                    (passedRecord, cancelledRecord) -> {
                        return new Data(passedRecord.getEpochDate(), passedRecord.getPassed(), cancelledRecord.getCancelled());
                    }
                ));

        List<Data> result = new ArrayList<Data>(resultMap.values());
        assertThat(result).usingRecursiveComparison().isEqualTo(expectedResult);
        
    }
    
    @lombok.Data
    public class Record {
        long epochDate;
        int count;

        public Record(long epochDate, int count) {
            this.epochDate= epochDate;
            this.count = count;
        }
    }
    
    @lombok.Data
    public class Data {
        long epochDate;
        int passed;
        int cancelled;

        public Data(long epochDate, int passed, int cancelled) {
            this.epochDate = epochDate;
            this.passed = passed;
            this.cancelled = cancelled;
        }
    }
    
}

【讨论】:

  • 你确定这行得通吗?它为我从上面的输入返回不正确的结果,日期 25.10.2022 和 27.10.2022 的取消数字为零。
  • 是的,我在发布之前对其进行了测试。我将分享所有的课堂测试。
猜你喜欢
  • 2015-02-04
  • 2021-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-08
  • 2019-10-05
相关资源
最近更新 更多