Gowhere – array, hash table, regular expression, and foreach loop
Gowhere? Some steps further—append arrays, hash table, regular expression, and for each loop.
After playing around with HTTP, I got the JSON data from the API. I wanted to analyze the data—to display the total hours spent on each work item.
A typical record has this structure
type WorkItem struct {
FromDateTime time.Time
ToDateTime time.Time
Detail string
}
And an actual work item looks like
{
"FromDateTime" : "2019-11-29 02:05:00 +0000 UTC",
"ToDateTime": "2019-11-29 03:19:00 +0000 UTC",
"Detail" : "Work Item 12345: Implement a cool feature"
}
The "Work Item 12345" has many records. The detail field are not the same except they contain the number 12345 as the work item ID. I want to display the sum of time spent for Work Item 12345. So the algorithm is pretty simple
- For each record, extract the work item id from the detail field
- Calculate the difference in hours between FromDateTime and ToDateTime
- Sum the difference with the existing value—if not exist, create a new one with the time spent zero
Note: If I am writing in C#, I can finish the implementation quickly with Linq support.
The expected result of a work item is below
{
"WorkItemId" : "12345",
"WorkItemName" : "Work Item 12345: Implement a cool feature",
"TimeSpent" : time_spent_in_hours
}
Stats structure to hold the analysis result of a work item
// Stats ...
type Stats struct {
WorkItemId string
WorkItemName string
TimeSpent float64
}
Let’s write some code and explore
package main
import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"time"
)
func main() {
// Assuming that I have a list of IDs for a team.
// The getWorkItemRecords will an array of WorkItem of a member.
var records = make([]WorkItem, 0)
for _, id := range team {
r := getWorkItemRecords(id)
// Discussion about appending 2 slices: https://stackoverflow.com/questions/16248241/concatenate-two-slices-in-go
// This is how to append 2 arrays
records = append(records, r...)
}
// A dictionary (hash table) with
// key: WorkItemID (or name if cannot find the ID) - simply a string
// value: total time spent
// More detail about map here: https://blog.golang.org/go-maps-in-action
statsMap := make(map[string]Stats)
// Regular expression to extract ID (all numeric characters)
workItemIdExp := regexp.MustCompile("[\\d]+")
var id string
for _, r := range records {
timeSpent := r.ToDateTime.Sub(r.FromDateTime).Hours()
if timeSpent < 0 {
// The record does not have an end time
continue
}
id = workItemIdExp.FindString(r.Detail)
if id == "" {
id = r.Detail
}
ts, exist := statsMap[id]
if !exist {
ts = Stats{id, r.Detail, 0}
}
ts.TimeSpent += timeSpent
statsMap[id] = ts
}
var workingHours float64 = 0
for key, value := range statsMap {
workingHours += value.TimeSpent
fmt.Printf("%s (%s) %f\n", key, value.WorkItemName, value.TimeSpent)
}
fmt.Printf("Working hours: %f\n", workingHours)
}
What are my further steps from this exercise?
- Append 2 arrays (slices) with "…" syntax—
append(records, r...)
- Hash table (dictionary like) with map—
map[string]int
means a dictionary with key is a string and value is an integer - Regular expression with regexp package—
regexp.MustCompile("[\\d]+")
- For each loop with the range—
for _, r := range records
A happy weekend!