Welcome to our new coding practice lesson! We have an intriguing problem centered around data from a social networking app. The challenge involves processing logs from this app and extracting valuable 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. The app keeps track of group creation and deletion events, logging all these actions in a string.
The task before us is to create a Go function named AnalyzeLogs
. This function will take as input a string of logs and output a slice of strings representing the groups with the longest lifetime. Each string in the slice 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.
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 function will return: ["2 05:00"]
.
If multiple groups have the same longest lifetime, the function should return all such groups in ascending order of their IDs. Let's say we have the log string "1 create 08:00, 2 create 09:00, 1 delete 10:00, 2 delete 11:00, 3 create 12:00, 3 delete 13:00"
. In this case, both group 1 and group 2 have the same lifetime of 02:00. The function should return ["1 02:00", "2 02:00"]
since both have the longest lifetime, and group 1 comes before group 2.
To tackle this problem, we will take the following steps:
- Split the Log Strings: Divide the input string into individual log entries based on a delimiter using
strings.Split
. - Parse Log Components: For each log entry, identify the group ID, action type, and timestamp, and use
strconv.Atoi
for conversions. - Record and Calculate Lifetimes: Track creation times using maps and compute lifetimes whenever a group is deleted.
- Identify Longest Lifetimes: Compare the lifetimes of all groups to determine the ones with the longest using Go's iteration and sorting capabilities.
First, we will split the input string into individual operations. In Go, string manipulation can be handled using functions from the strings
package.
Go1package main 2 3import ( 4 "fmt" 5 "strings" 6) 7 8func AnalyzeLogs(logs string) []string { 9 logList := strings.Split(logs, ", ")
The strings.Split
function in Go divides a string into a slice of substrings based on a specified delimiter. For example, strings.Split(logs, ", ")
will divide the logs
string into smaller substrings wherever it finds the delimiter ", "
and return a slice of those substrings. For example, take a look at the following code:
Go1logs := "1 create 09:00, 2 create 10:00, 1 delete 12:00, 3 create 13:00, 2 delete 15:00, 3 delete 16:00" 2fmt.Println(strings.Join(strings.Split(logs, ", "), "\n"))
The output would return:
Plain text11 create 09:00 22 create 10:00 31 delete 12:00 43 create 13:00 52 delete 15:00 63 delete 16:00
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.
Go1import ( 2 "fmt" 3 "strconv" 4 "strings" 5) 6 7func AnalyzeLogs(logs string) []string { 8 logList := strings.Split(logs, ", ") 9 10 timeDict := make(map[int][2]int) // Store the creation time in minutes 11 lifeDict := make(map[int]int) // Store the total lifetime for each group 12 13 for _, log := range logList { 14 parts := strings.Split(log, " ") // Split each log into components: group ID, action, and time 15 groupId, _ := strconv.Atoi(parts[0]) // Convert group ID from string to integer 16 action := parts[1] // Action can be either 'create' or 'delete' 17 time := parts[2] // Timestamp of the action (HH:MM format) 18 } 19}
Now that we can identify each group's actions and their timestamps, 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 creation time in a map under the group ID. If the entry signals delete
, we calculate the group's lifetime and store it in another map.
Using this to parse and calculate the lifetimes, we can use the following code:
Go1package main 2 3import ( 4 "fmt" 5 "strconv" 6 "strings" 7) 8 9func AnalyzeLogs(logs string) []string { 10 logList := strings.Split(logs, ", ") 11 12 timeDict := make(map[int][2]int) // Store the creation time in minutes 13 lifeDict := make(map[int]int) // Store the total lifetime for each group 14 15 for _, log := range logList { 16 parts := strings.Split(log, " ") 17 groupId, _ := strconv.Atoi(parts[0]) 18 action := parts[1] 19 time := parts[2] 20 21 // Parsing the time from HH:MM format 22 hour, _ := strconv.Atoi(time[:2]) 23 minute, _ := strconv.Atoi(time[3:]) 24 currentTime := hour*60 + minute // Time in minutes from the start of the day 25 26 if action == "create" { 27 timeDict[groupId] = [2]int{hour, minute} // If the group is created, log the creation time. 28 } else { 29 if creationTime, exists := timeDict[groupId]; exists { 30 // If the group is deleted, calculate its lifetime and update the records. 31 creationMinutes := creationTime[0]*60 + creationTime[1] 32 lifetime := currentTime - creationMinutes 33 lifeDict[groupId] += lifetime 34 delete(timeDict, groupId) // Remove group from timeDict after calculating lifetime. 35 } 36 } 37 } 38
If the action is "delete"
and the group exists in timeDict
, its lifetime is calculated. The lifetime is added to the group’s entry in lifeDict
.
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 those groups, sorted in ascending order, along with their lifetime.
Go1package main 2 3import ( 4 "fmt" 5 "sort" 6 "strconv" 7 "strings" 8) 9 10func AnalyzeLogs(logs string) []string { 11 logList := strings.Split(logs, ", ") 12 13 timeDict := make(map[int][2]int) // Store the creation time in minutes 14 lifeDict := make(map[int]int) // Store the total lifetime for each group 15 16 for _, log := range logList { 17 parts := strings.Split(log, " ") 18 groupId, _ := strconv.Atoi(parts[0]) 19 action := parts[1] 20 time := parts[2] 21 22 // Parsing the time from HH:MM format 23 hour, _ := strconv.Atoi(time[:2]) 24 minute, _ := strconv.Atoi(time[3:]) 25 currentTime := hour*60 + minute // Time in minutes from the start of the day 26 27 if action == "create" { 28 timeDict[groupId] = [2]int{hour, minute} // If the group is created, log the creation time. 29 } else { 30 if creationTime, exists := timeDict[groupId]; exists { 31 // If the group is deleted, calculate its lifetime and update the records. 32 creationMinutes := creationTime[0]*60 + creationTime[1] 33 lifetime := currentTime - creationMinutes 34 lifeDict[groupId] += lifetime 35 delete(timeDict, groupId) // Remove group from timeDict after calculating lifetime. 36 } 37 } 38 } 39 40 // Find the longest lifetime 41 var maxLife int 42 for _, life := range lifeDict { 43 if life > maxLife { 44 maxLife = life 45 } 46 } 47 48 // Building the result list where each item is a string of "group ID lifetime" if it has the longest lifetime. 49 var result []string 50 for id, life := range lifeDict { 51 if life == maxLife { 52 hours := life / 60 53 minutes := life % 60 54 timeString := fmt.Sprintf("%02d:%02d", hours, minutes) 55 result = append(result, fmt.Sprintf("%d %s", id, timeString)) 56 } 57 } 58 59 // Sorting the result in ascending order of the group IDs 60 sort.Slice(result, func(i, j int) bool { 61 id1, _ := strconv.Atoi(strings.Split(result[i], " ")[0]) 62 id2, _ := strconv.Atoi(strings.Split(result[j], " ")[0]) 63 return id1 < id2 64 }) 65 66 return result 67} 68 69func main() { 70 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" 71 result := AnalyzeLogs(logs) 72 for _, entry := range result { 73 fmt.Println("Group " + strings.Split(entry, " ")[0] + " lifetime: " + strings.Split(entry, " ")[1]) 74 } 75 // Outputs: 76 // Group 2 lifetime: 05:00 77}
Bravo! You have successfully navigated a non-trivial log analysis problem and worked with timestamped data, a real-world data type, in Go. Using Go's strings
package methods and maps 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 provided you with practical experience in that regard. Now, let's apply these new learnings to more practice challenges. Off to the races you go!