No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Uso de Slices

15/42
Recursos

Aportes 32

Preguntas 0

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Los tres puntos al final de t.task[index + 1:]… (operador ellipsis) es porque el segundo parámetro del append no es un slice y la función append recibe un item, con este operador lo que hacemos es decirle a go que tome ese slice y lo “desempaquete” para que sean muchos parámetros de 1 solo item y no un slice.

Ejemplo:

t.tasks = append(t.tasks, task1, task2, task3)

Lo que haría el operador de ellipsis seria pasar un slice a este formato de muchos parámetros.

Creo que se debería explicar en este punto el uso de los 3 puntos (…) no solamente decir que sino sale error de sintaxis.
Gracias igual a la comunidad que si lo menciona 😃

Los arreglos (arrays) son una estructura de dato de tamaño fijo. Previamente tienen declarada su longitud y no puede ser mutada en tiempo de ejecución.

Los slices son una estructura de dato que se utiliza de manera similar a los arreglos pero con un comportamiento más similar a las listas; esto en el aspecto de que tienen una longitud variable.

Así como los arrays, los slices definen su tipo con el uso de corchetes, pero con la diferencia de que no tenemos que indicar su tamaño.

// Declaración de una variable de tipo slice
// con elementos de tipo string
var list []string

La función append sirve para agregar elementos a un slice. Ésta recibe como parámetros el slice y el elemento que se quiere agregar

// Agregando un elemento al slice
list = append(list, "element")

// Eliminando un elemento del slice
// [:index] Desde el inicio hasta el índice
// [index+1:] Desde el índice hasta el final
list = append(list[:index], list[index+1]...)

// Inicializando un slice
// cuando se hace con una línea
// go no solicita la coma del último elemento
list := []string{"study","clean","pay"}

// Cuando se hace multi-línea. si
list := []string{
	"study",
	"clean",
	"pay",
}

length := len(list)

El método len() nos sirve para saber la longitud de alguna estructura de datos, string, apuntador de un array o un channel.

Cuando eliminas un elemento del slice estas usando un comportamiento del slice que son los dos puntos (😃 que lo que hace es obtener los elementos que se indican ( inicio : final ) asi:
a[1:4] ; que basicamente es un rango de indices, similar a un substring(1,4).

11:35 No queda muy clara la explicación. Aquí la explicación.

Un punto importante que encontré en la documentación, los array en GO funcionan como un conjunto de valores, no como un conjunto de referencias, es decir, al enviarlo a una función, esta hace una copia de los valores. Los slices funcionan como referencias, internamente GO utiliza slices por encima de arreglos y recomienda usarlos siempre que sea posible:

https://www.godesignpatterns.com/2014/05/arrays-vs-slices.html

Yo entendí el operador (ellipsis) de Go como un operador de dispersión.
Lo que se hizo en el arreglo es dispersar los elementos del slice como argumentos en la función append.
Aquí un ejemplo:


package main

import "fmt"

func sumar(numeros ...int) int {
	result := 0
	for _, num := range numeros {
		result += num
	}
	return result
}

func main() {
	primos := []int{2, 3, 5, 7}
	fmt.Println(sumar(primos...)) // Resultado: 17
}

Implementación de la función append en los Go Docs:
![](

Y aquí un ejemplo en Javascript que produce el mismo resultado:

const sumar = (w, x, y, z) => w + x + y + z;

const primos = [2, 3, 5, 7];

console.log(sumar(...primos)); // Resultado: 17

Para los del barrio que venimos sabiendo C, los arrays de aqui son los mismos de C, y los slices de golang no son mas que los malloc de C, pero en golang no hay que hacer free :v

Otra cosa, la manera de como el agrega elementos uno a uno expandiendo la memoria por cada elemento nuevo es una mala practica que pesa mucho en el performance, eso no se hace, si quieren entender porque es algo largo y complejo, pero en pocas palabras cada ves que agregas una nueva variable se hace limpieza y realocacion, a parte de que es en la memoria virtual la cua es lenta, asi que hay que tratar de evitar hacer esta operaciones a cada rato, la solucion? hacer allocacion de memoria exponencial

Si quieren que haga un blog explicando a profundidad y de como y porque implementar un sistema asi comenten y asi me animo

los slice son mucho mas poderoso sobre los arreglos porque son mas dinamicos

package main

import "fmt"


type taskList struct {
	tasks []*task
}

func(t *taskList) agregarALista(tl *task) {
	t.tasks = append(t.tasks, tl)
}

func (t *taskList) eliminarDeLista(index int) {
	// ... desempaquetar los elementos
	t.tasks = append(t.tasks[:index], t.tasks[index + 1:]...)
}

type task struct {
	nombre      string
	descripcion string
	completado  bool
}

func (t *task) marcarCompleta() {
	t.completado = true
}

func (t *task) actualizarDescripcion(desc string) {
	t.descripcion = desc
}

func (t *task) actualizarNombre( nombre string) {
	t.nombre = nombre
}

func main() {
	t0 := &task{
		nombre:      "Completar curso go",
		descripcion: "Completar el curso de go en esta semana",
	}
	t1 := &task{
		nombre:      "Completar curso Python",
		descripcion: "Completar el curso de go en esta semana",
	}
	t2 := &task{
		nombre:      "Completar curso Mongo",
		descripcion: "Completar el curso de go en esta semana",
	}
	

	lista := &taskList{
		tasks: []*task{
			t0, t1,
		},
	}

	fmt.Println(lista.tasks[0])

	fmt.Println(len(lista.tasks))

	lista.agregarALista(t2)

	fmt.Println(len(lista.tasks))

	lista.eliminarDeLista(1)
	fmt.Println(len(lista.tasks))

	
}

Humildemente, me parece que hay complicaciones extras sin necesidad, puede ser un slice normal y las funciones de manipulación del slice usan apuntadores para hacer paso de parámetros por referencia.

me perdí con tanto pasos de valores con & y * y mas con struct

El primer caso: recibe como parámetros un slice y los elementos que se agregaran a ese slice.
El segundo caso: recibe como parámetros dos slice.
Para el ejemplo de agregar elementos se usa el primer caso.
Para el ejemplo de remover tareas se usa el segundo caso.

Los slices tienen una forma de excluir e incluir índices según sea nuestra conveniencia, por ejemplo:

slice := []int{0,1,2,3,4,5,6}
fmt.Println(slice[:3])

nos mostrara en consola

[0 1 2] es decir se excluye el valor de la posición 3 y se muestra sus antecesores.

fmt.Println(slice[2:4])

nos mostrará en consola
[2 3] lo podemos ver como un rango de valores a mostrar, es decir se incluye el valor de la posición 2 hasta la posición 4 excluyendo este último del resultado.

fmt.Println(slice[4:])

nos mostrará en consola
[4 5 6] es decir se incluye el valor del índice 4 en adelante
así mismo podemos agregar un slice a otro slice

newSlice := []int{7,8,9}
slice = append(slice, newSlice…)
//nos mostrara en consola
//[0 1 2 3 4 5 6 7 8 9]

Genial 😃

//Arrays tamaño fijo
var numbers1 [4]int
numbers2 := […]int{0,0,0,0}

//Slices tamaño dinamico
slice1 := []int{2,3,4}

los rreglos son un poco diferente porque una vez dimensionados no se puede cambiar su longitud

los arreglos en go son inmutables

task []*task estamos agregando valores y no la referencia

loas struct tiene proiedades, como pueden ser arreglos

len para ver la Longitud del slice

append es muy poderosos para manejar slice, lo cual, los slice son muy dinamicos

Creo que se podria explicar un poco mas en detalle las funciones del slice al menos las que se usan en el ejemplo.
Yo siendo la primera vez que veo el lenguaje me quedó mas claro leyendo los comentarios

No podríamos tener un slice de punteros task sin tener que crear todo un struct (taskList)?

Por ejemplo:

taskList := []*task{}

Tiene más sentido tener un struct solo si tenemos otros valores en taskList aparte del slice []*task

También hay que poner cuidado cuando usamos slices de apunteros, les dejo este post que da un buen ejemplo de un error que en mi opinión puede ser común.

comparto una mi versión de eliminar tarea, donde en vez de pasar el indice paso la tarea y la busco en la lista

func (ts *Tasks) deleteTask(t Task) error {
	idx := sort.Search(len(ts.tasks), func(i int) bool {
		return string(ts.tasks[i].name) >= t.name
	})

	fmt.Println(idx)

	if idx != -1 {
		ts.tasks = append(ts.tasks[:idx], ts.tasks[idx + 1:]...)
		return nil
	} else {
		return errors.New("La tarea a eliminar no se encuentra en la lista actual")
	}
}

Prara quienes quedaron en duda con el trabajo de los slices y su diferencia con los arrays les dejo aca las notas de lo que investigué

Arrays vs Slices

Go tiene la particularidad de tener 2 tipos primitivos de coleccion de datos

<h3>Arrays</h3>

Los array en Go son tratados como un tipo de dato, no como un apuntador o una referencia a un objeto, por lo que reasignarlo o pasarlo como argumento implica una copia uno a uno de los valores que lo conforman (por lo que podria llegar a ser realmente costoso).

Estos datos son de tamaño estatico los cuales almacenan una cantidad de elementos de un mismo tipo en un espacio de memoria la cual es ocupada tan pronto es declarado el array.

El tamaño del array es parte del tipo de dato por lo que no es posible asignar un [4]int a un [3]int.

[N]Type
[N]Type{value1, value2, ..., valueN}
[...]Type{value1, value2, ..., valueN}
<h3>Slices</h3>

Los slice son un tipo referenciable de dato que almacena una cantidad de elementos del mismo tipo, a diferencia de los array estos tienen un tamaño dinámico y se pasan por referencia en la asignación y como argumento.

Un slice consiste de 3 elementos: la longitud, la capacidad y el apuntador al array que contiene los datos.

Puede pensarse un slice como la implementación nativa de Go para los ArrayList o Vector, trayendo consigo las virtudes y defectos inherentes a esta estructura de datos.

make([]Type, length, capacity)
make([]Type, length)
[]Type{}
[]Type{value1, value2, ..., valueN}

Qué buen profe !!! todo muy claro. Mil gracias

Buenas, este sería mi aporte del código de esta clase:

package main

import "fmt"

type task struct {
	name        string
	description string
	completed   bool
}

type tasksList []task

func (t *task) update(tasks ...interface{}) {
	for i := 0; i < len(tasks); i++ {
		switch i {
		case 0:
			if tasks[i] != nil {
				t.name = tasks[0].(string)
			}
		case 1:
			if tasks[i] != nil {
				t.description = tasks[1].(string)
			}
		case 2:
			t.completed = tasks[2].(bool)
		}
	}
}

func (t task) add(tasks *tasksList) {
	*tasks = append(*tasks, t)
}

func (t *tasksList) remove(i int) {
	temp := *t
	temp = append(temp[:i], temp[i+1:]...)
	*t = temp
}

func main() {
	t := task{}
	var tasks tasksList

	t.update("Curso de GO", "Completar el curso de GO en esta semana", false)
	t.add(&tasks)

	t.update("Curso de Inglés", "Empezar el curso de Inglés cuando me mude", false)
	t.add(&tasks)

	t.update("Curso de SQL", "Empezar el curso de SQL cuando me mude", false)
	t.add(&tasks)

	tasks.remove(1)

	for _, v := range tasks {
		fmt.Printf("%v: %v. Completado: %v\n", v.name, v.description, v.completed)
	}
}

package main

import (
	"fmt"
)

type taskList struct {
	tasks []*task
}

func (t *taskList) agregarALista( tl *task) {
	t.tasks = append(t.tasks, tl)
}

func ( t *taskList) eliminarDeLista(index int) {
	t.tasks = append(t.tasks[:index], t.tasks[index + 1:]...)
}

type task struct{
	nombre string
	descripcion string
	completado bool
}

func (t *task) marcarCompleta(){
	t.completado = true
}

func (t *task) actualizaDescripcion(descripcion string) {
	t.descripcion = descripcion
}

func (t *task) actualizaNombre(nombre string) {
	t.nombre = nombre
}

func main() {
	t1 := &task{
		nombre: 	 "Completar mi curso de Go::",
		descripcion: "Completare mi curso de Go de Platzi en esta semana::",
	}

	t2 := &task{
		nombre: 	 "Completar mi curso de JavaScript::",
		descripcion: "Completare mi curso de JavaScript de Platzi en esta semana::",
	}

	t3 := &task{
		nombre: 	 "Completar mi curso de NodeJS::",
		descripcion: "Completare mi curso de NodeJS de Platzi en esta semana::",
	}

	lista := &taskList {
		tasks: []*task{
			t1, t2,
		},
	}

	fmt.Println(lista.tasks[0])
	lista.agregarALista(t3)
	fmt.Println("elementos:", len(lista.tasks))

	lista.eliminarDeLista(1)
	fmt.Println("elementos:", len(lista.tasks))

}

Me quedaron dudas con el uso de los tres puntos. Se utiliza cuando no se sabe la aridad o la cantidad de argumentos que puede tener una función, como el número de elementos es variable, la función se llama con esta sintaxis que tiene Go. Acá hay un ejemplo que me generó mayor claridad. Además se habla de manera amplia del concepto.

func remove(slice []int, s int) []int {
    return append(slice[:s], slice[s+1:]...)
}

Basicamente toma la primera parte del slice hasta donde está el elemento que queremos eliminar y luego le agregamos el resto del slice después del elemento que queremos eliminar. De esta forma mantenemos el orden y eliminamos el elemento.