Lesson 4
Parsing and Manipulating Nested Maps in Kotlin
Introduction

Hello, and welcome back! Are you ready for a new challenge? In this unit, we're exploring an interesting task using Kotlin's powerful map capabilities. We'll be focusing on parsing complex strings into nested maps and updating them. This is a common requirement in many real-world tasks, making our session particularly practical — just the way you like it!

Task Statement

This task involves transforming a given string into a nested map and updating a specific key-value pair within that map using Kotlin. 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 nested map:

Kotlin
1val dictionary: MutableMap<String, Any> = mutableMapOf( 2 "A1" to mutableMapOf("" to "B1"), 3 "C1" to mutableMapOf("D1" to "E1", "F1" to "G1"), 4 "I1" to mutableMapOf("" to "J1") 5)

Your function should parse this string into the above nested map, then update the value of the nested key F1 from G1 to another 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 in Kotlin:

Kotlin
1fun parseString(inputString: String): MutableMap<String, Any> { 2 val result = mutableMapOf<String, Any>() // Stores the final nested map structure with parsed key-value pairs. 3 var key = "" // Temporarily holds the current key being parsed from the input string. 4 var innerMap = mutableMapOf<String, String>() // Temporarily holds key-value pairs for any detected inner map. 5 var inInnerMap = false // Flag to indicate whether the parsing process is currently inside an inner map. 6 var i = 0 // Index used to iterate through each character of the input string.
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:

Kotlin
1 while (i < inputString.length) { 2 when (inputString[i]) { 3 '{' -> { 4 // Entering an inner map 5 inInnerMap = true 6 i++ // Skip the '{' 7 } 8 '}' -> { 9 // Exiting an inner map 10 result[key] = innerMap 11 innerMap = mutableMapOf() 12 inInnerMap = false 13 i++ // Skip the '}' 14 if (i < inputString.length && inputString[i] == ',') { 15 i++ // Skip the ',' after '}' 16 } 17 }
Step 3 - Parsing Outer Map Key-Value Pairs

Handle parsing key-value pairs in the outer map:

Kotlin
1 else -> if (!inInnerMap) { 2 // Parsing key-value pairs in the outer map 3 val equalPos = inputString.indexOf('=', i) 4 val commaPos = inputString.indexOf(',', equalPos).takeIf { it != -1 } ?: inputString.length 5 6 key = inputString.substring(i, equalPos) 7 val value = inputString.substring(equalPos + 1, commaPos) 8 9 if ('{' in value) { 10 // Value is a nested map, will be processed separately 11 i = equalPos + 1 12 } else { 13 // Value is a simple string, add to result map 14 result[key] = mutableMapOf("" to value) 15 i = commaPos + 1 // Move past the comma 16 } 17 }
Step 4 - Parsing Inner Map Key-Value Pairs

Handle parsing key-value pairs inside the inner map:

Kotlin
1 else -> if (inInnerMap) { 2 // Parsing key-value pairs inside the inner map 3 val equalPos = inputString.indexOf('=', i) 4 val commaPos = inputString.indexOf(',', equalPos) 5 val bracePos = inputString.indexOf('}', equalPos) 6 7 // Determine the next delimiter that ends the current key-value pair 8 val endPos = listOf(commaPos, bracePos).filter { it != -1 }.minOrNull() ?: inputString.length 9 10 val innerKey = inputString.substring(i, equalPos) 11 val innerValue = inputString.substring(equalPos + 1, endPos) 12 innerMap[innerKey] = innerValue 13 14 i = endPos 15 if (i < inputString.length && inputString[i] == ',') { 16 i++ // Skip the comma 17 } 18 } 19 } 20 } 21 22 return result 23}
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 map:

Kotlin
1fun updateMap(map: MutableMap<String, Any>, key: String, value: String) { 2 map.values.forEach { 3 when (it) { 4 is MutableMap<*, *> -> if (it.containsKey(key)) { 5 (it as MutableMap<String, String>)[key] = value 6 return 7 } 8 } 9 } 10}
Step 6 - Putting It All Together

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

Kotlin
1fun parseStringAndUpdateValue(inputString: String, updateKey: String, newValue: String): MutableMap<String, Any> { 2 // Parse the input string into a nested map 3 val map = parseString(inputString) 4 // Update the specific key-value pair 5 updateMap(map, updateKey, newValue) 6 return map 7} 8 9fun main() { 10 val input = "A1=B1,C1={D1=E1,F1=G1},I1=J1" 11 val updateKey = "F1" 12 val newValue = "NewValue" 13 14 val updatedMap = parseStringAndUpdateValue(input, updateKey, newValue) 15 println(updatedMap) // Outputs {A1={=B1}, C1={D1=E1, F1=NewValue}, I1={=J1}} 16}
Lesson Summary

Well done! You've completed an intensive hands-on session dealing with complex strings and nested maps in Kotlin. This type of task mirrors real-life scenarios where you process complex data and make updates based on specific criteria.

Now it's your turn to reinforce what you've learned in this unit. Try practicing with different strings and attempting 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.