I have a some data in Java which is in the format Map<String, Map<String, Object>>
Let's say we have 2 countries and also assume that we have weekly data for each country, which means the last 60 days will have a data of something like this:
{
"US”= {
"2023-01-02”={
"customerViews"= 2500,
"productSales"= 1200,
"noOfUnitsSold"= 600,
"conversion"= 0.24
},
"2023-01-09”={
"customerViews"= 2900,
"productSales"= 1400,
"noOfUnitsSold"= 700,
"conversion"= 0.24
},
"2023-01-16”= {
"customerViews"= 2000,
"productSales"= 1000,
"noOfUnitsSold"= 500,
"conversion"= 0.25
},
"2023-02-23”= {
"customerViews"= 2700,
"productSales"= 1300,
"noOfUnitsSold"= 650,
"conversion"= 0.22
},
"2023-01-30”={
"customerViews"= 1800,
"productSales"= 900,
"noOfUnitsSold"= 450,
"conversion"= 0.21
},
"2023-02-06”= {
"customerViews"= 2300,
"productSales"= 1100,
"noOfUnitsSold"= 550,
"conversion"= 0.23
},
"2023-02-13”= {
"customerViews"= 2000,
"productSales"= 1000,
"noOfUnitsSold"= 500,
"conversion"= 0.25
},
"2023-02-20”= {
"customerViews"= 2500,
"productSales"= 1200,
"noOfUnitsSold"= 600,
"conversion"= 0.24
}
},
"CA”= {
"2023-01-02”= {
"customerViews"= 2000,
"productSales"= 1000,
"noOfUnitsSold"= 500,
"conversion"= 0.24
},
"2023-01-23”= {
"customerViews"= 2200,
"productSales"= 1100,
"noOfUnitsSold"= 550,
"conversion"= 0.22
},
"2023-01-30”={
"customerViews"= 1800,
"productSales"= 900,
"noOfUnitsSold"= 450,
"conversion"= 0.22
},
"2023-02-06”= {
"customerViews"= 1700,
"productSales"= 850,
"noOfUnitsSold"= 425,
"conversion"= 0.21
},
"2023-02-13”= {
"customerViews"= 2000,
"productSales"= 1000,
"noOfUnitsSold"= 500,
"conversion"= 0.24
}
}
}
The reason I didn't include few weeks of data for 2nd country is because, there might be a case where there are no data for that week. So in that case there will be no entries for that week.
Now, I want to take this data as a input and convert to a monthly data which in turn the response looks like this:
{
"US"= {
"2023-01"= {
"customerViews"= 12400.0,
"productSales"= 6000.0,
"noOfUnitsSold"= 3000.0,
"conversion"= 1.18
},
"2023-02"= {
"customerViews"= 6300.0,
"productSales"= 3100.0,
"noOfUnitsSold"= 1550.0,
"conversion"= 0.7
}
},
"CA"= {
"2023-01"= {
"customerViews"= 4150.0,
"productSales"= 2050.0,
"noOfUnitsSold"= 1025.0,
"conversion"= 0.45
},
"2023-02"= {
"customerViews"= 3500.0,
"productSales"= 1750.0,
"noOfUnitsSold"= 875.0,
"conversion"= 0.43
}
}
}
This is what I tried:
import java.util.*;
public class WeeklyToMonthlyConverter {
public static Map<String, Map<String, Object>> convertToMonthlyData(Map<String, Map<String, Object>> weeklyData) {
Map<String, Map<String, Object>> monthlyData = new HashMap<>();
for (Map.Entry<String, Map<String, Object>> countryEntry : weeklyData.entrySet()) {
String countryId = countryEntry.getKey();
Map<String, Object> weeklyMap = countryEntry.getValue();
Map<String, Object> monthlyMap = new HashMap<>();
for (Map.Entry<String, Object> weekEntry : weeklyMap.entrySet()) {
String weekDate = weekEntry.getKey();
Object weekData = weekEntry.getValue();
String month = weekDate.substring(0, 7); // Extract the year and month
if (monthlyMap.containsKey(month)) {
// If the month entry exists, sum the values
Map<String, Object> existingMonthData = (Map<String, Object>) monthlyMap.get(month);
sumData(existingMonthData, (Map<String, Object>) weekData);
} else {
// If the month entry does not exist, initialize it with the weekly data
monthlyMap.put(month, weekData);
}
}
monthlyData.put(countryId, monthlyMap);
}
return monthlyData;
}
private static void sumData(Map<String, Object> targetMap, Map<String, Object> sourceMap) {
for (Map.Entry<String, Object> entry : sourceMap.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (value instanceof Number) {
// If the value is numeric, sum it with the existing value
if (targetMap.containsKey(key)) {
Number existingValue = (Number) targetMap.get(key);
double sum = existingValue.doubleValue() + ((Number) value).doubleValue();
targetMap.put(key, sum);
} else {
targetMap.put(key, value);
}
}
}
}
public static void main(String[] args) {
Map<String, Map<String, Object>> weeklyData = new HashMap<>();
Map<String, Object> usWeeklyData = new HashMap<>();
usWeeklyData.put("2023-01-02", createWeeklyObject(1000, 500, 2000, 0.25));
usWeeklyData.put("2023-01-09", createWeeklyObject(1200, 600, 2500, 0.24));
usWeeklyData.put("2023-01-16", createWeeklyObject(1100, 550, 2300, 0.23));
usWeeklyData.put("2023-01-23", createWeeklyObject(1300, 650, 2700, 0.22));
usWeeklyData.put("2023-01-30", createWeeklyObject(1400, 700, 2900, 0.24));
usWeeklyData.put("2023-02-06", createWeeklyObject(900, 450, 1800, 0.21));
usWeeklyData.put("2023-02-13", createWeeklyObject(1000, 500, 2000, 0.25));
usWeeklyData.put("2023-02-20", createWeeklyObject(1200, 600, 2500, 0.24));
weeklyData.put("US", usWeeklyData);
Map<String, Object> caWeeklyData = new HashMap<>();
caWeeklyData.put("2023-01-02", createWeeklyObject(800, 400, 1800, 0.22));
caWeeklyData.put("2023-01-23", createWeeklyObject(950, 475, 1950, 0.23));
caWeeklyData.put("2023-01-30", createWeeklyObject(1100, 550, 2200, 0.22));
caWeeklyData.put("2023-02-06", createWeeklyObject(850, 425, 1700, 0.21));
caWeeklyData.put("2023-02-13", createWeeklyObject(900, 450, 1800, 0.22));
weeklyData.put("CA", caWeeklyData);
System.out.println(weeklyData);
// Convert to monthly data
Map<String, Map<String, Object>> monthlyData = convertToMonthlyData(weeklyData);
System.out.println(monthlyData);
}
private static Map<String, Object> createWeeklyObject(int productSales, int noOfUnitsSold, int customerViews, double conversion) {
Map<String, Object> weeklyObject = new HashMap<>();
weeklyObject.put("productSales", productSales);
weeklyObject.put("noOfUnitsSold", noOfUnitsSold);
weeklyObject.put("customerViews", customerViews);
weeklyObject.put("conversion", conversion);
return weeklyObject;
}
}
I don't have much hand's on experience using lambda and streams for which I think using these concepts, this can be made even more efficiently. Can you please guide me on this one?
Thanks in advance
Using Java Streams you can refer below code
private static Map<String, Map<String, Map<String, Object>>> convertToMonthlyData(Map<String, Map<String, Object>> weeklyData) {
DateTimeFormatter weekFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("yyyy-MM");
Map<String, Map<String, Map<String, Object>>> monthwiseMap = new HashMap<>();
weeklyData.forEach((country, weeklyDataDetail) -> {
Map<String, Map<String, Object>> countryMonthwiseMap = weeklyDataDetail.entrySet().stream()
.collect(Collectors.groupingBy(entry -> YearMonth.parse(entry.getKey(), weekFormatter).format(monthFormatter),
Collectors.mapping(Map.Entry::getValue, Collectors.toList())))
.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> combineWeeklyData(entry.getValue())));
monthwiseMap.put(country, countryMonthwiseMap);
});
return monthwiseMap;
}
private static Map<String, Object> combineWeeklyData(List<Object> weeklyData) {
Map<String, Object> combinedData = new HashMap<>();
int totalCustomerViews = 0;
int totalProductSales = 0;
int totalNoOfUnitsSold = 0;
double totalConversion = 0.0;
for (Object data : weeklyData) {
if (data instanceof Map) {
Map<String, Object> weeklyDataMap = (Map<String, Object>) data;
totalCustomerViews += (int) weeklyDataMap.get("customerViews");
totalProductSales += (int) weeklyDataMap.get("productSales");
totalNoOfUnitsSold += (int) weeklyDataMap.get("noOfUnitsSold");
totalConversion += (double) weeklyDataMap.get("conversion");
}
}
int numWeeks = weeklyData.size();
double averageConversion = (numWeeks > 0) ? totalConversion / numWeeks : 0.0;
combinedData.put("customerViews", totalCustomerViews);
combinedData.put("productSales", totalProductSales);
combinedData.put("noOfUnitsSold", totalNoOfUnitsSold);
combinedData.put("conversion", averageConversion);
return combinedData;
}