将值放入地图的内部地图

Posted

技术标签:

【中文标题】将值放入地图的内部地图【英文标题】:Put the value into the inner map of a map 【发布时间】:2022-01-09 04:24:43 【问题描述】:

我有一张地图:Map<String,<LocalDate,List<Integer>>。在循环的每次迭代中,我需要将 List<List<String> 中的每个国家/地区的每日病例插入每一天。每天将仅限于一条记录。但是我当前的尝试失败了,因为它将在表中的第一个日期附加所有列表。

List<List<String>

[Province/State, Country/Region, Lat, Long, 1/22/20, 1/23/20, 1/24/20, 1/25/20, 1/26/20, 1/27/20]
[, Afghanistan, 33.93911, 67.709953, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]
[, Angola, -11.2027, 17.8739, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0]
[, Angeria, -12.3047, 17.8739, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0]
[, Andora, -13.2087, 17.8739, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0]

期望的输出

Afghanistan --> 2020-01-22=[0], 2020-01-23=[0], 2020-01-24=[3], 2020-01-25=[0], 2020-01-26=[0]
Angola --> 2020-01-22=[0], 2020-01-23=[0], 2020-01-24=[0], 2020-01-25=[0], 2020-01-26=[0] 
Angeria --> 2020-01-22=[0], 2020-01-23=[0], 2020-01-24=[0], 2020-01-25=[0], 2020-01-26=[0] 
Andora --> 2020-01-22=[0], 2020-01-23=[0], 2020-01-24=[0], 2020-01-25=[0], 2020-01-26=[0] 

我的尝试

 Map<String, Map<LocalDate, List<Integer>>> dataMap = new LinkedHashMap<>();
 Map<LocalDate,List<Integer>> innerMap = new LinkedHashMap<>();
        IntStream //functional for loop to add the date as keys into the map
                .range(0,covidListWithoutCountryDetails.get(0).size())
                .forEach(i->
                        innerMap
                                .put(keys.get(i),new ArrayList<>()
                                )
                );
IntStream  //functional for loop to add the country keys into map 
    .range(0,mapKeys.size())
    .forEach(i->dataMap
    .put(mapKeys.get(i), innerMap));

【问题讨论】:

【参考方案1】:

首先,我建议使用an implementation of .zip(),它可以让您将两个流合并为一个。它极大地简化了地图的创建,因为您可以组合键和值流。

我将假设带有签名similar to the one found in Guava 的zip 实现可用:

public static <A,B,R> Stream<R> zip(Stream<A> streamA,
                                    Stream<B> streamB,
                                    BiFunction<? super A,? super B,R> function)

使用这两个非常简单的重载助手可以派生出从键和值流或单个条目流创建映射:

private static <K, V> Map<K, V> toMap(Stream<K> keys, Stream<V> values) 
    return toMap(zip(keys, values, AbstractMap.SimpleEntry::new));


private static <K, V> Map<K, V> toMap(Stream<Map.Entry<K, V>> entries) 
    return entries
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

有了这个,剩下的代码就是几个转换链,然后将数据组合到地图中。为了可读性,这分为三种方法,如果需要,可以将其重构为进一步拆分,或者可以内联方法:

/**
 * Convert the table data into a breakdown per country
 * @param input - data in table format. First entry contains the headings, 
 * the rest are the rows with corresponding data
 * @return breakdown where each country has number of cases per day as list
 */
public static Map<String, Map<String, List<Integer>>> transform(List<List<String>> input) 
    List<String> labels = transformDateStrings(input.get(0));

    Stream<Map.Entry<String, Map<String, List<Integer>>>> outer = input.stream()
        .skip(1) //skip the header data
        .map(data -> 
            Map<String, List<Integer>> innerMap = transformToInnerMap(labels, data);
            String country = data.get(1);

            return new AbstractMap.SimpleEntry(country, innerMap);
        );

    return toMap(outer);


/**
 * Transform the dates in the headings to proper ISO 8601 format showing a date.
 * @param headings  - list of strings in <month>/<day>/<year> format
 * @return list of strings in <year>-<month>-<day> 
 */
private static List<String> transformDateStrings(List<String> headings) 
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M/d/yy");

    return headings.stream()
        .skip(4) // ignore the non-date fields
        .map(str -> LocalDate.parse(str, formatter))
        .map(date -> date.format(DateTimeFormatter.ISO_LOCAL_DATE))
        .collect(Collectors.toList());


/** 
 * Transform input like
 * [, Afghanistan, 33.93911, 67.709953, 0, 0, 3, 0, 0, 0, ]
 * into only labelled values
 * @param labels - string dates to use for labels of each value
 * @param data   - row of information to transform
 * @return a map in format  2020-01-22=[0], 2020-01-23=[0] 
 */
private static Map<String, List<Integer>> transformToInnerMap(List<String> labels, 
                                                              List<String> data) 
    Stream<List<Integer>> values = data.stream()
        .skip(4) //skip fields that do not belong to dates
        .map(Integer::valueOf)
        .map(Collections::singletonList); //convert to immutable list
    
    return toMap(labels.stream(), values);

【讨论】:

以上是关于将值放入地图的内部地图的主要内容,如果未能解决你的问题,请参考以下文章

如何插入向量图。地图<int,矢量<int>>地图;我想将值一一插入到向量中

将值映射到色标

将值映射到色标

将值动态注入 Terraform 中的变量

将值从代码隐藏传递给 js

为啥不将值添加到 NSMutableArray?