I have below class
public class Data {
String name;
String dateOfBirth;
String dateAttendedClass;
}
I have a list of Data and wanted to create a map of below:
Map<String Map<String, List<String>>>
Map<dateOfBirth Map<name,list(dateAttendedClass)>>
I managed to create a Map of
Map<name,list(dateAttendedClass)>
by doing this
Map<String, List<String>> nameAttendedClassMap = data.stream()
.collect(Collectors.groupingBy(Data::getName,
Collectors.mapping(Data::getDateAttended, Collectors.toList())));
But I am not sure how to collect this map further and have it in a map with the key being date of birth.
Map<String Map<String, List<String>>>
As suggested by others, you should nest your groupingBy
calls.
Here is a working example. You should store your dates as epoch values. This is the best way to store the values. They are more transportable and universal this way. If you want to display any information, you can call convenience methods to format. When a date is represented as an integer, you can specify what locale you want to use to format the date. There is also no need to pare the dates when comparing them when sorting; or comparing in general.
Also, name your objects appropriately. Changing the name of Data
to Student
helps identify what the object represents.
package org.example;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import com.google.gson.GsonBuilder; // Used for printing formatted JSON
public class DataMap {
private static final DateTimeFormatter dateFormatter;
private static final DateTimeFormatter yearFormatter;
static {
dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
yearFormatter = DateTimeFormatter.ofPattern("yyyy");
}
public static void main(String[] args) {
List<Student> data = new ArrayList<Student>();
data.add(createStudent("Billy", "1990-01-30", "2005-08-28"));
data.add(createStudent("Billy", "1990-01-30", "2006-08-28"));
data.add(createStudent("Billy", "1990-01-31", "2007-08-28"));
data.add(createStudent("Billy", "1990-01-31", "2008-08-28"));
data.add(createStudent("Alice", "1990-01-30", "2005-08-28"));
data.add(createStudent("Alice", "1990-01-30", "2006-08-28"));
data.add(createStudent("Alice", "1990-01-31", "2007-08-28"));
data.add(createStudent("Alice", "1990-01-31", "2008-08-28"));
Map<String, Map<String, List<String>>> grouped = groupAttendanceByBirthAndName(data);
System.out.println(grouped);
// For display purposes...
System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(grouped).toString());
}
private static Map<String, Map<String, List<String>>> groupAttendanceByBirthAndName(List<Student> students) {
return students.stream()
.collect(Collectors.groupingBy(Student::getDateOfBirthAsString,
Collectors.groupingBy(Student::getName,
Collectors.mapping(Student::getDateAttendedClassAsString, Collectors.toList()))));
}
private static String formatEpochMilliAsDate(long epochMilli, DateTimeFormatter dateFormatter) {
return dateFormatter.format(Instant.ofEpochMilli(epochMilli).atZone(ZoneId.systemDefault()).toLocalDate());
}
private static long epochFromDate(String date) {
return LocalDate.parse(date).atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
private static Student createStudent(String name, String birthYear, String attendanceYear) {
return new Student(name, epochFromDate(birthYear), epochFromDate(attendanceYear));
}
private static class Student {
private String name;
private long dateOfBirth;
private long dateAttendedClass;
public Student(String name, long dateOfBirth, long dateAttendedClass) {
this.name = name;
this.dateOfBirth = dateOfBirth;
this.dateAttendedClass = dateAttendedClass;
}
public String getName() {
return name;
}
public long getDateOfBirth() {
return dateOfBirth;
}
// Convenience method ~ Used for grouping and display
public String getDateOfBirthAsString() {
return formatEpochMilliAsDate(dateOfBirth, dateFormatter);
}
public long getDateAttendedClass() {
return dateAttendedClass;
}
// Convenience method ~ Used for grouping and display
public String getDateAttendedClassAsString() {
return formatEpochMilliAsDate(dateAttendedClass, yearFormatter);
}
}
}
Raw Map
output:
{1990-01-31={Billy=[2007, 2008], Alice=[2007, 2008]}, 1990-01-30={Billy=[2005, 2006], Alice=[2005, 2006]}}
I used Google's Gson library to pretty-print the nested map as JSON. You could use Jackson's ObjectMapper
as well.
{
"1990-01-31": {
"Billy": [
"2007",
"2008"
],
"Alice": [
"2007",
"2008"
]
},
"1990-01-30": {
"Billy": [
"2005",
"2006"
],
"Alice": [
"2005",
"2006"
]
}
}