如何对按日期和时间键入的地图数据进行排序,并按日期和/或时间进行汇总?

时间:2015-05-27 22:47:07

标签: java

假设我有一个整数数据映射,其中键是日期和时间,格式如下:

  

2015年5月15日下午5:46

例如,我有:

  

2015年5月15日下午5:46,25   2015年5月15日下午5点50分,25周年   2015年5月15日下午6:15,30   2015年5月15日8:05 PM,40
  2015年5月15日10:46 PM,10
  2015年5月15日上午5点10分,5周

有没有办法按小时对这些数据进行排序?所以我可以有类似的东西:

  下午5点,55周   下午6点30分   晚上8点,40   晚上10点,10日

日期也一样吗?

3 个答案:

答案 0 :(得分:2)

Map<String, Integer> originalMap = ... // the map you mentioned
Map<String, Integer> aggregationMap = new HashMap<String, Integer>();
DateFormat sdf = new SimpleDateFormat("MMM dd, yyyy hh:mm a", Locale.US);

// change "ha" to "yyyyMMdd" if you wanna do aggregation by date
DateFormat sdf2 = new SimpleDateFormat("ha", Locale.US);
Iterator<String> it = originalMap.keySet().iterator();
while (it.hasNext()) {
    String k = it.next();
    int v = originalMap.get(k);
    String key = sdf2.format(sdf.parse(k));
    Integer value = aggregationMap.get(key);
    if (value == null) {
        aggregationMap.put(key, v);
    } else {
        aggregationMap.put(key, v + value);
    }
}

// TODO dump aggregationMap to see the result

答案 1 :(得分:0)

假设您正在使用java.util.Map的实现类,我认为这样做的最好方法是:

  1. 使用entrySet()方法获取地图条目集。
  2. 编写自定义Comparator类,按小时按升序对日期时间条目进行排序。另外,你可以写一个按日期排序。
  3. 使用使用自定义SortableSet初始化TreeSet的构造函数创建TreeSet(Comparator的实现)。
  4. 使用TreeSet.addAll()复制和排序您在步骤1中获得的Set<Map.Entry>条目。
  5. 或者,如果您不想创建与现有地图完全相同的数据结构,则可以获取keySet并仅对键进行排序。然后,当您想按顺序访问地图元素时,您可以遍历已排序的keySet并将相应的数字拉出已有的地图。

答案 2 :(得分:0)

首先,我假设您的原始数据存储在Map<String, Integer>中,该rawData是名为<T extends Comparable<? super T> & Temporal> SortedMap<T, Integer> summarize( DateTimeFormatter keyFormat, Function<LocalDateTime, ? extends T> keyTransform) { SortedMap<T, Integer> summaryData = new TreeMap<>(); rawData.forEach((k, v) -> { LocalDateTime rawDateTime = LocalDateTime.parse(k, keyFormat); T summaryKey = keyTransform.apply(rawDateTime); summaryData.merge(summaryKey, v, (x, y) -> x + y); }); return summaryData; } 的成员字段。使用Java 8,这是您所需的聚合摘要和排序逻辑的核心:

rawData

此方法处理Temporal映射并返回其键为LocalDate类型的有序映射 - 例如,没有时间组件的LocalTime或{{1}没有日期组件。为了确定哪种行为,您必须传入两个参数:

  1. 格式对象,描述如何将原始数据的String键解释为LocalDateTime
  2. 转换功能,然后将LocalDateTime转换为您所需的粒度级别的聚合键。
  3. summaryData.merge(...)的调用负责第一次添加密钥和值;当同一个键再次合并时,提供的lambda表达式结合了先前值和新值 - 在您的情况下,您只想添加两个值。

    密钥类型(例如LocalDate)也必须是Comparable,以便排序的地图实现将自动进行排序。这是一个帮助方法,它使用上面的方法在您的问题中按小时生成所需的输出。仍需要原始数据密钥格式化程序作为输入:

    void printSummaryByHour(DateTimeFormatter rawKeyFormat)
    {
        SortedMap<LocalTime, Integer> summary =
            summarize(
                rawKeyFormat,
                key -> LocalTime.of(key.getHour(), 0) // ignore date, minutes, seconds, etc.
            );
    
        DateTimeFormatter summaryKeyFormat =
            DateTimeFormatter.ofPattern("h a", Locale.US); // e.g. "7 PM"
        print(summary, summaryKeyFormat::format);
    }
    

    summarize方法的调用使用 lambda表达式LocalDateTime映射到始终忽略分钟值的LocalTime(删除日期)只保留时间。然后它将生成的有序集传递给我稍后将要覆盖的方法输出到控制台,但很可能你的真实程序不仅要打印到屏幕上。

    您的问题没有给出您预期输出的示例&#34;按日期&#34;,所以我假设您想要离散的日子,例如&#34; 2015年5月15日和#34 ;并且不会在几个月或任何特殊情况下汇总 - 只需总结整个24小时的时间段。使用上述方法的不同版本可以轻松完成此操作,仅更改摘要键类型,摘要转换和输出格式:

    void printSummaryByDate(DateTimeFormatter rawKeyFormat)
    {
        SortedMap<LocalDate, Integer> summary =
            summarize(
                rawKeyFormat,
                LocalDateTime::toLocalDate
            );
    
        DateTimeFormatter summaryKeyFormat =
            DateTimeFormatter.ofPattern("MMMM dd, yyyy", Locale.US); // e.g. "May 15, 2015"
        print(summary, summaryKeyFormat::format);
    }
    

    现在,排序后的地图由LocalDate代替LocalTime。从日期时间到日期的密钥转换仅使用函数引用而不是自定义lambda表达式,并且摘要键的输出格式也相应地更改。

    要完成答案,请参阅设置脚手架和print辅助方法:

    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.LocalTime;
    import java.time.format.DateTimeFormatter;
    import java.time.temporal.Temporal;
    import java.util.*;
    import java.util.function.Function;
    
    public class StackOverflow30494397
    {
        private final Map<String, Integer> rawData = new HashMap<>();
    
        public static void main(String... args)
        {
            StackOverflow30494397 instance = new StackOverflow30494397();
            instance.rawData.put("May 15, 2015 5:46 PM", 25);
            instance.rawData.put("May 15, 2015 5:50 PM", 25);
            instance.rawData.put("May 15, 2015 6:15 PM", 30);
            instance.rawData.put("May 15, 2015 8:05 PM", 40);
            instance.rawData.put("May 15, 2015 10:46 PM", 10);
            instance.rawData.put("May 15, 2015 5:10 AM", 5);
    
            DateTimeFormatter rawKeyFormat =
                DateTimeFormatter.ofPattern("MMMM dd, yyyy h:mm a", Locale.US);
    
            instance.printSummaryByHour(rawKeyFormat);
            instance.printSummaryByDate(rawKeyFormat);
        }
    
        private <K> void print(Map<K, ?> map, Function<? super K, String> keyFormatter)
        {
            map.forEach((k, v) -> System.out.printf("%s, %d%n", keyFormatter.apply(k), v));
            System.out.println();
        }
    
        // INSERT OTHER METHODS (DESCRIBED IN ANSWER) HERE    
    }
    

    输出:

      

    凌晨5点,5      下午5点,50
         下午6点30      晚上8点,40
         晚上10点,10日

         

    2015年5月15日,135