Split list of string to list of map - java

I have the following list
[12_223,13_4356,15_5676]
I was able to spilt on underscore and convert this into one Hashmap using the below code
list.stream()
.map(s -> s.split("_"))
.collect(Collectors.toMap(
a -> a[0],
a -> a[1]));
it gives the below map
{"12"="223", "13"="4356", "15"="5676"}
But I wanted to change this code so that it gives me a list of maps like below as I might encounter duplicate keys while splitting
[{"12"="223"}, {"13"="4356"}, {"15"="5676"}]

If you really want the result to be a list of maps, I'd do it this way:
List<Map<String, String>> listOfMaps(List<String> list) {
return list.stream()
.map(s -> s.split("_", 2))
.map(a -> Collections.singletonMap(a[0], a[1]))
.collect(toList());
}
(HT: Eugene for recommending split("_", 2).)
In Java 9, you could do it this way:
List<Map<String, String>> listOfMaps(List<String> list) {
return list.stream()
.map(s -> s.split("_", 2))
.map(a -> Map.of(a[0], a[1]))
.collect(toList());
}
In both cases, the map instances in the resulting list will be unmodifiable.

This does what you are looking for
List<Map<String, String>> output = list.stream().map(
s-> {
Map<String, String> element = new HashMap<>();
String[] arr = s.split("_");
element.put(arr[0], arr[1]);
return element;
}).collect(Collectors.toList());
But as #Pshemo suggests, this should be more appropriate
Map<String, List<String>> outputnew = list.stream().map(
s -> s.split("_"))
.collect(Collectors.groupingBy(s -> s[0],
Collectors.mapping(s -> s[1], Collectors.toList())));

Related

How to split a string into a map, grouping values by duplicate keys using streams?

I want to convert the following
String flString="view1:filedname11,view1:filedname12,view2:fieldname21";
to a Map<String,Set<String>> to get the key/value as below:
view1=[filedname11,filedname12]
view2=[fieldname21]
I want to use Java 8 streams. I tried
Arrays.stream(tokens)
.map(a -> a.split(":"))
.collect(Collectors.groupingBy(
a -> a[0], Collectors.toList()));
However the keys are also getting added to the value list.
You should use a Collectors::mapping to map the array to an element.
String flString = "view1:filedname11,view1:filedname12,view2:fieldname21";
Map<String, List<String>> map = Pattern.compile(",")
.splitAsStream(flString)
.map(a -> a.split(":"))
.collect(
Collectors.groupingBy(a -> a[0],
Collectors.mapping(a -> a[1], Collectors.toList())
)
);
map.entrySet().forEach(System.out::println);
Output
view1=[filedname11, filedname12]
view2=[fieldname21]
You can use Collectors#toMap(keyMapper,valueMapper,mergeFunction) method as follows:
String flString = "view1:filedname11,view1:filedname12,view2:fieldname21";
Map<String, Set<String>> map = Arrays
.stream(flString.split(","))
.map(str -> str.split(":"))
.collect(Collectors.toMap(
arr -> arr[0],
arr -> new HashSet<>(Set.of(arr[1])),
(s1, s2) -> {
s1.addAll(s2);
return s1;
}));
System.out.println(map);
// {view1=[filedname11, filedname12], view2=[fieldname21]}
Off course #K.Nicholas solution with using downstream collector is perfect.
But I additionally created another alternative solution that also uses Stream API.
It is more complex and generates three streams, but it works too.
String flString = "view1:filedname11,view1:filedname12,view2:fieldname21";
Map<String, Set<String>> map1 =
Arrays.stream(flString.split(","))
.map(a -> a.split(":"))
.collect(Collectors.toMap(
a -> a[0],
a -> a[1],
(a1, a2) -> a1 + "," + a2))
.entrySet().stream()
.collect(Collectors.toMap(
e -> e.getKey(),
e -> Arrays.stream(e.getValue().split(","))
.collect(Collectors.toSet())));
map1.entrySet().forEach(System.out::println);
You can achieve your goal using the following piece of codes:
String flString = "view1:filedname11,view1:filedname12,view2:fieldname21";
Map<String, List<String>> collect = Stream.of(flString)
.flatMap(str -> Stream.of(str.split(",")))
.map(keyValuePair -> keyValuePair.split(":"))
.collect(Collectors.groupingBy(it -> it[0], Collectors.mapping(it -> it[1], Collectors.toList())));
Simple output:
{view1=[filedname11, filedname12], view2=[fieldname21]}

Getting ArraysIndexOutOfBoundException while trying to convert from list to map in java8

I am trying to get Map<String,List<String>> from List<String> using split(), but I'm getting ArrayIndexOutOfBoundException.
List<String> lst = new ArrayList<>();
lst.add(A1);
lst.add(A2);
lst.add(A3);
Map<String, List<String>> test1 = lst.stream()
.map(s -> new AbstractMap.SimpleEntry<String,String>(s.split("[^A-Z]")[0], s.split("[^A-Z]")[1]))
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
results exception.
To avoid the exception, you should filter out arrays that don't have enough elements:
Map<String, List<String>> test1 =
lst.stream()
.map(s -> s.split("[^A-Z]"))
.filter(a -> a.length > 1)
.map(a -> new AbstractMap.SimpleEntry<String,String>(a[0],a[1]))
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())));
BTW, there's no need to create the Map.Entrys. You can directly work with the arrays:
Map<String, List<String>> test1 =
lst.stream()
.map(s -> s.split("[^A-Z]"))
.filter(a -> a.length > 1)
.collect(Collectors.groupingBy(a -> a[0],
Collectors.mapping(a -> a[1],
Collectors.toList())));

Processing a list of Map<String,List<Object>> in java 8

I am trying to create a single map from list of maps. Which contains only key "1" and all the values of key "1" across different maps under that list using Java 8 stream API.
List<Map<String,Object>> list=new ArrayList<>();
Map<String,Object> map1=new HashMap<>();
map1.put("1", Arrays.asList(new String[] {"A"}));
map1.put("2", Arrays.asList(new String[] {"B"}));
Map<String,Object> map2=new HashMap<>();
map2.put("1", Arrays.asList(new String[] {"C"}));
map2.put("2", Arrays.asList(new String[] {"D"}));
Required output :- {1=[A, C]}
Because you have only one entry, then in this case, you need just to focus on the values, and for the key you can just use "1", for that you can create a Map like this :
Map<String, List<String>> result = new HashMap<>();
result.put("1", list.stream()
.filter(e -> e.containsKey("1"))
.flatMap(e -> e.values().stream())
.flatMap(List::stream)
.collect(Collectors.toList()));
Or as stated by Lino in the comment, you can also use :
Map<String, List<String>> result = list.stream()
.filter(e -> e.containsKey("1"))
.flatMap(e -> e.values().stream())
.flatMap(List::stream)
.collect(Collectors.groupingBy(t -> "1"));
As mentioned by #ernest_k you should declare list as: List<Map<String, List<String>>>
You can use a groupingBy collector:
Map<String, List<String>> result = list.stream()
.map(m -> m.get("1"))
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.collect(Collectors.groupingBy(t -> "1"));
You can get rid of the intermediate filter with this. But this may be more confusing (and effectively does the same):
Map<String, List<String>> result = list.stream()
.map(m -> m.get("1"))
.flatMap(l -> l == null ? Stream.empty() : l.stream())
.collect(Collectors.groupingBy(t -> "1"));
You can do:
Map<String, List<Object>> result = list.stream()
.filter(entry -> entry.containsKey("1"))
.map(entry -> (List<Object>) entry.get("1"))
.collect(Collectors.toMap(t1 -> "1",
ArrayList::new, (l1, l2) -> { l1.addAll(l2);return l1; }));
or use groupingBy collector :
Map<String, List<Object>> result2 = list.stream()
.filter(entry -> entry.containsKey("1"))
.flatMap(entry -> ((List<Object>) entry.get("1")).stream())
.collect(Collectors.groupingBy(t->"1",Collectors.
collectingAndThen(Collectors.toList(),ArrayList::new)));

Extract values (not keys) from a List<Map<String,String>>, and flatten it to a List<String>

How would I extract the values (not keys) from a List<Map<String,String>>, and flatten it to a List<String>?
i.e. tried the following but doesn't work.
List<Map<String,String>> mapList = .... ;
List<String> valueList = mapList.stream()
.map(o -> o.getValue())
.collect(Collectors.toList());
I'd like to filter the result by a given key as well.
You mean :
List<String> valueList = mapList.stream()
.flatMap(a -> a.values().stream())
.collect(Collectors.toList());
Edit
What if I want to specify a key e.g. I have "id" and "firstName", but
only want "firstName"
In this case you can use filter after the flatmap like so :
List<String> valueList = mapList.stream()
.flatMap(a -> a.entrySet().stream())
.filter (e -> e.getKey().equals("firstName"))
.map(Map.Entry::getValue)
.collect(Collectors.toList ());
Use .flatMap:
List<Map<String,String>> mapList = new ArrayList<>();
Map<String, String> mapOne = new HashMap<>();
mapOne.put("1", "one");
mapOne.put("2", "two");
Map<String, String> mapTwo = new HashMap<>();
mapTwo.put("3", "three");
mapTwo.put("4", "four");
mapList.add(mapOne);
mapList.add(mapTwo);
List<String> allValues = mapList.stream()
.flatMap(m -> m.values().stream())
.collect(Collectors.toList()); // [one, two, three, four]
Try
List<String> valueList = mapList.stream()
.flatMap(map -> map.entrySet().stream())
.filter(entry -> entry.getKey().equals("KEY"))
.map(Map.Entry::getValue)
.collect(Collectors.toList());
The object o you are trying to map to o.getValue() is of type Map (which does not have a function getValue()), not Map.Entry (which would have such a function). What you can is get a Collection of the values with the function o.values().
You can then get a Stream from that collection, and flatten the resulting Stream of Streams like this:
List<String> valueList = mapList.stream()
.map(o -> o.values().stream())
.flatMap(Function.identity())
.collect(Collectors.toList());

List of Strings to hashmap using Streams

I have a "," separated String array like this
a b c d,
f b h j,
l p o i,
I would like this to be converted to a Hashmap like
HashMap<String, List<String>> such that second element in list (delimited by space becomes key and the 3rd element becomes value)
So,
This should become
b -> c,h
p -> o
I want to use Streams API and I think this is the way to go:
List<String> entries = new ArrayList<>();
HashMap<String, List<String>> map = new HashMap<>();
HashMap<String, List<String>> newMap = entries.stream()
.collect(line -> {
if (map.contains(line.split(" ")[1])) {
// Get existing list and add the element
map.get(line.split(" ")[1].add(line.split(" ")[1]));
} else {
// Create a new list and add
List<String> values = new ArrayList<>();
values.add(line.split(" ")[1]);
map.put(line.split(" ")[0], values);
}
});
Is there any better way? How exactly should I return Hashmap from collect function?
You can use the Collectors.groupingBy as shown below to group the inputs (follow the inline comments):
String[] inputs = {"a b c d,", "f b h j,", "l p o i,"};
Map<String, List<String>> results =
Arrays.stream(inputs).map(s -> s.split(" ")).//splt with space
collect(Collectors.groupingBy(arr -> arr[1], // Make second element as the key
Collectors.mapping(arr -> arr[2], // Make third element as the value
Collectors.toList())));//collect the values to List
System.out.println(results);
Output:
{p=[o], b=[c, h]}
I suggest you read the API here to understand how Collectors.groupingBy along with Collectors.mappingworks.
You can achieve the task at hand using a groupingBy collector along with Collectors.mapping as a downstream collector.
Map<String, List<String>> collect =
myList.stream()
.map(s -> s.split(" "))
.collect(Collectors.groupingBy(a -> a[1],
Collectors.mapping(a -> a[2], Collectors.toList())));
output:
{p=[o], b=[c, h]}
if you want to maintain insertion order then you can specify a LinkedHashMap like this:
Map<String, List<String>> collect =
myList.stream()
.map(s -> s.split(" "))
.collect(Collectors.groupingBy(s -> s[1],
LinkedHashMap::new,
Collectors.mapping(s -> s[2], Collectors.toList())));
output:
{b=[c, h], p=[o]}
If you want HashMap , not just any Map
HashMap<String, List<String>> output =myList.stream().map(s -> s.split(" "))
.collect(Collectors.groupingBy((s) -> s[1],
HashMap::new,
Collectors.mapping(
(s) -> s[2],
Collectors.toList())));

Categories

Resources