Jesús Mendoza

Notas de Go - Arrays, Slices y Maps

Tipos de datos compuestos

Los tipos de datos compuestos son aquellos que pueden almacenar uno o más valores. En Go existen diferentes tipos de datos compuestos, los que permiten almacenar uno o más valores de un solo tipo o aquellos que permiten almacenar uno o más valores de uno o más tipos diferentes.

Arrays

Los arrays son una secuencia de elementos numerados que nos permite almacenar valores de un mismo tipo con una longitud definida. Es decir, si queremos almacenr una serie de datos del mismo tipo usaríamos un array. Por ejemplo, para definir un array:

var arr [5]int

En el ejemplo anterior estamos definiendo una variable de tipo array con nombre arr, con una longitud de 5 elementos y cada de esos elementos tiene que ser de tipo int. Luego podemos llenar esa "serie" de datos de la siguiente manera:

arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
arr[5] = 6 // fallará

Como ya mencionamos anteriormente, los arrays son una serie de elementos numerados, por este motivo podemos acceder al "índice" de cada elemento usando 0, 1, 2, 3, 4, dependiendo de la longitud de nuestro array. Hay que tener en cuenta que en programación, todos los índices empiezan a contar desde 0. Por este motivo la operación arr[5] fallará ya que nuestro array es de una longitud de 5 elementos y el índice 5 (que sería el elemento 6) está fuera de nuestro rango.

Slices

Un slice es un segmento de un array. Igua que los arrays, los slices son indexados, es decir, tienen una numeración desde que empieza desde el 0 y se extiende hasta su longitud-1. También tienen una longitud pero a diferencia de los arrays esta longitud puede variar. Por ejemplo:

var slc []int

La única diferencia de esta declaración con la de un array es que no definimos la longitud dentro de los brakets []. En este caso un slice con longitud de 0 ha sido creado.

Otra forma de crear un slice es usando make:

slc := make([]int, 5, 10)
slc[0] = 10

fmt.Println(slc[0]) // 10

En el ejemplo anterior estamos creando un slice usando la función make. El primer argumento es el tipo de valores que queremos almacenar, el segundo es la longitud y el tercero representa la capacidad que puede tener. Note que para acceder al valor en el índice 0 usamos la notación slc[0] donde 0 es el índice.

Otra forma de crear slice es usando la notación [low : high]:

arr := [5]int{1, 2, 3, 4, 5}
slc := arr[0:5]

fmt.Println()

En el ejemplo anterior primero tenemos un array de 5 elementos. Luego, en la segunda línea copiamos los elementos en un slice usando la notación [low : high]. El primer element es el array del que vamos a copiar, el 0 representa el índice desde donde queremos empezar a copiar y el 5 representa el índice hasta donde queremos copiar. Si quisieramos copiar todo el array también podríamos usar arr[:] de esta forma Go sabría que queremos empezar desde el índice 0 hasta la longitud - 1.

Cómo ya mencionamos anteriormente, un slice es similar a un array pero con la diferencia de que su longitud puede variar. ¿Por que deberíamos usar slices en vez de arrays? Por los métodos a los que tenemos acceso cuando usamos slice.

Métodos de Slices

Go tiene muchos funciones construidas dentro del lenguaje, las que más usaremos para manipular slices son: append y copy.

El método append crea un nuevo slice y le agrega los elementos que pasemos como argumento:

slc := []int{1, 2, 3, 4, 5}
newSlc := append(slc, 6, 7)

fmt.Println(newSlc) // 1, 2, 3, 4, 5, 6, 7

El método copy copia los elementos de un slice dentro de otro:

slc1 := []int{1, 2, 3, 4, 5}
slc2 := []int{6, 7}
copy(slc1, slc2)
fmt.Println(slc1) // 6, 7, 3, 4, 5

El resultado de ejecutar copy es un poco raro, el motivo por el que copia 6 y 7 en el índice 0 y 2 es por que slc1 solo tiene una longitud de 5 elementos, por los que copy intenta buscar donde puede copiar los nuevos elementos y los copia en los primeros índices.

Map

Son una lista desordenada de llaves-valor. También conocidos como un array asociativo, un hash table o diccionario. Se usan para buscar valores usando una llave. Por ejemplo:

var x = map[string]int{}

En el ejemplo anterior estamos definiendo una variable x que almacena un map cuya llave debe ser un string y su valor debe ser un int. Al igual que los array y slice, podemos usar la notación de brackets [] para acceder a un elemento de un map. Si queremos asignarle alguna llave valor a nuestro nuevo map podemos usar:

var x = map[string]int {}

x["edad"] = 29

fmt.Println(x["edad"]) // 29

En el ejemplo anterior x["edad"] se refiere a la llave "edad" y 29 es el valor. En el print estamos usando x["edad"] para acceder a el valor almacenado en la llave "edad" e imprimirlo.

Puede parecer muy similar a un array pero hay varias diferencias. En primer lugar, la longitud de un map va creciendo conforme vamos añadiendo más llaves-valor. Segundo, los map no son secuenciales, es decir, no empiezan en el índice 0 y van aumentando de 1, se podría decir que los índices en un map son las llaves.

Si queremos verificar que existe un valor en una llave-valor en nuestro map podemos usar una de las siguientes maneras:

if x["edad"] == 0 {
  fmt.Println("No existe")
}

Si el tipo del valor es int la expresión x["edad"] retornará 0, si es string retornará "", si es booleano retornará false, etc. Esto es muy tedioso, así que podemos usar una de las formas que nos provee Go para hacerlo más sencillo:

if edad, ok := x["edad"]; !ok {
  fmt.Println("No existe")
}

En el ejemplo anterior podemos observar que estamos definiendo dos variables en una sola línea, edad y ok, si x["edad"] tiene un valor, se le asignará a la variable de edad y ok obtendrá el valor true, si no existe, ok obtendrá false. Justo después del ; con el !ok verificamos si ok es true o false y ejecutamos lo que esté dentro del if.

Para borrar un elemento de un map podemos usar la función delete:

delete(x, "edad")

Para recorrer un map podemos usar:

for key, value := range x {
  fmt.Println(key, value) // edad 29
}

En resumen

Existen diferentes tipos de datos compuestos, los arrays, slices nos permiten almacenar valores de forma ordenada y numerada y los maps nos permiten almacenar valores de una forma a la que podemos acceder facilmente a ellos.

En el siguiente post seguiremos hablando de tipos de datos compuestos