I am trying to flatten a Big List:
List<List<List<String>>> input
Example of my list:
[[[a,b],[c,b]], [[x],[y]]]`
I want the result to be as follows:
[[a,b,c],[x,y]]
For the duplicates, I will try to use LinkedHashSet, but I can not flatten the list.
List<List<String>> result =
list.stream()
.map(x -> x.stream()
.flatMap(List::stream)
.distinct()
.collect(Collectors.toList()))
.collect(Collectors.toList());
You can do like this:
List<Set<String>> result = new ArrayList<>();
lists.forEach(list -> result.add(list.stream()
.flatMap(List::stream)
.collect(Collectors.toSet())));
or
lists.stream()
.collect(
() -> new ArrayList<Set<String>>(),
(sets, l) -> sets.add(l.stream()
.flatMap(List::stream)
.collect(Collectors.toSet())),
List::addAll);
Not much different, and much simple as in:
List<List<List<String>>> input = List.of(
List.of(List.of("a", "b"), List.of("c", "b")),
List.of(List.of("x"), List.of("y"))
);
List<LinkedHashSet<String>> output = input.stream()
.map(l -> l.stream()
.flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new)))
.collect(Collectors.toList());
Related
I have Map<String,Integer> map. I want to filter and sort the map by key and then get 5 percent of their number , I have such a function:
public List<String> getValuableSubStr(){
List<String> result = new ArrayList<>();
long size = map.entrySet().stream().filter(e -> e.getKey().length() ==3).count();
map.entrySet().stream()
.filter(e -> e.getKey().length() ==3)
.sorted(Map.Entry.<String,Integer>comparingByKey().reversed())
.limit((size*5)/100)
.peek(e -> result.add(e.getKey()));
return result;
}
But after calling the function , I get an empty list , although the map is not empty and forEach are printed normally .What did I do wrong?
peek is not a terminal operation, so it doesn't cause the stream to be evaluated.
Use forEach instead of peek.
Or, better, collect directly into a list.
return map.entrySet().stream()
.filter(e -> e.getKey().length() ==3)
.sorted(Map.Entry.<String,Integer>comparingByKey().reversed())
.limit((size*5)/100)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
Actually, since you're dealing only with keys:
return map.keySet().stream()
.filter(e -> e.length() ==3)
.sorted(Comparator.reverseOrder())
.limit((size*5)/100)
.collect(Collectors.toList());
peek is more useful for debugging. See here:
it's an intermediate operation and we didn't apply a terminal operation
from https://www.baeldung.com/java-streams-peek-api
I would do
public List<String> getValuableSubStr(){
List<String> result = new ArrayList<>();
long size = map.entrySet().stream().filter(e -> e.getKey().length() ==3).count();
return map.entrySet().stream()
.filter(e -> e.getKey().length() ==3)
.sorted(Map.Entry.<String,Integer>comparingByKey().reversed())
.limit((size*5)/100)
.map(a -> a.getKey())
.collect(Collectors.toList());
}
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]}
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())));
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())));
I have a map:
Map<String, Map<Integer, List<Integer>>>
e.g. Map<Name, Map<Id, List<ReferenceId>>>
Outcome:
List<Id>
List<ReferenceId>
I wanna convert this map into two list of Integers. One list contains inner-map keys, and other contains inner-map value (i.e. List<Integer>)
Can anyone tell me how to do this in Java 8 using streams?
I tried this way but got Cast Exception, can not convert String to Integer.
map.values().stream()
.map(m -> m.entrySet()
.stream()
.map(e -> e.getKey())
.collect(Collectors.toList()))
.flatMap(l -> l.stream())
.collect(Collectors.toList());
Map<String, Map<Integer, List<Integer>>> map = ...
List<Integer> keys = map.values() // Collection<Map<Integer, List<Integer>>>
.stream() // Stream<Map<Integer, List<Integer>>>
.map(Map::keySet) // Stream<Set<Integer>>
.flatMap(Set::stream) // Stream<Integer>
.collect(Collectors.toList()); // List<Integer>
List<Integer> values = map.values() // Collection<Map<Integer, List<Integer>>>
.stream() // Stream<Map<Integer, List<Integer>>>
.map(Map::values) // Stream<Collection<List<Integer>>>
.flatMap(Collection::stream) // Stream<List<Integer>>
.flatMap(List::stream) // Stream<Integer>
.collect(Collectors.toList()); // List<Integer>
There is no way, how your code
List<Integer> list = map.values().stream()
.map(m -> m.entrySet().stream()
.map(e -> e.getKey())
.collect(Collectors.toList()))
.flatMap(l -> l.stream())
.collect(Collectors.toList());
can produce a ClassCastException, unless you managed to insert objects of wrong type into the source map via unchecked operation(s) before the Stream operation. Such a situation is called heap pollution and you should compile your entire code with all warnings enabled (javac: use option -Xlint:unchecked) and solve them.
But note that your code is unnecessarily complicated. The chain, .entrySet().stream().map(e -> e.getKey()) is streaming over the entries and mapping to the keys, so you can stream over the keys in the first place, i.e. .keySet().stream(). Then, you are collecting the stream into a List, just to invoke .stream() in the subequent flatMap step, so you can simply use the stream you already have instead:
List<Integer> list = map.values().stream()
.flatMap(m -> m.keySet().stream())
.collect(Collectors.toList());
Alternatively, you can let the collector do all the work:
List<Integer> list = map.values().stream()
.collect(ArrayList::new, (l,m) -> l.addAll(m.keySet()), List::addAll);
Getting the values instead of the keys works similar, but requires another flatMap step to get the List elements:
List<Integer> list = map.values().stream()
.flatMap(m -> m.values().stream().flatMap(List::stream))
.collect(Collectors.toList());
which is equivalent to
List<Integer> list = map.values().stream()
.flatMap(m -> m.values().stream())
.flatMap(List::stream)
.collect(Collectors.toList());
Again, there’s the alternative of letting the collector do all the work:
List<Integer> list = map.values().stream()
.collect(ArrayList::new, (l,m)->m.values().forEach(l::addAll), List::addAll);
or
List<Integer> list = map.values().stream()
.collect(ArrayList::new, (l,m)->m.forEach((k,v)->l.addAll(v)), List::addAll);
If your value is like Map<String,Object>. And your Object is like Map<String,Object>:
Set<String> mapKeys = myMap.entryset() //Set<Entry<String,Object>>
.stream() //Stream<Entry<String,Object>>
.map(m->(Map<String,Object>) m.getValue()) //Stream<Map<String,Object>>
.map(Map::keySet) //Stream<Set<String>>
.flatMap(l->l.stream()) //Stream<String>
.collect(Collectors.toSet())
it works
Adding for one more working scenario
Map<Integer, List<Integer>> existingPacakagesMap = // having value {123=[111, 222, 333], 987=[444, 555, 666]}
Retrieving logic
List<Integer> ListOfAllPacakages= existingPacakagesMap.values().stream().flatMap(List::stream).collect(Collectors.toList());
Result will be
ListOfAllPacakages= [111, 222, 333, 444, 555, 666]