creating Map from List is not giving expected result - java

I have the two list objects as shown below, from which i'm creating the map object.
List<Class1> list1;
List<Class2> list2;
HashMap<String,String> map = new HashMap<>();
for(Class1 one : list1){
if(one.isStatus()){
map.put(one.getID(),one.getName());
}
}
//iterating second list
for(Class2 two : list2){
if(two.isPerformed()){
map.put(two.getID(),two.getName());
}
}
The above code works fine , want the above to be written using streams.
Below is the sample code using streams().
map = list1.stream().filter(one.isStatus()).collect(toMap(lst1 -> lst1.getID(), lst1.getName());
map = list2.stream().filter(...);
But the "map" is not giving the expected result when written using stream() API.

Stream concatenation Stream.concat may be applied here to avoid map.putAll
Map<String, String> map = Stream.concat(
list1.stream()
.filter(Class1::isStatus)
.map(obj -> Arrays.asList(obj.getID(), obj.getName())),
list2.stream()
.filter(Class2::isPerformed)
.map(obj -> Arrays.asList(obj.getID(), obj.getName()))
) // Stream<List<String>>
.collect(Collectors.toMap(
arr -> arr.get(0), // key - ID
arr -> arr.get(1),
(v1, v2) -> v1 // keep the first value in case of possible conflicts
));
The code above uses a merge function (v1, v2) -> v1 to handle possible conflicts when the same ID occurs several times in list1 and/or list2 to keep the first occurrence.
However, the following merge function allows joining all the occurrences into one string value (v1, v2) -> String.join(", ", v1, v2).

I'm not sure what expected result you're not seeing but I created a minimal working example that you should be able to adapt for your own use case.
public class Main {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
Map<Integer, String> personMap = personList.stream()
.filter(Person::isStatus)
.collect(Collectors.toMap(person -> person.id, person -> person.name));
}
private static class Person {
public String name;
public int id;
public boolean isStatus() {
return true;
}
}
}

Try this,
List<Class1> list1;
List<Class2> list2;
Map<String, String> map1 = list1.stream().filter(Class1::isStatus).collect(Collectors.toMap(Class1::getId, Class1::getName));
Map<String, String> map2 = list2.stream().filter(Class2::isPerformed).collect(Collectors.toMap(Class2::getId, Class2::getName));
map1.putAll(map2);

Related

How to filter a HashMap with a value Predicate?

I have the following query header method:
public Map<String, List<String>> query(Predicate<String> valuePredicate)
Before this, I implementated another method with a specific column (label). It was:
public Map<String, List<String>> query(String keySelector,Predicate<String> valuePredicate) {
try {
final List<String> row = frameInfo.get(keySelector);
List<Integer> indices = IntStream.range(0, row.size()).filter(columnIndex -> valuePredicate.test(row.get(columnIndex))).boxed().collect(Collectors.toList());
Map<String, List<String>> auxMap = new HashMap<>();
for (Map.Entry<String, List<String>> entry : frameInfo.entrySet()) {
for (int columnIndex : indices) {
auxMap.putIfAbsent(entry.getKey(), new ArrayList<>());
auxMap.get(entry.getKey()).add(entry.getValue().get(columnIndex));
}
}
return auxMap;
}catch (Exception e){
return null;
}
How could I implementate the new method with just 1 argument (valuePredicate)?
It seems to me that you could do it like so. Since the predicate tests a string from a list which can be streamed, I don't see why you need to iterate the indices.
Stream the entrySet from frameInfo
then flatmap e.getValue() (a list) and apply the predicate
preserve the key and filtered value in a String array
then group based on the key
public Map<String, List<String>> queryAll(Predicate<String> valuePredicate) {
return frameInfo.entrySet().stream()
.flatMap(e -> e.getValue().stream()
.filter(valuePredicate)
.map(s -> new String[] { e.getKey(), s }))
.collect(Collectors.groupingBy(arr -> arr[0],
Collectors.mapping(arr -> arr[1],
Collectors.toList())));
}
I'm tossing this one in as well, it's a rewrite of your existing method.
it simply streams the list for the supplied key, applies the filter and populates the map. Since there is only one key, you could just return a list.
public Map<String, List<String>> query(String keySelector,
Predicate<String> valuePredicate) {
return frameInfo.get(keySelector).stream()
.filter(valuePredicate)
.collect(Collectors.groupingBy(a -> keySelector));
}
If I misunderstood something, let me know and I will try to correct it.

in java I want to return in one line stream to map

I'm trying to improve my code which already works, so here is the thing, I have a method who return a Map:
public Map<String, String> extractPartitionsValues(java.nio.file.Path marketFile) {
Map<String, String> map = new HashMap<>();
for (String p : this.partitions) {
map.put(p, partitionValueFromFilePath(p, marketFile.toString()));
}
return map;
}
I want to retrun this in one line using stream and collectorsToMap but im really noob at this so I read doc and all I found but it still doesn't work, can some body help me with this.
Use Collectors.toMap() with identity() for the key and your calculation for the value:
public Map<String, String> extractPartitionsValues(Path marketFile) {
return partitions.stream()
.collect(toMap(identity(), p -> partitionValueFromFilePath(p, marketFile.toString())));
}
this should work:
Map<String, Object> collect =
partitions.stream().collect(Collectors.toMap(p -> p, p -> partitionValueFromFilePath(p, marketFile.toString())));
Main point here is to get a stream from partitions field.
If partitions is defined as a string array, Arrays.stream should be used: Arrays.stream(this.partitions).
Otherwise, if partitions is a collection (set or list) of strings, it has its methods stream() / parallelStream().
Next, in order to use Collectors.toMap in its simplest form, the partitions field should contain only unique values. This may be enforced by applying .distinct() operation to the stream if partitions is defined as an array or list.
So, the resulting function may look like this:
private String[] partitions;:
public Map<String, String> extractPartitionsValues(Path marketFile) {
String path = marketFile.toString();
return Arrays.stream(this.partitions)
.distinct()
.collect(Collectors.toMap(
p -> p, p -> partitionValueFromFilePath(p, path)
));
}
private List<String> partitions;
public Map<String, String> extractPartitionsValues(Path marketFile) {
String path = marketFile.toString();
return this.partitions.stream()
.distinct()
.collect(Collectors.toMap(
p -> p, p -> partitionValueFromFilePath(p, path)
));
}
private Set<String> partitions;
public Map<String, String> extractPartitionsValues(Path marketFile) {
String path = marketFile.toString();
return this.partitions.stream() // does not contain duplicates
.collect(Collectors.toMap(
p -> p, p -> partitionValueFromFilePath(p, path)
));
}
Or an overloaded toMap with a merge function may be used to handle possible duplications of keys:
private Collection<String> partitions; // could be set or list
public Map<String, String> extractPartitionsValues(Path marketFile) {
String path = marketFile.toString();
return this.partitions.stream()
.collect(Collectors.toMap(
p -> p,
p -> partitionValueFromFilePath(p, path),
(p1, p2) -> p1 // use the first key
));
}

Java 8 Join Map with Collectors.toMap

I'm trying to collect in a Map the results from the process a list of objects and that it returns a map. I think that I should do it with a Collectors.toMap but I haven't found the way.
This is the code:
public class Car {
List<VersionCar> versions;
public List<VersionCar> getVersions() {
return versions;
}
}
public class VersionCar {
private String wheelsKey;
private String engineKey;
public String getWheelsKey() {
return wheelsKey;
}
public String getEngineKey() {
return engineKey;
}
}
process method:
private static Map<String,Set<String>> processObjects(VersionCar version) {
Map<String,Set<String>> mapItems = new HashMap<>();
mapItems.put("engine", new HashSet<>(Arrays.asList(version.getEngineKey())));
mapItems.put("wheels", new HashSet<>(Arrays.asList(version.getWheelsKey())));
return mapItems;
}
My final code is:
Map<String,Set<String>> mapAllItems =
car.getVersions().stream()
.map(versionCar -> processObjects(versionCar))
.collect(Collectors.toMap()); // here I don't know like collect the map.
My idea is to process the list of versions and in the end get a Map with two items: wheels and engine but with a set<> with all different items for all versions. Do you have any ideas as can I do that with Collectors.toMap or another option?
The operator you want to use in this case is probably "reduce"
car.getVersions().stream()
.map(versionCar -> processObjects(versionCar))
.reduce((map1, map2) -> {
map2.forEach((key, subset) -> map1.get(key).addAll(subset));
return map1;
})
.orElse(new HashMap<>());
The lambda used in "reduce" is a BinaryOperator, that merges 2 maps and return the merged map.
The "orElse" is just here to return something in the case your initial collection (versions) is empty.
From a type point of view it gets rid of the "Optional"
You can use Collectors.toMap(keyMapper, valueMapper, mergeFunction). Last argument is used to resolve collisions between values associated with the same key.
For example:
Map<String, Set<String>> mapAllItems =
car.getVersions().stream()
.map(versionCar -> processObjects(versionCar))
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(firstSet, secondSet) -> {
Set<String> result = new HashSet<>();
result.addAll(firstSet);
result.addAll(secondSet);
return result;
}
));
To get the mapAllItems, we don't need and should not define processObjects method:
Map<String, Set<String>> mapAllItems = new HashMap<>();
mapAllItems.put("engine", car.getVersions().stream().map(v -> v.getEngineKey()).collect(Collectors.toSet()));
mapAllItems.put("wheels", car.getVersions().stream().map(v -> v.getWheelsKey()).collect(Collectors.toSet()));
Or by AbstractMap.SimpleEntry which is lighter than the Map created byprocessObjects`:
mapAllItems = car.getVersions().stream()
.flatMap(v -> Stream.of(new SimpleEntry<>("engine", v.getEngineKey()), new SimpleEntry<>("wheels", v.getWheelsKey())))
.collect(Collectors.groupingBy(e -> e.getKey(), Collectors.mapping(e -> e.getValue(), Collectors.toSet())));

Searching a Hashmap for values of type list, retrieving an element of list and adding it to a new Collection to return

i'm pretty new when it comes to Java but i'll hopefully clear this up.
I currently have one class and within that class i have a TreeMap called "departments" which takes an argument of >:
TreeMap <String, List<String>> department;
Within each department such as HR, Builders etc there are a list of names of people who work there. Such as:
HR: Janet, Jones, Bob
What i'd like to do is search through department to find all departments (keys) that contain someone who's called "bob" for instance and add them to a collection to make a return.
Can anyone help with this, i've been pulling my hair out for a few days! So far i'm this far with the method although clearly nowhere near complete!
public List<String> selectValues( String... aValue)
{
for(String eachDept : department.keySet()){
Collection<String> peopleInTheDept = department.get(eachDept);
for(String person : aValue){
if(peopleInTheDept.contains(person)){
result.add(person);
}
}
}
System.out.println(result);
}
Just like OH GOD SPIDERS predicted, there is a Java 8 stream solution:
TreeMap <String, List<String>> department = new TreeMap<>();
department.put("AB", Arrays.asList("Bob", "Truus", "Miep"));
department.put("CD", Arrays.asList("Jan", "Kees", "Huub"));
department.put("EF", Arrays.asList("Jan", "Piet", "Bert"));
String aValue = "Jan";
Map<String,List<String>> result = department.entrySet().stream()
// filter out those departments that don't contain aValue
.filter(entry -> entry.getValue().contains(aValue))
// collect the matching departments back into a map
.collect(Collectors.toMap(k -> k.getKey(), k -> k.getValue()));
// print the result
result.forEach((k,v)-> System.out.println(k + " " + v.toString()));
Which prints:
EF [Jan, Piet, Bert]
CD [Jan, Kees, Huub]
Pre Java 8 solution:
Map<String, List<String>> result2 = new TreeMap<>();
for(String eachDept : department.keySet()){
List<String> peopleInTheDept = department.get(eachDept);
if(peopleInTheDept.contains(aValue)){
result2.put(eachDept, department.get(eachDept));
}
}
for (String s : result2.keySet()){
System.out.println(s + " " + result2.get(s));
}
This prints exactly the same as my Java 8 code.
Here is some java 8 Streams fancy solution to get the information and fill your list.
public void selectValues(String... values)
{
for(String value : values) {
results.addAll(department.entrySet()
.stream()
.filter(entry -> entry.getValue().contains(value))
.map(entry -> entry.getKey())
.collect(Collectors.toList()));
}
}
What I understand that you need a list of department who's Value contain certain name. Here is the solution of Java 8 version.
public static void main(String[] args) {
// Initialization of map
Map<String, List<String>> department = new TreeMap<>();
department.put("HR", new ArrayList<>());
department.put("ACC", new ArrayList<>());
department.put("MK", new ArrayList<>());
department.get("HR").add("bob");
department.get("HR").add("John");
department.get("ACC").add("Kim");
department.get("ACC").add("bob");
department.get("MK").add("XXX");
// Here is the solution
// depts is a list of string which contains the name of
// department who's value contains 'bob'
List<String > depts = department.entrySet()
.stream()
.filter(i -> i.getValue().contains("bob"))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
// Printing out the result
depts.stream().forEach(System.out::println);
}
In traditional way
List<String> depts = new ArrayList<>();
for (Map.Entry<String , List<String >> it :
department.entrySet()) {
if (it.getValue().contains("bob"))
depts.add(it.getKey());
}
Java8's streams is certainly an elegant solution...but if you are restricted with the use of java 8 then how about your departments be of type TreeMap<String, Set<String>>. If you insist in using a list its very similiar. Just change the Set to List. There is a contains method in List interface too.
TreeMap<String, List<String>> departments = new TreeMap<String, List<String>>();
ArrayList<String> myList = new ArrayList<String>();
myList.add("person1");
departments.put("dep1", myList);
String[] aValue = {"person1","person2"};
public void selectValues( String... aValue)
{
for(String eachDept : departments.keySet()){
List<String> peopleInTheDept = departments.get(eachDept);
for(String person : aValue){
if(peopleInTheDept.contains(person)){
result.add(person)
}
}
}
System.out.println(result);
}
Something similar to this maybe?

Java 8 Merge maps in iterator

I have an iteraror where in every iteration I´m creating a new map
Map<String, List<String>>
Now I would like to merge in every iteration the last emitted map with the new one.
If I send a list of items to getMap
{"a","a","b"}
I expect to receive a map of
["a",{"foo:a", "foo:a"}, "b",{"foo:b"}]
I try to use reduce function, but because putall only works if I use multimap and not map, is not a good option.
Here my code
public Map<String, List<String>> getMap(List<String> items){
return items().stream()
.map(item -> getNewMap(item) --> Return a Map<String, List<String>>
.reduce(new HashMap<>(), (o, p) -> {
o.putAll(p);
return o;
});
}
public Map<String, List<String>> getNewMap(String item){
Map<String, List<String>> map = new HashMap<>();
map.put(item, Arrays.asList("foo:" + item));
return map;
}
I´m looking for a no verbose way to do it.
What you want is to flat map each intermediate map to its entries and make a single map out of that.
In the following code, each item is mapped to its corresponding map. Then, each map is flat mapped to its entries and the Stream is collected into a map.
public static void main(String[] args) {
System.out.println(getMap(Arrays.asList("a", "a", "b")));
// prints "{a=[foo:a, foo:a], b=[foo:b]}"
}
public static Map<String, List<String>> getMap(List<String> items) {
return items.stream()
.map(item -> getNewMap(item))
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(l1, l2) -> { List<String> l = new ArrayList<>(l1); l.addAll(l2); return l; }
));
}
public static Map<String, List<String>> getNewMap(String item) {
Map<String, List<String>> map = new HashMap<>();
map.put(item, Arrays.asList("foo:" + item));
return map;
}
In the case of multiple keys, this appends each list together.
Whenever you want to get a Map<…, List<…>> from a stream, you should first check, how the groupingBy collector fits in. In its simplest form, it receives a grouping function which determines the keys of the resulting map and will collect all elements of a group into a list. Since you want the prefix "foo:" prepended, you’ll have to customize this group collector by inserting a mapping operation before collecting the items into a list:
public static Map<String, List<String>> getMap(List<String> items) {
return items.stream().collect(Collectors.groupingBy(
Function.identity(),
Collectors.mapping("foo:"::concat, Collectors.toList())));
}
The classification function itself is as trivial as the identity function, as you want all equal elements building one group.

Categories

Resources