Librería lo para Go: Utilidades Genéricas (Lodash para Go)
En el desarrollo con Go, a menudo surge la necesidad de funciones prácticas para trabajar con colecciones, similares a las que existen en otros lenguajes (como map, filter, reduce de JavaScript o Python). La biblioteca estándar de Go ofrece un conjunto mínimo de herramientas, y escribir bucles for repetitivos para cada operación se vuelve tedioso. Aquí es donde entra en juego la librería lo: un potente conjunto de utilidades genéricas, inspirado en la popular librería JavaScript Lodash.
La librería lo utiliza genéricos (tipos genéricos), disponibles desde Go 1.18+, para proporcionar funciones tiposeguras para trabajar con slices, maps, canales y otras estructuras de datos. En lugar de escribir for i := range slice { ... } para cada transformación, puedes usar una sola línea de código: lo.Map(slice, transformFunc). Esto hace que el código sea más legible, conciso y menos propenso a errores.
¿Por qué usar lo? Primero, ahorra tiempo: dejas de escribir código boilerplate. Segundo, aumenta la fiabilidad: las funciones de la librería están minuciosamente probadas y cubren muchos casos extremos. Tercero, aporta uniformidad: todo el equipo usa las mismas funciones con nombres claros, lo que simplifica las revisiones de código. lo es una herramienta imprescindible para cualquier proyecto Go que maneje colecciones de datos de forma intensiva.
Instalación
La instalación de la librería lo se realiza de la forma estándar en Go mediante go get. Asegúrate de tener instalada la versión Go 1.18 o superior (ya que la librería usa genéricos).
go get github.com/samber/lo@latest
Después de ejecutar este comando, la librería se añadirá a tu archivo go.mod. Ahora puedes importarla en tu código:
import "github.com/samber/lo"
Inicio rápido
Escribamos un programa sencillo que use lo para filtrar y transformar un slice de números.
package main
import (
"fmt"
"github.com/samber/lo"
)
func main() {
// Slice original de números
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// Filtramos solo los números pares
evenNumbers := lo.Filter(numbers, func(x int, _ int) bool {
return x%2 == 0
})
fmt.Println("Números pares:", evenNumbers) // [2 4 6 8 10]
// Duplicamos cada número
doubled := lo.Map(evenNumbers, func(x int, _ int) int {
return x * 2
})
fmt.Println("Pares duplicados:", doubled) // [4 8 12 16 20]
// Sumamos todos los números
sum := lo.Sum(doubled)
fmt.Println("Suma:", sum) // 60
}
Como ves, el código es mucho más declarativo y legible. Hemos evitado escribir tres bucles for separados.
Métodos y funciones principales
A continuación, se muestra una lista de las funciones clave de la librería lo con ejemplos de uso. La mayoría de las funciones aceptan un slice y una función de callback.
1. lo.Filter
Firma: func Filter[T any](collection []T, predicate func(item T, index int) bool) []T
Qué hace: Crea un nuevo slice que contiene solo los elementos del slice original para los cuales la función predicado devuelve true.
package main
import (
"fmt"
"github.com/samber/lo"
)
func main() {
names := []string{"Ana", "Luis", "Carlos", "David", "Eva"}
// Dejamos solo los nombres con más de 3 caracteres
longNames := lo.Filter(names, func(name string, _ int) bool {
return len(name) > 3
})
fmt.Println(longNames) // [Carlos David]
}
2. lo.Map
Firma: func Map[T any, R any](collection []T, iteratee func(item T, index int) R) []R
Qué hace: Transforma cada elemento de un slice aplicando una función, y devuelve un nuevo slice con los resultados.
package main
import (
"fmt"
"github.com/samber/lo"
)
func main() {
numbers := []int{1, 2, 3, 4, 5}
// Elevamos al cuadrado cada número
squares := lo.Map(numbers, func(x int, _ int) int {
return x * x
})
fmt.Println(squares) // [1 4 9 16 25]
}
3. lo.Reduce
Firma: func Reduce[T any, R any](collection []T, accumulator func(agg R, item T, index int) R, initial R) R
Qué hace: Reduce un slice a un único valor aplicando una función acumuladora de izquierda a derecha, comenzando con un valor inicial.
package main
import (
"fmt"
"github.com/samber/lo"
)
func main() {
numbers := []int{1, 2, 3, 4, 5}
// Calculamos el producto de todos los números
product := lo.Reduce(numbers, func(agg int, item int, _ int) int {
return agg * item
}, 1)
fmt.Println(product) // 120
}
4. lo.Find
Firma: func Find[T any](collection []T, predicate func(item T) bool) (T, bool)
Qué hace: Busca el primer elemento en un slice que cumple la condición dada. Devuelve el elemento y un booleano indicando si se encontró.
package main
import (
"fmt"
"github.com/samber/lo"
)
func main() {
ages := []int{15, 22, 18, 30, 12}
// Buscamos el primer mayor de edad (edad >= 18)
adult, found := lo.Find(ages, func(age int) bool {
return age >= 18
})
if found {
fmt.Println("Primer adulto encontrado:", adult) // 22
}
}
5. lo.Uniq
Firma: func Uniq[T comparable](collection []T) []T
Qué hace: Elimina elementos duplicados de un slice, manteniendo el orden de la primera aparición.
package main
import (
"fmt"
"github.com/samber/lo"
)
func main() {
items := []string{"manzana", "pera", "manzana", "uva", "pera"}
uniqueItems := lo.Uniq(items)
fmt.Println(uniqueItems) // [manzana pera uva]
}
6. lo.Chunk
Firma: func Chunk[T any](collection []T, size int) [][]T
Qué hace: Divide un slice en sub-slices (fragmentos) del tamaño especificado. Útil para procesamiento por lotes.
package main
import (
"fmt"
"github.com/samber/lo"
)
func main() {
numbers := []int{1, 2, 3, 4, 5, 6, 7}
chunks := lo.Chunk(numbers, 3)
fmt.Println(chunks) // [[1 2 3] [4 5 6] [7]]
}
7. lo.Flatten
Firma: func Flatten[T any](collection [][]T) []T
Qué hace: Aplana un slice de slices en un solo slice (una sola dimensión).
package main
import (
"fmt"
"github.com/samber/lo"
)
func main() {
matrix := [][]int{{1, 2}, {3, 4}, {5, 6}}
flat := lo.Flatten(matrix)
fmt.Println(flat) // [1 2 3 4 5 6]
}
8. lo.Sum
Firma: func Sum[T constraints.Integer | constraints.Float](collection []T) T
Qué hace: Calcula la suma de todos los elementos numéricos de un slice.
package main
import (
"fmt"
"github.com/samber/lo"
)
func main() {
prices := []float64{10.5, 20.3, 5.2}
total := lo.Sum(prices)
fmt.Printf("Total: %.2f\n", total) // Total: 36.00
}
Conclusión
La librería lo es una herramienta esencial para cualquier desarrollador Go que quiera escribir código más limpio, eficiente y expresivo. Con funciones como Filter, Map, Reduce y muchas más, puedes olvidarte de los bucles repetitivos y centrarte en la lógica de tu aplicación. Además, al estar basada en genéricos, ofrece total seguridad de tipos. ¡No dudes en integrarla en tu próximo proyecto Go!