The map type

The Go map is a composite type that is used as containers for storing unordered elements of the same type indexed by an arbitrary key value. The following code snippet shows a variety of map variables declarations with a variety of key types:

var ( 
    legends map[int]string 
    histogram map[string]int 
    calibration map[float64]bool 
    matrix map[[2][2]int]bool    // map with array key type 
    table map[string][]string    // map of string slices 
 
   // map (with struct key) of map of string 
   log map[struct{name string}]map[string]string 
) 

golang.fyi/ch07/maptypes.go

The previous code snippet shows several variables declared as maps of different types with a variety of key types. In general, map type is specified as follows:

map[<key_type>]<element_type>

The key specifies the type of a value that will be used to index the stored elements of the map. Unlike arrays and slices, map keys can be of any type, not just int. Map keys, however, must be of types that are comparable including numeric, string, Boolean, pointers, arrays, struct, and interface types (see Chapter 4, Data Types, for discussion on comparable types).

Map initialization

Similar to a slice, a map manages an underlying data structure, opaque to its user, to store its values. An uninitialized map has a nil zero-value as well. Attempts to insert into an uninitialized map will result in a program panic. Unlike a slice, however, it is possible to access elements from a nil map, which will return the zero value of the element.

Like other composite types, maps may be initialized using a composite literal value of the following form:

<map_type>{<comma-separated list of key:value pairs>}

The following snippet shows variable initialization with map composite literals:

var ( 
   histogram map[string]int = map[string]int{ 
         "Jan":100, "Feb":445, "Mar":514, "Apr":233, 
         "May":321, "Jun":644, "Jul":113, "Aug":734, 
         "Sep":553, "Oct":344, "Nov":831, "Dec":312,  
   } 
 
   table = map[string][]int { 
         "Men":[]int{32, 55, 12, 55, 42, 53}, 
         "Women":[]int{44, 42, 23, 41, 65, 44}, 
   } 
) 

golang.fyi/ch07/mapinit.go

The literal mapped values are specified using a colon-separated pair of key and value as shown in the previous example. The type of each key and value pair must match that of the declared elements in the map.

Making Maps

Similar to a slice, a map value can also be initialized using the make function. Using the make function initializes the underlying storage allowing data to be inserted in the map as shown in the following short snippet:

func main() { 
   hist := make(map[int]string) 
   hist["Jan"] = 100 
   hist["Feb"] = 445 
   hist["Mar"] = 514 
... 
} 

golang.fyi/ch07/maptypes.go

The make function takes as argument the type of the map and it returns an initialized map. In the previous example, the make function will initialize a map of type map[int]string. The make function can optionally take a second parameter to specify the capacity of the map. However, a map will continue to grow as needed ignoring the initial capacity specified.

Using maps

As is done with slice and arrays, index expressions are used to access and update the elements stored in maps. To set or update a map element, use the index expression, on the left side of an assignment, to specify the key of the element to update. The following snippet shows an element with the "Jan" key being updated with the value 100:

hist := make(map[int]string) 
hist["Jan"] = 100 

Accessing an element with a given key is done with an index expression, placed on the right side of an assignment, as shown in the following example, where the value indexed with the "Mar" key is assigned the val variable:

val := hist["Mar"] 

Earlier it was mentioned that accessing a non-existent key will return the zero-value for that element. For instance, the previous code would return 0 if the element with the key "Mar" does not exist in the map. As you can imagine, this can be a problem. How would you know whether you are getting an actual value or the zero-value? Fortunately, Go provides a way to explicitly test for the absence of an element by returning an optional Boolean value as part of the result of an index expression, as shown in the following snippet:

func save(store map[string]int, key string, value int) { 
   val, ok := store[key] 
   if !ok { 
         store[key] = value 
   }else{ 
         panic(fmt.Sprintf("Slot %d taken", val)) 
   } 
} 

golang.fyi/ch07/map_use.go

The function in the preceding snippet tests the existence of a key before updating its value. Called the comma-ok idiom, the Boolean value stored in the ok variable is set to false when the value is not actually found. This allows the code to distinguish between the absence of a key and the zero value of the element.

Map traversal

The for…range loop statement can be used to walk the content of a map value. The range expression emits values for both key and element values with each iteration. The following code snippet shows the traversal of map hist:

for key, val := range hist { 
   adjVal := int(float64(val) * 0.100) 
   fmt.Printf("%s (%d):", key, val) 
   for i := 0; i < adjVal; i++ { 
         fmt.Print(".") 
   } 
   fmt.Println() 
} 

golang.fyi/ch07/map_use.go

Each iteration returns a key and its associated element value. Iteration order, however, is not guaranteed. The internal map iterator may traverse the map in a different order with each run of the program. In order to maintain a predictable traversal order, keep (or generate) a copy of the keys in a separate structure, such as a slice for instance. During traversal, range over the slice of keys to traverse in a predictable manner.

Note

You should be aware that update done to the emitted value during the iteration will be lost. Instead, use an index expression, such as hist[key] to update an element during iteration. For details on for…range loop, refer to Chapter 3, Go Control Flow, for a thorough explanation of Go for loops.

Map functions

Besides the make function, discussed earlier, map types support two additional functions discussed in the following table:

Function

Description

len(map)

As with other composite types, the built-in len() function returns the number of entries in a map. For instance, the following would print 3:

h := map[int]bool{3:true, 7:false, 9:false}   
fmt.Println(len(h))   

The len function will return zero for an uninitialized map.

delete(map, key)

The built-in delete function deletes an element from a given map associated with the provided key. The following code snippet would print 2:

h := map[int]bool{3:true, 7:false, 9:false}   
delete(h,7)   
fmt.Println(len(h))   

Maps as parameters

Because a map maintains an internal pointer to its backing storage structure, all updates to map parameter within a called function will be seen by the caller once the function returns. The following sample shows a call to the remove function to change the content of a map. The passed variable, hist, will reflect the change once the remove function returns:

func main() { 
   hist := make(map[string]int) 
   hist["Jun"] = 644 
   hist["Jul"] = 113 
   remove(hit, "Jun") 
   len(hist) // returns 1 
} 
func remove(store map[string]int, key string) error { 
   _, ok := store[key] 
   if !ok { 
         return fmt.Errorf("Key not found") 
   } 
   delete(store, key) 
   return nil 
} 

golang.fyi/ch07/map_use.go

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.133.160