将值放入地图的内部地图
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 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);
}
我有地图的地图: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 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);
}