Java 8 groupby one field and then maps to multiple fields
I have an object structure like this
class Person{ String userId; String firstName; String lastName; Set<Courses> courses = new HashSet<Courses>(); }
The database record is like this (in fact, the users firstname and LastName are from another table, but for simplicity, I will be as follows):
user1 John Smith course1 user1 John Smith course2 user1 John Smith course3 user2 Jack Smith course1 user2 Jack Smith course2
Retrieve the results from the database to the list < map < string, Object > > result set
Now I need to group by userid, then map the course to set and create a list < person > object
Now I can group by userid and collect courses into the collection, but I can't map firstname and LastName
Map<Object,Set<Object>> userList = resultSet.stream().collect() .Collectors.groupingBy( usr -> usr.get("user_id"),Collectors.mapping( usr -> usr.get("courses"),Collectors.toSet()) )); // Result {user1=[course1,course2,course3]}
Then I'm creating a person object
List<Person> = userList.entrySet.stream(). .map( usr -> new Person(usr.getKey().toString(),(Set<Courses)(Set<?>)usr.getValue())) .collect(Collectors.toList())
How do I get firstname, LastName in the grouping by step and use it to create objects?
Solution
You can also try to define a custom collector passed as a downstream function to collectors groupingBy(). Consider the following examples:
import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collector; import java.util.stream.Collectors; public class PersonGroupByExample { public static void main(String[] args) { final List<Map<String,Object>> input = Arrays.asList( new HashMap<String,Object>(){{ put("userId","user1"); put("firstName","John"); put("lastName","Smith"); put("courses","course1"); }},new HashMap<String,"course2"); }},"course3"); }},"user2"); put("firstName","Jack"); put("lastName","course2"); }} ); final Collection<Person> result = input.stream() .parallel() .collect(Collectors.groupingBy(it -> it.get("userId"),Collector.of( // Start with an empty Person object Person::new,// Collect a list of map objects grouped by the same userId into a single Person object (person,map) -> { // Override common properties person.setUserId(map.getOrDefault("userId","").toString()); person.setFirstName(map.getOrDefault("firstName","").toString()); person.setLastName(map.getOrDefault("lastName","").toString()); // Add person's course to a courses set person.getCourses().add(new Course(map.getOrDefault("courses","").toString())); },// Combiner function that join partials results (for parallel execution) (person,person2) -> { person.getCourses().addAll(person2.getCourses()); return person; } ))).values(); result.forEach(System.out::println); } static class Person { String userId; String firstName; String lastName; Set<Course> courses = new HashSet<>(); public Person() {} public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Set<Course> getCourses() { return courses; } public void setCourses(Set<Course> courses) { this.courses = courses; } @Override public String toString() { return "Person{" + "userId='" + userId + '\'' + ",firstName='" + firstName + '\'' + ",lastName='" + lastName + '\'' + ",courses=" + courses + '}'; } } static class Course { String id; public Course(String id) { this.id = id; } public String getId() { return id; } public void setId(String id) { this.id = id; } @Override public String toString() { return "Course{" + "id='" + id + '\'' + '}'; } } }
Collectors. Groupingby() groups all entries by userid field, and then uses the downstream function of the custom collector to reduce the list of map < string, Object > Entries grouped by the same userid contain a single person instance of all courses I use person and course POJO to describe the process, and also enter list < map < string, Object > > to describe the conversion process
Finally, we call map The values () method returns collection < person > instead of map < string, person >
Running this sample will create the following output:
Person{userId='user1',firstName='John',lastName='Smith',courses=[Course{id='course1'},Course{id='course3'},Course{id='course2'}]} Person{userId='user2',firstName='Jack',courses=[Course{id='course2'},Course{id='course1'}]}