Lesson 5
Practical Data Manipulation Techniques in Go
Topic Overview and Importance

Hello and welcome! Today, we're diving into practical data manipulation techniques in Go. We'll be using Go's slices and maps to represent our data and perform projection, filtering, and aggregation. The operations will be encapsulated within a Go struct, ensuring our code remains organized and efficient. So let's get ready to tackle data manipulation in a clean, idiomatic Go manner!

Introduction to Data Manipulation

Data manipulation can be likened to sculpting — it's all about shaping and conditioning data to fit our specific needs. In Go, slices and maps offer powerful ways to handle and transform data. We'll harness these constructs within a Go struct to provide a tidy toolbox for our data operations. Here's a simple Go struct, DataStream, that will serve as the framework for our exploration:

Go
1package main 2 3import "fmt" 4 5type DataStream struct { 6 data []map[string]string 7} 8 9func NewDataStream(data []map[string]string) *DataStream { 10 return &DataStream{data: data} 11} 12 13func (ds *DataStream) PrintData() { 14 for _, entry := range ds.data { 15 for key, value := range entry { 16 fmt.Printf("%s: %s, ", key, value) 17 } 18 fmt.Println() 19 } 20}
Data Projection in Practice

Our first task is data projection — selecting specific attributes of interest. Let's say we're handling a dataset about individuals, and we're only interested in names and ages. We can extend our DataStream struct with a Project method to efficiently handle this:

Go
1// ... previous code 2 3func (ds *DataStream) Project(projectFunc func(map[string]string) map[string]string) *DataStream { 4 var projectedData []map[string]string 5 for _, entry := range ds.data { 6 projectedData = append(projectedData, projectFunc(entry)) 7 } 8 return NewDataStream(projectedData) 9} 10 11func main() { 12 ds := NewDataStream([]map[string]string{ 13 {"name": "Alice", "age": "25", "profession": "Engineer"}, 14 {"name": "Bob", "age": "30", "profession": "Doctor"}, 15 }) 16 17 projectedDs := ds.Project(func(entry map[string]string) map[string]string { 18 return map[string]string{"name": entry["name"], "age": entry["age"]} 19 }) 20}

In this example, by applying the Project method we filter our dataset to focus solely on names and ages.

Data Filtering in Practice

Next, let's tackle data filtering by picking specific entries that meet certain criteria. We'll enhance our DataStream struct with a Filter method using Go's native for loops:

Go
1// ... previous code 2 3 4func (ds *DataStream) Filter(testFunc func(map[string]string) bool) *DataStream { 5 var filteredData []map[string]string 6 for _, entry := range ds.data { 7 if testFunc(entry) { 8 filteredData = append(filteredData, entry) 9 } 10 } 11 return NewDataStream(filteredData) 12} 13 14func main() { 15 ds := NewDataStream([]map[string]string{ 16 {"name": "Alice", "age": "25", "profession": "Engineer"}, 17 {"name": "Bob", "age": "30", "profession": "Doctor"}, 18 }) 19 20 filteredDs := ds.Filter(func(entry map[string]string) bool { 21 return entry["age"] > "26" 22 }) 23}

Here, through filtering, we isolate and select only the data of individuals whose age exceeds the threshold of 26 years.

Data Aggregation in Practice

Finally, let's perform data aggregation by summarizing key elements. We'll enhance our DataStream struct to include an Aggregate method:

Go
1// ... previous code 2 3func (ds *DataStream) Aggregate(key string, aggFunc func([]string) float64) float64 { 4 var values []string 5 for _, entry := range ds.data { 6 if value, ok := entry[key]; ok { 7 values = append(values, value) 8 } 9 } 10 return aggFunc(values) 11} 12 13func main() { 14 ds := NewDataStream([]map[string]string{ 15 {"name": "Alice", "age": "25", "profession": "Engineer"}, 16 {"name": "Bob", "age": "30", "profession": "Doctor"}, 17 }) 18 19 averageAge := ds.Aggregate("age", func(ages []string) float64 { 20 sum := 0 21 for _, age := range ages { 22 num, _ := strconv.Atoi(age) 23 sum += num 24 } 25 return float64(sum) / float64(len(ages)) 26 }) 27 28 fmt.Println(averageAge) // Outputs: 27.5 29}

Using aggregation, we can derive valuable insights such as calculating the average age in our dataset.

Combining Projection, Filtering, and Aggregation

Let's combine projection, filtering, and aggregation to explore their collective power. We'll demonstrate this flow using Go's syntax:

Go
1package main 2 3import ( 4 "fmt" 5 "strconv" 6) 7 8type DataStream struct { 9 data []map[string]string 10} 11 12func NewDataStream(data []map[string]string) *DataStream { 13 return &DataStream{data: data} 14} 15 16func (ds *DataStream) Project(projectFunc func(map[string]string) map[string]string) *DataStream { 17 var projectedData []map[string]string 18 for _, entry := range ds.data { 19 projectedData = append(projectedData, projectFunc(entry)) 20 } 21 return NewDataStream(projectedData) 22} 23 24func (ds *DataStream) Filter(testFunc func(map[string]string) bool) *DataStream { 25 var filteredData []map[string]string 26 for _, entry := range ds.data { 27 if testFunc(entry) { 28 filteredData = append(filteredData, entry) 29 } 30 } 31 return NewDataStream(filteredData) 32} 33 34func (ds *DataStream) Aggregate(key string, aggFunc func([]string) float64) float64 { 35 var values []string 36 for _, entry := range ds.data { 37 if value, ok := entry[key]; ok { 38 values = append(values, value) 39 } 40 } 41 return aggFunc(values) 42} 43 44func main() { 45 ds := NewDataStream([]map[string]string{ 46 {"name": "Alice", "age": "25", "profession": "Engineer", "salary": "70000"}, 47 {"name": "Bob", "age": "30", "profession": "Doctor", "salary": "120000"}, 48 {"name": "Carol", "age": "35", "profession": "Artist", "salary": "50000"}, 49 {"name": "David", "age": "40", "profession": "Engineer", "salary": "90000"}, 50 }) 51 52 projectedDs := ds.Project(func(entry map[string]string) map[string]string { 53 return map[string]string{"age": entry["age"], "salary": entry["salary"]} 54 }) 55 56 filteredDs := projectedDs.Filter(func(entry map[string]string) bool { 57 age, _ := strconv.Atoi(entry["age"]) 58 return age > 30 59 }) 60 61 averageSalary := filteredDs.Aggregate("salary", func(salaries []string) float64 { 62 sum := 0 63 for _, salary := range salaries { 64 num, _ := strconv.Atoi(salary) 65 sum += num 66 } 67 return float64(sum) / float64(len(salaries)) 68 }) 69 70 fmt.Println(averageSalary) // Outputs: 70000.0 71}

Here we perform:

  • Projection: Extract selected fields (age and salary) from the dataset.
  • Filtering: Retain only entries where age exceeds 30.
  • Aggregation: Calculate the average salary of the filtered group.

This combination achieves efficient and meaningful data manipulation in Go.

Lesson Summary

Great work! You've learned to implement data projection, filtering, and aggregation in Go using slices and maps. By encapsulating these operations within a Go struct, you've created a neat and reusable code bundle!

Continue practicing these skills with some exercises, and you'll be ready to master more complex data manipulation tasks. Let's dive deeper into the world of Go!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.