GO: understanding Map — Part 1

Alessandro Giovanardi
4 min readOct 21, 2019

--

This article is about how to use map in GO (Golang).

THEORY

  • A map is an unordered collection of key-values pairs.
  • Maps are used to retrieve, update or remove a value by its associated key.
  • In Go, a map, is a reference to a hash table, where all the keys are distinct.
  • Map type is written map[K]V where K and V are types of its keys and values.
  • A map has a length and a capacity, which are modifiable.
  • Maps are Go’s built-in associative data type

CODE

A map can be initialized in different ways:

  1. Empty map with nil keys and zero-value values
//create an empty nil map
var em map[int]string
fmt.Println(em) // output = map[]

2. Map literal

//map literal
m := map[string][]string{
“string_one”: []string{“Stringy”, “Wolly”, “Lulalop”},
}
fmt.Println(m) // output = map[string_one:[Stringy Wolly Lulalop]]

3. Use built-in function make to declare and initialize a map

//use built-in function make to create a map. When using make, the keys and values of the map are 0-values for their own type
//Occasionally you want to give capacity room in advance with make(map[K]v, ###)
//where ### is the desired capacity of the map specified as int
m1 := make(map[string]float64)
//add entry to m1 map. note that we add a key and value pair of the type specified in map init statement.
m1[“Pi”] = 3.1416
fmt.Println(m1) // output = map[Pi:3.1416]

It’s a good practice to allocate an underlying slice (array of slice) of a required size upfront which has the capacity to hold all the keys of the map used with the following code:

sl := make([]string, 0, len(our_map))

Map data manipulation

Key-Values pairs in map can be retrieved, updated or deleted.

  • Retrieve single value by its key
v := m1["pi"]               // Get value: v == 3.1416
fmt.Println(v) // Output = 3.1416
  • Delete a key-value pair
delete(m, "string_one")     // Delete a key-value pair
fmt.Println(m) // Print map: "map[]"
  • Add key-value pair
//map[“new key”] = new_value//map literal
m := map[string][]string{
“string_one”: []string{“Stringy”, “Wolly”, “Lulalop”},
}
fmt.Println(m) // output = map[string_one:[Stringy Wolly Lulalop]]
//add new entry in map with key "string_two" and its values
m["string_two"] = []string{"I'm a new value string", "I'm a really great string babe", "WE do L!ké ASCII"}
fmt.Println(m) // output = map[string_one:[Stringy Wolly Lulalop] string_two:[I'm a new value string I'm a really great string babe WE do L!ké ASCII]]
  • Range over the map to retrieve values for each records
for i, v := range m {
fmt.Println("Map record #:", i)
for j, val := range v {
fmt.Printf("\t Map's slice position index: %v \t Value: %v\n", j, val)
if j == 2 {
fmt.Printf("\n")
}
}
}
/* output =
Map record: string_one
Map's slice position index: 0 Value: Stringy
Map's slice position index: 1 Value: Wolly
Map's slice position index: 2 Value: Lulalop

Map record: string_two
Map's slice position index: 0 Value: I'm a new value string
Map's slice position index: 1 Value: I'm a really great string babe
Map's slice position index: 2 Value: WE do L!ké ASCII
*/

In the code snippet above we range the map m with two nested range-based for loops.

The first for loop range over map’s keys index and prints out key for each record available in map.

The second for loop range over the indexes and values of each key in the map. In our case every key has more than 1 underlying index and value pairs, that’s why for each key record we have more than one value associated, each of the value with a different index position of the underlying slice (you should remember that slices are ordered list of index-values pairs.).

Compare two maps

Maps, as with slices, cannot be compared to each other in a direct way. The only legal comparison is with nil. For this task we need to write a loop:

First of all we check for the length of the two maps. If the length is different the maps are different and we can already know that mapA != mapB.

If lenght of mapA is equal to mapB we must use a range-based for loop to iterate over the elements of the mapA and compare to the elements of the mapB.

//[...omissis]
func equal(x, y map[string]int) {
if len(x) != len(y) {
return false
}
for k, xv := range x {
if yv, ok := y[k]; !ok || yv != xv {
return false
}
}
return true
}
//[...omissis]
//compare two maps with equal() function
x1 := map[string]int{
"jim": 1,
"logan": 2,
"henry": 3,
}
y1 := map[string]int{
"jim": 1,
"lucie": 2,
"alex": 3,
}
fmt.Println("Does maps contains the same keys? ", equal(x1, y1))//output = Does maps contains the same keys? false

Comma-OK statements

Sometimes you need to distinguish a missing entry from a zero value.

We use the comma-ok statement to check if a variable is true or false, then execute code.

//comma-ok or comma-found statements checks if value is available, then if available prints it out.
// value, bool := m[]
if x, found := m["string_two"]; found {
fmt.Println(x)
}
if x, ok := m["string_two"]; ok {
fmt.Println(x)
}
//If the key doesn’t exist, the first value will be the default zero value.
if x, ok := m["string_five"]; ok {
fmt.Println(x)
} else {
fmt.Printf("%v", x)
}
}

USEFUL TIPS

  • Iterate over map records produces random ordering
    GO’s map iteration order is not specified and may vary from iteration to iteration (this is intentional).
  • Lookup, len, delete, range over nil map reference performs correctly BUT..
  • Storing to a nil map causes panic! (example below)
m["string_five"] = "a string" // panic: assignment to entry nil map

Thanks for reading! Here it is a complete commented program which showcases all the features for map usage. I suggest to try it out to understand and practice map operations.

Stay tuned for part 2 ;)

If you want to support my work feel free to send a tip:

$BTC: 1EtMQNXADe5ZoSgbwox7Ug5TPvP8YWSpL3

$QRL: Q010400e08175fe9823e6caa1aa3359686d7251a40eb432230d55f5b4e7388ec1c947b4361fb5cf

--

--

No responses yet