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'}]}
