Welcome to our new coding practice lesson! We have an interesting problem in this unit that centers around data from a social networking app. The challenge involves processing logs from this app and extracting useful information from them. This task will leverage your skills in string manipulation, working with timestamps, and task subdivision. Let's get started!
Imagine a social networking application that allows users to form groups. Each group has a unique ID ranging from 1 to n
, where n
is the total number of groups. Interestingly, the app keeps track of when a group is created and deleted, logging all these actions in a string.
The task before us is to create a Java method named analyzeLogs
. This method will take as input a string of logs and output a List<String>
representing the groups with the longest lifetime. Each string in the list contains two items separated by a space: the group ID and the group's lifetime. By 'lifetime,' we mean the duration from when the group was created until its deletion. If a group has been created and deleted multiple times, the lifetime is the total sum of those durations. If multiple groups have the same longest lifetime, the method should return all such groups in ascending order of their IDs.
For example, if we have a log string as follows: "1 create 09:00, 2 create 10:00, 1 delete 12:00, 3 create 13:00, 2 delete 15:00, 3 delete 16:00"
, the method will return: ["2 05:00"]
.
First, we will split the input string into individual operations. In Java, string manipulation can be handled using methods from the String
class.
Java1import java.util.*; 2import java.util.stream.Collectors; 3 4class Solution { 5 public static List<String> analyzeLogs(String logs) { 6 List<String> logList = new ArrayList<>(Arrays.asList(logs.split(", ")));
Next, we delve deeper into the logs. For each logged group operation in the string, we need to parse its components. These include the group ID, the type of operation (create
or delete
), and the time of action.
Java1import java.util.*; 2import java.util.stream.Collectors; 3 4class Solution { 5 public static List<String> analyzeLogs(String logs) { 6 List<String> logList = new ArrayList<>(Arrays.asList(logs.split(", "))); 7 8 HashMap<Integer, int[]> timeDict = new HashMap<>(); // HashMap to record the creation moment for each group in minutes 9 TreeMap<Integer, Integer> lifeDict = new TreeMap<>(); // TreeMap to record the lifetime for each group in minutes 10 11 for (String log : logList) { 12 String[] parts = log.split(" "); 13 int groupId = Integer.parseInt(parts[0]); 14 String action = parts[1]; 15 String time = parts[2];
Now that we can identify the action performed on each group and when it's time to process these details. We convert the group ID into an integer and the timestamp into minutes from the start of the day. If the log entry marks a create
action, we register the time of creation in a dictionary under the group ID. If the entry signals delete
, we calculate the lifetime of the group and store it in another dictionary.
Java1import java.util.*; 2import java.util.stream.Collectors; 3 4class Solution { 5 public static List<String> analyzeLogs(String logs) { 6 List<String> logList = new ArrayList<>(Arrays.asList(logs.split(", "))); 7 8 HashMap<Integer, int[]> timeDict = new HashMap<>(); // HashMap to record the creation moment for each group in minutes 9 TreeMap<Integer, Integer> lifeDict = new TreeMap<>(); // TreeMap to record the lifetime for each group in minutes 10 11 for (String log : logList) { 12 String[] parts = log.split(" "); 13 int groupId = Integer.parseInt(parts[0]); 14 String action = parts[1]; 15 String time = parts[2]; 16 17 // Parsing the time from HH:MM format 18 int hour = Integer.parseInt(time.substring(0, 2)); 19 int minute = Integer.parseInt(time.substring(3, 5)); 20 int currentTime = hour * 60 + minute; // Time in minutes from start of day 21 22 if (action.equals("create")) { 23 timeDict.put(groupId, new int[]{hour, minute}); // If the group is created, log the creation time. 24 } else { 25 if (timeDict.containsKey(groupId)) { 26 // If the group is deleted, calculate its entire lifetime and remove it from the records. 27 int creationTime = timeDict.get(groupId)[0] * 60 + timeDict.get(groupId)[1]; 28 int lifetime = currentTime - creationTime; 29 lifeDict.put(groupId, lifeDict.getOrDefault(groupId, 0) + lifetime); 30 timeDict.remove(groupId); 31 } 32 } 33 }
After recording the lifetimes of all groups, we can compare them to determine which group or groups had the longest lifetime. Finally, we return the ID or IDs of that group or groups, sorted in ascending order, along with their lifetime.
Java1import java.util.*; 2import java.util.stream.Collectors; 3 4class Solution { 5 public static List<String> analyzeLogs(String logs) { 6 List<String> logList = new ArrayList<>(Arrays.asList(logs.split(", "))); 7 8 HashMap<Integer, int[]> timeDict = new HashMap<>(); // HashMap to record the creation moment for each group in minutes 9 TreeMap<Integer, Integer> lifeDict = new TreeMap<>(); // TreeMap to record the lifetime for each group in minutes 10 11 for (String log : logList) { 12 String[] parts = log.split(" "); 13 int groupId = Integer.parseInt(parts[0]); 14 String action = parts[1]; 15 String time = parts[2]; 16 17 // Parsing the time from HH:MM format 18 int hour = Integer.parseInt(time.substring(0, 2)); 19 int minute = Integer.parseInt(time.substring(3, 5)); 20 int currentTime = hour * 60 + minute; // Time in minutes from start of day 21 22 if (action.equals("create")) { 23 timeDict.put(groupId, new int[]{hour, minute}); // If the group is created, log the creation time. 24 } else { 25 if (timeDict.containsKey(groupId)) { 26 // If the group is deleted, calculate its entire lifetime and remove it from the creation records. 27 int creationTime = timeDict.get(groupId)[0] * 60 + timeDict.get(groupId)[1]; 28 int lifetime = currentTime - creationTime; 29 lifeDict.put(groupId, lifeDict.getOrDefault(groupId, 0) + lifetime); 30 timeDict.remove(groupId); 31 } 32 } 33 } 34 35 // Find the longest lifetime 36 int maxLife = Collections.max(lifeDict.entrySet(), Map.Entry.comparingByValue()).getValue(); 37 38 // Building the result list where each item is a string of "group ID lifetime" if it has the longest lifetime. 39 List<String> result = new ArrayList<>(); 40 for (Map.Entry<Integer, Integer> entry : lifeDict.entrySet()) { 41 if (entry.getValue() == maxLife) { 42 int hours = entry.getValue() / 60; 43 int minutes = entry.getValue() % 60; 44 String timeString = String.format("%02d:%02d", hours, minutes); 45 result.add(entry.getKey() + " " + timeString); 46 } 47 } 48 49 // Sorting the result in ascending order of the group IDs 50 result.sort(Comparator.comparing(s -> Integer.parseInt(s.split(" ")[0]))); 51 return result; 52 } 53 54 public static void main(String[] args) { 55 String logs = "1 create 09:00, 2 create 10:00, 1 delete 12:00, 3 create 13:00, 2 delete 15:00, 3 delete 16:00"; 56 List<String> result = analyzeLogs(logs); 57 for (String entry : result) { 58 System.out.println("Group " + entry.split(" ")[0] + " lifetime: " + entry.split(" ")[1]); 59 } 60 // Outputs: 61 // Group 2 lifetime: 05:00 62 } 63}
Bravo! You have successfully navigated a non-trivial log analysis problem and worked with timestamped data, a real-world data type in Java. Using Java's String
methods and HashMap
alongside traditional arithmetic, you transformed raw strings into meaningful data. Real-life coding often involves accurately understanding, dissecting, and analyzing data, and this unit's lesson has given you practical experience in that regard. Now, let's apply these new learnings to more practice challenges. Off to the races you go!