Introduction
Welcome to the part 7 of Go series. In the previous part (part 6): https://dev.to/vchiranjeeviak/go-maps-structs-485c, we saw what Maps and Structs are and how different operations like creating, adding, deleting etc can be done on them. In this one, lets see how we can iterate through them and the use-cases of these non-primitive datatypes.
Iterating through non-primitive datatypes
Arrays, Slices and Maps are some sort of collection of some similar type of values. There might be many situation where we want to go through each of these collections, do some computation or update the values. No matter what we do, going through the entire collection is the point to note here. We use loops to do this. Revisit this https://dev.to/vchiranjeeviak/go-conditionals-and-loops-5f1g loops article if haven't already for more context on loops.
Using normal for loop to iterate
Run a "for" loop from 0 until length of collection and do any operation on each item inside collection using index operator.
Program to print all items in a slice in new line
package main
import "fmt"
func main() {
arr := []string{"one", "two", "three", "four"}
for i := 0; i < len(arr); i++ {
fmt.Println(arr[i])
}
}
Output:
We can iterate through an array in the similar way. But in case of maps, this way of iteration is possible only if the keys in the map are of the int
type. If it is not of int
type, we can't use integer indices inside index operator.
Program to add all items in an array
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4, 5}
sum := 0
for i := 0; i < len(arr); i++ {
sum = sum + arr[i]
}
fmt.Println("Sum = ", sum)
}
Output:
Using for-each loop to iterate
We now know why iterating using normal "for" loop is not always possible in case of maps. It is because maps don't guarantee that they always have int
keys and while iterating using a normal "for" loop give us only integer value.
for-each loop lets us overcome this issue. If we use for-each loop with array or slice, it gives us 2 values, which are position and value. If we use for-each loop with maps, it gives us 2 values again, which are key and value.
The syntax to use it is for <pos/key>, <value> := range collection { }
.
Program to print key and value combined as a string in a string-string map
package main
import "fmt"
func main() {
mapp := make(map[string]string)
mapp["one"] = "1"
mapp["two"] = "2"
mapp["three"] = "3"
for key, value := range mapp {
fmt.Println(key + value)
}
}
Output:
Program to print position and value in a slice side by side
package main
import "fmt"
func main() {
arr := []int{23, 52, 745, 13}
for pos, val := range arr {
fmt.Printf("%v -> %v \n", pos, val)
}
}
Output:
This is how we can iterate through any collection like arrays, slices and maps.
Real world scenarios where non-primitive datatypes are useful
When we want to keep track of some items as a collection and we know the upper limit of the number of these items or want to limit the items to a particular number, then arrays are useful. I know it almost feels like a definition. Let's say we are keeping track of students joining in a class. Obviously every class is limited and let's consider the limit as 60. To store names/numbers of these students, we will need an array of max size 60.
Now, consider this situation where we need to keep track of voters in a country like we did for students in above example. Number of voters is not limited and not constant. So, we can't keep a cap on the value. This is a situation where we need a slice. When there is no limit required or known, to store items as a collection, slice is the way to go.
We can use maps wherever we are connecting two entities as a whole item. But the condition is that at least one of the two entities should be unique among all items. The unique entity is called as key. An item can be uniquely identified by these keys. A real world case can be keeping track of credit card number along with user name. Here, the relation we chose is the credit card and it's owner. So, credit card number is the key and owner name is the value. We can't represent a relation of a person and his credit cards. It is because, a person name can't be unique for all items. But in first case, a credit card number will always be unique in any pair.
A complex example of non-primitive datatypes and iteration
import "fmt"
type person struct { // defining a struct
name string
city string
}
func main() {
register := make(map[int]person) // map of int key and person value type
register[5463715] = person{name: "Ram", city: "Ayodhya"}
register[2385928] = person{name: "Sita", city: "Mithila"}
register[1432545] = person{name: "chiranjeevi", city: "Bangalore"}
for id, person := range register {
fmt.Printf("%v having id %v lives in %v. \n", person.name, id, person.city)
}
}
Output:
Conclusion
That's it for this one. In the next one, we will see what defer, panic, recover are. Let's meet there.
More on Go lang will be coming soon and do follow me to see them. Make sure you like and share if you felt this good.
Top comments (2)
Very well written. I finished reading all of your articles in this series. Examples are extremely helpful in determining which data types to use.
I look forward to reading more articles.
Glad you liked it. Thank you so much. Consider sharing this with your network if you find this series helpful.