Convierta el tipo X en Y en el Mapa<K, Mapa<V, X>> usando la API de Java Stream
stream map java (9)
Quiero convertir mapa interno de mapa de mapas.
Mapa antiguo: Map<String, Map<LocalDate, Integer>>
Entero significa segundos
Nuevo mapa: Map<String, Map<LocalDate, Duration>>
He intentado crear un nuevo mapa interno, pero obtuve un error
Error: java: no se ha encontrado un método adecuado para el método
putAll(java.util.stream.Stream<java.lang.Object>)
java.util.Map.putAll(java.util.Map<? extends java.time.LocalDate,? extends java.time.Duration>)
no es aplicable
oldMap.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey,
e -> new HashMap<LocalDate, Duration>() {{
putAll(
e.getValue().entrySet().stream()
.map(x -> new HashMap.SimpleEntry<LocalDate, Duration>
(x.getKey(), Duration.ofSeconds(x.getValue())))
);
}}
));
Aquí está la solución por StreamEx :
EntryStream.of(oldMap)
.mapValues(v -> EntryStream.of(v).mapValues(Duration::ofSeconds).toMap())
.toMap();
Mis dos centavos, cree un método para transformar el Map<K, V1>
en un Map<K, V2>
:
public static <K,V1,V2> Map<K, V2> transformValues(final Map<K, V1> input, final Function<V1,V2> transform) {
Function<Map.Entry<K, V1>, V2> mapper = transform.compose(Map.Entry::getValue);
return input.entrySet().stream()
.collect(toMap(Map.Entry::getKey, mapper));
}
Entonces su código se convierte en:
Map<String, Map<LocalDate, Duration>> transformed
= transformValues(maps, map -> transformValues(map, Duration::ofSeconds));
Para completar, aquí hay una versión que usa Maps.transformValues
de Guava:
Map<String, Map<LocalDate, Duration>> result =
Maps.transformValues(oldMap, m -> Maps.transformValues(m, Duration::ofSeconds));
Preferiría iterar sobre las entradas de su mapa antiguo y transmitir en el mapa interno:
for (Entry<String, Map<LocalDate, Integer>> entry : oldMap.entrySet()) {
Map<LocalDate, Duration> asDuration = entry.getValue().entrySet().stream()
.collect(Collectors.toMap(e -> e.getKey(), e -> Duration.ofSeconds(e.getValue().longValue())));
newMap.put(entry.getKey(), asDuration);
}
De lo contrario, necesita una segunda transmisión dentro de su collect
:
newMap = oldMap.entrySet().stream()
.collect(Collectors.toMap(s -> s.getKey(), s -> s.getValue().entrySet().stream()
.collect(Collectors.toMap(e -> e.getKey(), e -> Duration.ofSeconds(e.getValue().longValue())))));
Rapido y limpio
HashMap<String, HashMap<LocalDate, Duration>> newMap = new HashMap<>();
oldHashMap.forEach((key, innerMap) -> {
HashMap<LocalDate, Duration> newStuff = new HashMap<>();
innerMap.forEach((k2,v2) -> newStuff.put(k2,Duration.ofSeconds(v2)));
newMap.put(key, newStuff);
});
Si entiendo que está en lo correcto, puede ver el siguiente código:
oldMap.entrySet().stream()
.collect(Collectors.toMap(x -> x.getKey(),
x -> {
Map<LocalDate, Duration> temp = new HashMap<>();
x.getValue().forEach((k, v) -> {
temp.put(k, Duration.ofSeconds(v));
});
return temp;
})
);
Si quieres código compacto, puedes usar
Map<String, Map<LocalDate, Duration>> newMap = new HashMap<>();
oldMap.forEach((s,o) -> o.forEach((d, i) ->
newMap.computeIfAbsent(s, x->new HashMap<>()).put(d, Duration.ofSeconds(i))));
Si desea evitar operaciones de hash innecesarias, puede ampliarlo un poco.
Map<String, Map<LocalDate, Duration>> newMap = new HashMap<>();
oldMap.forEach((s,o) -> {
Map<LocalDate, Duration> n = new HashMap<>();
newMap.put(s, n);
o.forEach((d, i) -> n.put(d, Duration.ofSeconds(i)));
});
Y uno más ...
Map<String, Map<LocalDate, Duration>> newMap = map.entrySet().stream().collect(
Collectors.toMap(Entry::getKey,
entry -> entry.getValue().entrySet().stream().collect(
Collectors.toMap(Entry::getKey, e -> Duration.ofSeconds(e.getValue())))));
toMap
un método toMap
a toMap
para simplificar el problema.
Map<String, Map<LocalDate, Duration>> result = oldMap.entrySet().stream()
.map(entry -> new AbstractMap.SimpleEntry<>(
entry.getKey(),
entry.getValue().entrySet().stream()
.collect(toMap(it -> Duration.ofSeconds(it.longValue())))
// ^--- mapping Integer to Duration
)).collect(toMap(Function.identity()));
<K, V, R> Collector<Map.Entry<K, V>, ?, Map<K, R>> toMap(Function<V, R> mapping) {
return Collectors.toMap(
Map.Entry::getKey,
mapping.compose(Map.Entry::getValue)
);
}
Y puede simplificar el código aún más, ya que la lógica primaria está duplicada: adapta un valor a otro valor en un Map
.
Function<
Map<String, Map<LocalDate, Integer>>,
Map<String, Map<LocalDate, Duration>>
> mapping = mapping(mapping(seconds -> Duration.ofSeconds(seconds.longValue())));
// | |
// | adapts Map<LocalDate, Integer> to Map<LocalDate,Duration>
// |
//adapts Map<String,Map<LocalDate,Integer>> to Map<String,Map<LocalDate,Duration>>
Map<String, Map<LocalDate, Duration>> result = mapping.apply(oldMap);
<K, V1, V2> Function<Map<K, V1>, Map<K, V2>> mapping(Function<V1, V2> mapping) {
return it -> it.entrySet().stream().collect(toMap(mapping));
}
ENTONCES , puede usarlo en cualquier lugar que necesite una Function<Map<?,?>,Map<?,?>>
para adaptar el valor a otro valor, por ejemplo:
Map<String, Map<LocalDate, Duration>> result = Optional.of(oldMap)
.map(mapping(mapping(seconds -> Duration.ofSeconds(seconds.longValue()))))
.get();