My inputs are :
List<Map<String, String>> = [{1=a, 2=b, 3=c},
{1=d, 2=e, 3=f}]
List<String> = [x, y, z]
Expected Output is :
[
{1=a, 2=b, 3=c, 4=x},
{1=a, 2=b, 3=c, 4=y},
{1=a, 2=b, 3=c, 4=z},
{1=d, 2=e, 3=f, 4=x},
{1=d, 2=e, 3=f, 4=y},
{1=d, 2=e, 3=f, 4=z}
]
My Code :
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ExplodeData {
public static void main(String[] args) {
List<Map<String, String>> listOfMap = List.of(
new HashMap<>(Map.of("1", "a", "2", "b", "3", "c")),
new HashMap<>(Map.of("1", "d", "2", "e", "3", "f"))
);
List<String> stringList = new ArrayList<>();
stringList.add("x");
stringList.add("y");
stringList.add("z");
listOfMap.forEach(System.out::println);
System.out.println(stringList "\n\n");
List<Map<String, String>> result = new ArrayList<>();
for (Map<String, String> eachMap: listOfMap) {
for (String eachString: stringList) {
Map<String, String> newEntry = new HashMap<>();
newEntry.putAll(eachMap);
newEntry.put("4", eachString);
result.add(newEntry);
}
}
System.out.println("Expected Result using for loops");
result.forEach(System.out::println);
List<Map<String, String>> result2 = stringList
.stream()
.flatMap(each -> listOfMap.stream().peek(entry -> entry.put("4", each)))
.collect(Collectors.toList());
System.out.println("\n\nResult using streams");
result2.forEach(System.out::println);
}
}
Problem: I want to convert my for loop which gives the correct result into stream. My current stream code gives the correct size of result i.e. (listOfMap * stringList) but the value for the new key is overridden due to shallow copy.
Current output
Expected Result using for loops
{1=a, 2=b, 3=c, 4=x}
{1=a, 2=b, 3=c, 4=y}
{1=a, 2=b, 3=c, 4=z}
{1=d, 2=e, 3=f, 4=x}
{1=d, 2=e, 3=f, 4=y}
{1=d, 2=e, 3=f, 4=z}
Result using streams
{1=a, 2=b, 3=c, 4=z}
{1=d, 2=e, 3=f, 4=z}
{1=a, 2=b, 3=c, 4=z}
{1=d, 2=e, 3=f, 4=z}
{1=a, 2=b, 3=c, 4=z}
{1=d, 2=e, 3=f, 4=z}
Your help is appreciated.
CodePudding user response:
You can use flatMap()
with a function that generates a stream of new maps, much like the loop version does, and collect everything back into a list. Your stream version modifies existing maps in-place, and keeps overwriting previously added "4"
elements with new ones.
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Demo {
public static void main(String[] args) {
List<Map<String, String>> listOfMap =
List.of(Map.of("1", "a", "2", "b", "3", "c"),
Map.of("1", "d", "2", "e", "3", "f"));
List<String> stringList = List.of("x", "y", "z");
List<Map<String, String>> result =
listOfMap.stream()
.flatMap(map -> stringList.stream().map(elem -> {
Map<String, String> newMap = new HashMap<>(map);
newMap.put("4", elem);
return newMap;
}))
.collect(Collectors.toList());
for (Map<String, String> elem : result) {
System.out.println(elem);
}
}
}
outputs
{1=a, 2=b, 3=c, 4=x}
{1=a, 2=b, 3=c, 4=y}
{1=a, 2=b, 3=c, 4=z}
{1=d, 2=e, 3=f, 4=x}
{1=d, 2=e, 3=f, 4=y}
{1=d, 2=e, 3=f, 4=z}
CodePudding user response:
Here is one way which avoids using flatMap
.
List<Map<String, String>> map =
List.of(Map.of("1", "a", "2", "b", "3", "c"),
Map.of("1", "d", "2", "e", "3", "f"));
List<String> list = List.of("x", "y", "z");
- use
mapmulti
to create a new map and modify it with the new elements - then put each new map on the stream
- and collect into a list.
List<Map<String, String>> newList = map.stream()
.<Map<String, String>>mapMulti((mp, consumer) -> {
for (String v : list) {
Map<String, String> newMap =
new HashMap<>(mp);
newMap.put("4", v);
consumer.accept(newMap);
}
}).toList();
for (Map<?,?> nmap : newList) {
System.out.println(nmap);
}
prints
{1=a, 2=b, 3=c, 4=x}
{1=a, 2=b, 3=c, 4=y}
{1=a, 2=b, 3=c, 4=z}
{1=d, 2=e, 3=f, 4=x}
{1=d, 2=e, 3=f, 4=y}
{1=d, 2=e, 3=f, 4=z}
mapMulti was introduced in Java 16.