Lesson 4
Parsing and Updating Complex String Maps in Go
Introduction

Hello, and welcome back! Are you ready for a new challenge? In this unit, we're stepping up to tackle a complex yet intriguing task. It involves parsing complex strings into a Go map[string]interface{} and then updating them, which is a common requirement in many real-world tasks. So yes, this unit's session is going to be pretty pragmatic — just the way you like it!

Task Statement

This task involves transforming a given string into a nested map[string]interface{} and updating a specific key-value pair within that map. The input string will take the form "Key1=Value1,Key2=Value2,...". When a part of the value is another key-value string, we create a nested map.

For example, the string "A1=B1,C1={D1=E1,F1=G1},I1=J1" should be transformed into the following map:

Go
1map[string]interface{}{ 2 "A1": "B1", 3 "C1": map[string]string{ 4 "D1": "E1", 5 "F1": "G1", 6 }, 7 "I1": "J1", 8}

Your Go function should parse this string into the above map, then update the value of the nested key F1 from G1 to some other value, say 'NewValue'. The function should ultimately return the updated map.

Step 1 - Setting Up the Function and Variables

First, set up the function and necessary variables:

Go
1package main 2 3import ( 4 "fmt" 5 "strings" 6) 7 8func parseString(inputString string) map[string]interface{} { 9 resultMap := make(map[string]interface{}) 10 11 var key string 12 innerMap := make(map[string]string) 13 inInnerMap := false 14 i := 0
Step 2 - Handling the Opening and Closing Braces

Next, handle the opening and closing braces. If an inner map is encountered, set the flag and prepare to parse it:

Go
1 for i < len(inputString) { 2 switch inputString[i] { 3 case '{': 4 inInnerMap = true 5 i++ // Skip the '{' 6 case '}': 7 resultMap[key] = innerMap 8 innerMap = make(map[string]string) 9 inInnerMap = false 10 i++ // Skip the '}' 11 if i < len(inputString) && inputString[i] == ',' { 12 i++ // Skip the ',' after '}' 13 }
Step 3 - Parsing Outer Map Key-Value Pairs

Handle parsing key-value pairs in the outer map:

Go
1 default: 2 if !inInnerMap { 3 equalPos := strings.Index(inputString[i:], "=") + i 4 commaPos := strings.Index(inputString[equalPos:], ",") + equalPos 5 if commaPos < equalPos { 6 commaPos = len(inputString) 7 } 8 9 key = inputString[i:equalPos] 10 value := inputString[equalPos+1 : commaPos] 11 12 if strings.Contains(value, "{") { 13 key = inputString[i:equalPos] 14 i = equalPos + 1 // Move forward to process the nested part 15 } else { 16 resultMap[key] = value 17 i = commaPos + 1 // Move past the comma 18 } 19 }
Step 4 - Parsing Inner Map Key-Value Pairs

Handle parsing key-value pairs inside the nested map:

Go
1 if inInnerMap { 2 equalPos := strings.Index(inputString[i:], "=") + i 3 commaPos := strings.Index(inputString[equalPos:], ",") + equalPos 4 bracePos := strings.Index(inputString[equalPos:], "}") + equalPos 5 6 endPos := len(inputString) 7 if bracePos >= equalPos && (commaPos < 0 || bracePos < commaPos) { 8 endPos = bracePos 9 } else if commaPos >= equalPos { 10 endPos = commaPos 11 } 12 13 innerKey := inputString[i:equalPos] 14 innerValue := inputString[equalPos+1 : endPos] 15 innerMap[innerKey] = innerValue 16 17 i = endPos 18 if i < len(inputString) && inputString[i] == ',' { 19 i++ // Skip the comma 20 } 21 } 22 } 23 } 24 25 return resultMap 26}
Step 5 - Updating the Value in the Map

Now that we have the parsed map, we can move into the final phase of the task: updating a specific key-value pair. Here’s the function to update a value in the nested maps:

Go
1func updateMap(m map[string]interface{}, key, value string) { 2 if val, exists := m[key]; exists { 3 if strVal, ok := val.(string); ok { 4 m[key] = value 5 } else if innerMap, ok := val.(map[string]string); ok { 6 if _, exists := innerMap[key]; exists { 7 innerMap[key] = value 8 } 9 } 10 return 11 } 12 13 for _, v := range m { 14 if innerMap, ok := v.(map[string]string); ok { 15 if _, exists := innerMap[key]; exists { 16 innerMap[key] = value 17 return 18 } 19 } 20 } 21}
Step 6 - Putting It All Together

Finally, we put everything together in one function to parse the string and update the value:

Go
1package main 2 3import ( 4 "fmt" 5 "strings" 6) 7 8func parseString(inputString string) map[string]interface{} { 9 resultMap := make(map[string]interface{}) 10 11 var key string 12 innerMap := make(map[string]string) 13 inInnerMap := false 14 i := 0 15 16 for i < len(inputString) { 17 switch inputString[i] { 18 case '{': 19 inInnerMap = true 20 i++ // Skip the '{' 21 case '}': 22 resultMap[key] = innerMap 23 innerMap = make(map[string]string) 24 inInnerMap = false 25 i++ // Skip the '}' 26 if i < len(inputString) && inputString[i] == ',' { 27 i++ // Skip the ',' after '}' 28 } 29 default: 30 if !inInnerMap { 31 equalPos := strings.Index(inputString[i:], "=") + i 32 commaPos := strings.Index(inputString[equalPos:], ",") + equalPos 33 if commaPos < equalPos { 34 commaPos = len(inputString) 35 } 36 37 key = inputString[i:equalPos] 38 value := inputString[equalPos+1 : commaPos] 39 40 if strings.Contains(value, "{") { 41 key = inputString[i:equalPos] 42 i = equalPos + 1 // Move forward to process the nested part 43 } else { 44 resultMap[key] = value 45 i = commaPos + 1 // Move past the comma 46 } 47 } 48 49 if inInnerMap { 50 equalPos := strings.Index(inputString[i:], "=") + i 51 commaPos := strings.Index(inputString[equalPos:], ",") + equalPos 52 bracePos := strings.Index(inputString[equalPos:], "}") + equalPos 53 54 endPos := len(inputString) 55 if bracePos >= equalPos && (commaPos < 0 || bracePos < commaPos) { 56 endPos = bracePos 57 } else if commaPos >= equalPos { 58 endPos = commaPos 59 } 60 61 innerKey := inputString[i:equalPos] 62 innerValue := inputString[equalPos+1 : endPos] 63 innerMap[innerKey] = innerValue 64 65 i = endPos 66 if i < len(inputString) && inputString[i] == ',' { 67 i++ // Skip the comma 68 } 69 } 70 } 71 } 72 73 return resultMap 74} 75 76func updateMap(m map[string]interface{}, key, value string) { 77 if val, exists := m[key]; exists { 78 if strVal, ok := val.(string); ok { 79 m[key] = value 80 } else if innerMap, ok := val.(map[string]string); ok { 81 if _, exists := innerMap[key]; exists { 82 innerMap[key] = value 83 } 84 } 85 return 86 } 87 88 for _, v := range m { 89 if innerMap, ok := v.(map[string]string); ok { 90 if _, exists := innerMap[key]; exists { 91 innerMap[key] = value 92 return 93 } 94 } 95 } 96} 97 98func parseStringAndUpdateValue(inputString, updateKey, newValue string) map[string]interface{} { 99 // Parse the input string into a map 100 mapData := parseString(inputString) 101 // Update the specific key-value pair 102 updateMap(mapData, updateKey, newValue) 103 return mapData 104} 105 106func main() { 107 input := "A1=B1,C1={D1=E1,F1=G1},I1=J1" 108 updatedMap := parseStringAndUpdateValue(input, "F1", "NewValue") 109 fmt.Println(updatedMap) 110}
Lesson Summary

Well done! You've completed an intensive hands-on session dealing with complex strings and map[string]interface{} in Go. This type of task often mirrors real-life scenarios where you process complex data and make updates based on particular criteria.

Now it's your turn to reinforce what you've learned. Try practicing with different strings and attempt to update various key-value pairs. With practice, you'll be able to apply these coding strategies to a wide range of problems. Happy coding!

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