-
-
Save hevela/8c4ce085b58fbec57c38b96a83ad4809 to your computer and use it in GitHub Desktop.
Introduction to maps
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"sort" | |
"strings" | |
) | |
func main() { | |
// Introduction to Maps | |
introToMaps() | |
// Working with Map Elements | |
workingWithMapElements() | |
// Map Operations | |
mapOperations() | |
// Iterating Over Maps | |
loopingThroughMaps() | |
loopingMapsInOrder() | |
// Maps and Reference Types: | |
referenceMaps() | |
// Map Key and Values of Different Types | |
complexMaps() | |
// Map Use Cases | |
useCases() | |
// common errors | |
commonErrors() | |
// Exercise | |
urlEncode() | |
} | |
func introToMaps() { | |
// Mapas son una de las estructuras de datos más útiles. La mayoría de los lenguajes de programación | |
// cuentan con una implementación (Diccionarios en Python, HashMaps en Java, Objetos en Javascript, arreglos asociativos en PHP, etc). | |
// Son contenedores donde podemos colocar información para después encontrarla fácilmente | |
// Declaración | |
// Un mapa en Go luce así: map[KeyType]ValueType | |
// donde KeyType es un tipo [comparable](https://go.dev/ref/spec#Comparison_operators), | |
// (boolean, numeric, string, pointer, channel, interface, y structs o arrays que contengan solo estos tipos de valores. | |
// ValueType puede ser cualquier cosa | |
var products map[string]float64 | |
fmt.Println(fmt.Sprintf("products is of type %T and its value is %#v", products, products)) | |
fmt.Println(products == nil) | |
// al leerlo, un mapa nulo (nil) se comporta como un mapa vacío para lecturas, | |
// pero si se intenta escribir en él, habrá un panic en tiempo de ejecución | |
fmt.Println(products["non existent key"]) | |
// panic: assignment to entry in nil map | |
//products["Gorra Mario"] = 99.99 | |
//----------------- | |
// Inicialización | |
// Para inicializar un mapa, podemos usar la función _make_ | |
var products2 = make(map[string]float64) | |
products = map[string]float64{} | |
products2["Gorra Mario"] = 99.99 | |
products["Gorra Mario"] = 99.99 | |
fmt.Println(fmt.Sprintf("products2 is of type %T and its value is %#v", products2, products2)) | |
// también se pueden inicializar con la sintaxis abreviada | |
products3 := map[string]float64{} | |
products3["Gorra Luigi"] = 89.99 | |
fmt.Println(fmt.Sprintf("products3 is of type %T and its value is %#v", products3, products3)) | |
} | |
func workingWithMapElements() { | |
// la sintaxis para trabajar con mapas es familiar y sencilla | |
// inicializando un mapa con valores | |
account := map[string]string{ | |
"dev": "691484518277", | |
"qa": "691515518215", | |
"stg": "632515518875", | |
} | |
// asignando valores a un mapa existente | |
account["prod"] = "369524578943" | |
fmt.Println(fmt.Sprintf("%#v", account)) | |
// ----------- | |
//// obteniendo valores de un mapa | |
devAccount := account["dev"] | |
fmt.Println(fmt.Sprintf("%q", devAccount)) | |
// ----------- | |
//// al acceder a una llave no existente, obtenemos el "zero value" del tipo del valor del mapa | |
uatAccount := account["uat"] | |
fmt.Println(fmt.Sprintf("%q", uatAccount)) | |
} | |
func mapOperations() { | |
// Go incluye varias funciones que nos ayudan a trabajar con mapas | |
// Podemos contar las llaves que tiene un mapa | |
account := map[string]string{ | |
"dev": "691484518277", | |
"qa": "691515518215", | |
"stg": "632515518875", | |
"test": "", | |
} | |
fmt.Println("number of keys:", len(account)) | |
// ----------- | |
key := "dev" | |
//// podemos borrar una llave de un mapa | |
delete(account, key) | |
fmt.Println("number of keys:", len(account)) | |
// ----------- | |
//// podemos obtener el valor de un campo/llave de un mapa, pero si no está definido, obtenemos su zero value | |
value := account[key] | |
fmt.Println(fmt.Sprintf("the value of map[\"%s\"] is: %q", key, value)) | |
// ----------- | |
//// para revisar la existencia de una llave, usamos una asignación de dos valores | |
if _, ok := account[key]; !ok { | |
fmt.Println(fmt.Sprintf("key '%s' not found", key)) | |
return | |
} | |
fmt.Println(fmt.Sprintf("the value of map[\"%s\"] is: %q", "test", value)) | |
} | |
type numberToStr map[int]string | |
func loopingThroughMaps() { | |
// Podemos iterar un mapa con un for, usando range. | |
// Range nos provee una manera de recorrer cada elemento de un iterable por medio de su llave y valor | |
a := numberToStr{ | |
0: "zero", | |
1: "one", | |
2: "two", | |
3: "three", | |
4: "four", | |
5: "five", | |
6: "six", | |
7: "seven", | |
8: "eight", | |
9: "nine", | |
10: "ten", | |
11: "eleven", | |
12: "twelve", | |
13: "thirteen", | |
14: "fourteen", | |
15: "fifteen", | |
} | |
for key, value := range a { | |
fmt.Println(key, value) | |
} | |
// ----------- | |
// El orden de las llaves al iterar un mapa es aleatorio por diseño | |
// Esto es para incentivar a los desarrolladores a escribir código que no dependa del orden de las llaves | |
// de un mapa (eliminando el requerimiento de mantener el orden de las llaves) | |
// También evita que los desarrolladores hagan suposiciones sobre el orden | |
// Ejercicio: recorrer las llaves de un mapa en orden | |
} | |
func loopingMapsInOrder() { | |
a := numberToStr{ | |
0: "zero", | |
1: "one", | |
2: "two", | |
3: "three", | |
4: "four", | |
5: "five", | |
6: "six", | |
7: "seven", | |
8: "eight", | |
9: "nine", | |
10: "ten", | |
11: "eleven", | |
12: "twelve", | |
13: "thirteen", | |
14: "fourteen", | |
15: "fifteen", | |
} | |
// Extract the keys into a slice. | |
keys := make([]int, 0, len(a)) | |
for key := range a { | |
keys = append(keys, key) | |
} | |
fmt.Println(keys) | |
// Sort the keys. | |
sort.Ints(keys) | |
fmt.Println(keys) | |
// Iterate through the sorted keys and access the map elements. | |
for _, key := range keys { | |
fmt.Printf("%d: %s\n", key, a[key]) | |
} | |
} | |
func referenceMaps() { | |
// Los mapas son un tipo de dato de referencia. Es decir, la variable no contiene la data, | |
// sino que apunta a la locación de memoria donde están. | |
// Cuando se asigna una variable de tipo de referencia a otra variable o es pasada como | |
// argumento a una función, se está copiando la referencia a memoria, no la data | |
a := numberToStr{ | |
1: "one", | |
2: "two", | |
} | |
b := a | |
b[1] = "uno" | |
fmt.Println(b) | |
fmt.Println(a) | |
fmt.Println(mapsAreEqual(a, b)) | |
// Ejercicio: copiar un mapa | |
valueMaps() | |
} | |
func valueMaps() { | |
a := numberToStr{ | |
1: "one", | |
2: "two", | |
} | |
b := copyMap(a) | |
fmt.Println(b) | |
fmt.Println(a) | |
b[1] = "uno" | |
fmt.Println(mapsAreEqual(a, b)) | |
} | |
func complexMaps() { | |
// como fue mencionado, los mapas pueden contener valores de cualquier tipo y llaves de tipos comparables | |
type person struct { | |
firstName string | |
lastName string | |
} | |
type address struct { | |
streetName string | |
number string | |
neighborhood string | |
zipCode string | |
} | |
peopleAddresses := map[person][]address{ | |
person{ | |
firstName: "Makko", | |
lastName: "Vela", | |
}: []address{ | |
{ | |
streetName: "Evergreen Terrace", | |
number: "742", | |
neighborhood: "Henderson", | |
zipCode: "90210", | |
}, | |
{ | |
streetName: "Spalding Way", | |
number: "420", | |
neighborhood: "Adamsbert", | |
zipCode: "63637", | |
}, | |
}, | |
} | |
makko := person{ | |
firstName: "Makko", | |
lastName: "Vela", | |
} | |
fmt.Println(peopleAddresses[makko]) | |
// ----------- | |
// podemos tener mapas anidados | |
familyTree := map[string]map[string]string{ | |
"Juan": { | |
"father": "Miguel", | |
"mother": "Ana", | |
}, | |
"María": { | |
"father": "Alfredo", | |
"mother": "Guadalupe", | |
}, | |
} | |
// Accessing nested map values | |
fmt.Println("Juan's dad is:", familyTree["Juan"]["father"]) | |
fmt.Println("María's mom is:", familyTree["María"]["mother"]) | |
// ----------- | |
// incluso podemos guardar funciones como valores | |
type mathOperations map[string]func(a, b float64) float64 | |
calculator := mathOperations{ | |
"suma": func(a, b float64) float64 { | |
return a + b | |
}, | |
"resta": func(a, b float64) float64 { | |
return a - b | |
}, | |
"multiplicacion": func(a, b float64) float64 { | |
return a * b | |
}, | |
"division": func(a, b float64) float64 { | |
return a / b | |
}, | |
} | |
operands := []float64{3.0, 7.0} | |
for k, _ := range calculator { | |
result := calculator[k](operands[0], operands[1]) | |
fmt.Println(fmt.Sprintf("el resultado de %s es %f", k, result)) | |
} | |
} | |
func copyMap(map1 map[int]string) map[int]string { | |
newMap := map[int]string{} | |
for key, value := range map1 { | |
newMap[key] = value | |
} | |
return newMap | |
} | |
func useCases() { | |
// los casos de uso más comunes para usar mapas, son: | |
// asociación de datos | |
dictionary := map[string]string{ | |
"map": "a diagram or collection of data showing the spatial arrangement or distribution of something over an area", | |
} | |
fmt.Println(dictionary["map"]) | |
// contar coincidencias | |
song := "Baby Shark, doo-doo, doo-doo, doo-doo, Baby Shark, doo-doo, doo-doo, doo-doo, Baby Shark, doo-doo, doo-doo, doo-doo, Baby Shark" | |
song = strings.ReplaceAll(song, ",", "") | |
words := strings.Split(song, " ") | |
lyricMap := map[string]int{} | |
for _, word := range words { | |
if _, found := lyricMap[word]; !found { | |
lyricMap[word] = 1 | |
continue | |
} | |
lyricMap[word]++ | |
} | |
fmt.Printf("%#v\n", lyricMap) | |
// cache | |
cache("key") | |
cache("key") | |
// switch | |
switchMap := map[string]func(a, b float64) float64{ | |
"suma": func(a, b float64) float64 { | |
return a + b | |
}, | |
"resta": func(a, b float64) float64 { | |
return a - b | |
}, | |
"multiplicacion": func(a, b float64) float64 { | |
return a * b | |
}, | |
"division": func(a, b float64) float64 { | |
return a / b | |
}, | |
} | |
fmt.Println(switchMap["suma"](3, 4)) | |
fmt.Println(switchMap["division"](3, 4)) | |
} | |
func commonErrors() { | |
// nil map panic | |
var myMap map[string]string | |
myMap["value"] = "something" | |
// key not found | |
fmt.Printf("%#v\n", myMap["non-existing-key"]) | |
// pass by reference | |
myMap["key"] = "value" | |
updateKey(myMap) | |
fmt.Printf("%#v\n", myMap) | |
// concurrent access | |
} | |
func updateKey(myMap map[string]string) { | |
myMap["key"] = "some other value" | |
} | |
var cacheMap = map[string]int{} | |
func cache(key string) int { | |
data, ok := cacheMap[key] | |
if !ok { | |
// do some expensive operation here to get `data` | |
data = 123 | |
cacheMap[key] = data | |
fmt.Println("expensive operation") | |
return data | |
} | |
fmt.Println("serve from cache") | |
return data | |
} | |
func mapsAreEqual(map1, map2 map[int]string) bool { | |
// Step 1: Check if the maps have the same length. | |
if len(map1) != len(map2) { | |
return false | |
} | |
// Step 2: Compare the keys and values of the first map with the second map. | |
for key, value1 := range map1 { | |
value2, found := map2[key] | |
if !found || value1 != value2 { | |
fmt.Println("found:", found, value1, value2) | |
return false | |
} | |
} | |
return true | |
} | |
type urlValues map[string][]string | |
// Encode encodes the values into “URL encoded” form | |
// ("bar=baz&foo=quux") sorted by key. | |
// Key values are sorted as well. | |
func (v urlValues) Encode() string { | |
if v == nil { | |
return "" | |
} | |
uri := "" | |
keys := make([]string, 0, len(v)) | |
for k := range v { | |
keys = append(keys, k) | |
} | |
sort.Strings(keys) | |
for _, k := range keys { | |
vs := v[k] | |
sort.Strings(vs) | |
for _, v := range vs { | |
if len(uri) > 0 { | |
uri += "&" | |
} | |
uri += fmt.Sprintf("%s=%s", k, v) | |
} | |
} | |
return uri | |
} | |
func urlEncode() { | |
urlVals := urlValues{ | |
"status": []string{ | |
"suspended", | |
"active", | |
}, | |
"user": []string{ | |
"7123f734-680d-42bc-8c27-aef98e8c5590", | |
}, | |
"type": []string{ | |
"transfer", | |
"download", | |
}, | |
} | |
uri := urlVals.Encode() | |
// status=active&status=suspended&type=download&type=transfer&user=7123f734-680d-42bc-8c27-aef98e8c5590 | |
fmt.Println(uri) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment