java dictionary java-8 java-stream collectors

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