Comienzo sección nueva, en este caso son pequeños post que trataran sobre algún truco para trabajar con Go, como todo existen varias formas de hacer, estas serán solo algunas posibles no siempre serán las mejores pero seguro que funcionan ;)
En este primer post vamos a ver como comprobar si un elemento existe dentro de un array(o slice), lo veremos con strings pero puede ser con cualquier tipo(primitivo por lo menos)
Como sabemos no tenemos forma directa en Go de comprobar si algo existe dentro de un array por lo que debemos implementarlo nosotros.
Una forma de hacerlo es la habitual de recorrer el array e ir comprobando pero realmente tenemos un par de formas(mínimo) más que yo creo que pueden ser más eficientes, aunque siempre depende del caso concreto.
Primera:
Veamos una típica recorriendo el array
list := []string{"a", "z", "j", "f", "i", "v", "r", "c", "l", "p", "h", "w", "k", "e", "u", "s", "b"}
for _, v := range list {
if v == "b" {
fmt.Println("Existe")
break
}
}
Método bastante sencillo, recorremos el array y comprobamos si es lo que buscamos, en cuanto lo encuetre salimos del for. Es bastante rápido la verdad.
Segunda:
list := []string{"a", "x", "z", "j", "f", "i", "v", "r", "c", "l", "p", "h", "w", "k", "e", "u", "s", "b"}
set := make(map[string]bool)
for _, v := range list {
set[v] = true
}
fmt.Println(set["b"])
Tenemos una array de strings desordenado, lo que hacemos es crear un map(lo que seria un diccionario en otros lenguajes), de clave=valor donde la clave es el contenido del array, y como valor le he puesto true, para que cuando comprobemos que existe muestre un true(aunque eso pasaria con todos), es un poquito de azuquitar visual que hemos puesto. Realmente nos pasaria lo mismo si intentamos asignar el valor de forma corta, como tal recordar que los maps al asignarlos a otra variable nueva devuelven realmente 2 valores, el contenido del map y si existe o no.
Como veis bastante sencillo, tenemos un for si o si pero es bastante rápido. Fijaos que le he puesto el último valor con la idea de que al final haremos pruebas de cuanto tarda cada uno.
Tercera:
list := []string{"a", "x", "z", "j", "f", "i", "v", "r", "c", "l", "p", "h", "w", "k", "e", "u", "s", "b"}
sort.Strings(list)
i := sort.SearchStrings(list, "b")
fmt.Println(list[i] == "b")
Lo primero que hacemos es ordenar el array y luego nos valemos del método SearchStrings(tiene search de todo) que tiene el objeto sort, le pasamos el array y lo que queremos buscar y nos devuelve la posición en la que se encuentra. A continuación comprobamos si el array tiene en la posición recibida el string que buscamos y voilá.
OJO!! Hacemos la comprobación de list[i] == "b" porque el método SearchStrings devuelve la posición donde deberiamos insertar el elemento en el caso de que no exista para que este ordenado directamente al añadirlo
Esta forma a mi me gusta más pero en grandes cantidades de datos realmente tendriamos que probar su eficienca
Pruebas VERSUS
Vamos a ver la diferencia de lo que tarda cada uno, esto no es del todo válido ya que depende de muchos factores pero es solo por ver lo que podria ser un acercamiento.
Vamos a crear un bucle rápido con cada tipo, que nos muestre el tiempo que ha tardado, pondremos unas 50 ejecuciones de cada uno para ver un poco la media, pero siempre tener en cuenta que afecta la cantidad de código que tengamos un simple Print puede alterar los valores:
Primer método
j := 0
for {
t1 := time.Now()
list := []string{"a", "z", "j", "f", "i", "v", "r", "c",
"l", "p", "h", "w", "k", "e", "u", "s", "b"}
for _, v := range list {
if v == "b" {
//Aqui podriamos hacer que muestre true o algo asi
break
}
}
fmt.Println("Tiempo total: ", time.Since(t1))
if j == 50 {
break
}
j++
Seguiremos la misma linea para los 3, creamos una especie de bucle while en el que cuando sea 50 la variable j se salga de la funcion.
Esta nos daria algo parecido a esto:
La primera ejecución tarda un poco mas y luego se mantiene, muy rápido no??(el array es muy pequeño jejejejje)
Segundo método:
j := 0
for {
t1 := time.Now()
list := []string{"a", "z", "j", "f", "i", "v", "r", "c", "l", "p", "h", "w", "k", "e", "u", "s", "b"}
set := make(map[string]bool)
for _, v := range list {
set[v] = true
}
_ = set["b"]
fmt.Println("Tiempo total: ", time.Since(t1))
if j == 50 {
break
}
j++
Nos da unos tiempos de este estilo
Bien aqui vemos que hemos bastante mas lentos en comparación, le he puesto el _ = set["b"] para simular que haciamos algo para comprobar.
Este método tiene una ventaja, y es que si queremos buscar algo más ya lo tenemos en el map set. Como desventaja es que posiblemente consumamos recursos innecesariamente.
Tercer Método:
j := 0
for {
t1 := time.Now()
list := []string{"a", "z", "j", "f", "i", "v", "r", "c", "l", "p", "h", "w", "k", "e", "u", "s", "b"}
sort.Strings(list)
i := sort.SearchStrings(list, "b")
_ = (list[i] == "b")
fmt.Println("Tiempo total: ", time.Since(t1))
if j == 50 {
break
}
j++
Y con este ultimo obtenemos:
Vemos que es más rápido que el segundo pero menos que el primero. Como tal con este método si tenemos que realizar otra búsqueda como ya estaria ordenado seria más rápida, he probado y salen unos tiempos como estos:
Como veis llegamos a los nanosegundos, que no esta mal.
Al final todo esto es orientativo, siempre dependerá del entorno y la cantidad de elementos que tenga el array. Es bastante probable que la primera forma sea la más rápida en la mayoría de los entornos pero al final deberemos valorarlo en el momento. También es cierto que es más fácil crearnos un método de utilidad con la primera opción que con el resto, podríamos generalizar usando interface o cualquier cosa que se nos ocurra. Siendo esto bastante útil si pensamos en la concurrencia como Go funciona con copias de elementos(a no ser que usemos punteros) no tendríamos "condiciones de carrera"(race conditions) a la hora de acceder a los elementos.
Espero que os haya parecido interesante, nos vemos en el siguiente NINJA POST :)
P.D: Para el que no sepa lo que es el race condition, básicamente es la posibilidad de que se este accediendo al mismo elemento y al mismo tiempo, lo que generaría varios problemas(cosa que en Go si trabajamos con copias no tendríamos)




