将值放入地图的内部地图

Put the value into the inner map of a map

我有地图的地图:Map<String,<LocalDate,List<Integer>>。在循环的每次迭代中,我需要将List<List<String>中每个国家的每日案例插入到每个day.Each天中,并且仅限于一条记录。但我目前的尝试失败了,因为它将在 table.

的第一个日期附加所有列表

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));

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

我将假定带有签名 similar to the one found in Guavazip 实现可用:

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);
}