Ordenar y buscar en slices en Go

Desde Go 1.21 hay un paquete slices en la biblioteca estándar que hace casi todo esto en una línea. Sustituye al viejo sort, que sigue funcionando pero pedía bastante más ceremonia.

Lo más simple

nums := []int{7, 2, 9, 4, 2}
slices.Sort(nums)
fmt.Println(nums)
salida[2 2 4 7 9]

Ordena en el sitio: no devuelve nada, modifica el slice que le pasas. Si necesitas conservar el original, slices.Sort(slices.Clone(nums)).

Y con cadenas, cuidado

nombres := []string{"Marta", "ana", "Luis"}
slices.Sort(nombres)
fmt.Println(nombres)
salida[Luis Marta ana]

«ana» va al final. No es un fallo: la comparación es por el valor de los caracteres, y las mayúsculas van antes que las minúsculas. Si quieres el orden que espera una persona, hay que decirlo:

slices.SortFunc(nombres, func(a, b string) int {
	return strings.Compare(strings.ToLower(a), strings.ToLower(b))
})
salida[ana Luis Marta]

Ordenar structs

SortFunc recibe una función que compara dos elementos y devuelve un número: negativo si el primero va antes, positivo si va después, cero si da igual.

slices.SortFunc(ps, func(a, b Producto) int {
	if a.Precio < b.Precio {
		return -1
	}
	if a.Precio > b.Precio {
		return 1
	}
	return 0
})
salidaBufanda     9.00  2
Gorra      12.50  7
Camiseta   19.90  3
Mochila    34.00  7
Con números enteros se puede abreviar devolviendo la resta: return a.Unidades - b.Unidades. Con float64 no, porque la resta no es un int; y con enteros muy grandes tampoco, porque la resta puede desbordar. Las tres comparaciones son siempre seguras.

Por dos criterios

slices.SortFunc(ps, func(a, b Producto) int {
	if a.Unidades != b.Unidades {
		return b.Unidades - a.Unidades        // más unidades primero
	}
	return strings.Compare(a.Nombre, b.Nombre) // y a igualdad, por nombre
})
salidaGorra     7
Mochila   7
Camiseta  3
Bufanda   2

El patrón es siempre ese: comprueba el primer criterio y, solo si empata, pasa al siguiente. Fíjate en que el orden descendente se consigue invirtiendo la resta, b - a.

Buscar

ord := []int{2, 2, 4, 7, 9}

fmt.Println(slices.Contains(ord, 7))
fmt.Println(slices.Index(ord, 4))

i, hay := slices.BinarySearch(ord, 7)
fmt.Printf("posición %d, encontrado %v\n", i, hay)

i, hay = slices.BinarySearch(ord, 5)
fmt.Printf("posición %d, encontrado %v\n", i, hay)
salidatrue
2
posición 3, encontrado true
posición 3, encontrado false

Las dos primeras recorren el slice de principio a fin y valen para cualquiera. BinarySearch es mucho más rápida pero exige que el slice esté ordenado; sobre uno desordenado no da error, da una respuesta equivocada.

Y fíjate en la última línea: buscando el 5, que no está, devuelve 3 y false. Ese 3 es la posición donde habría que insertarlo para mantener el orden, que es justo lo que necesitas para insertar ordenado.

El resto del paquete

fmt.Println(slices.Max(ord), slices.Min(ord))
fmt.Println(slices.Compact(slices.Clone(ord)))   // quita duplicados seguidos
slices.Reverse(nums)
salida9 2
[2 4 7 9]

Compact elimina los repetidos consecutivos, así que solo sirve para quitar duplicados si antes has ordenado. Es la misma lógica del uniq de Unix.

El paquete sort, por si lo ves

sort.Ints(viejo)
sort.Strings(nombres)
sort.Slice(ps, func(i, j int) bool { return ps[i].Precio < ps[j].Precio })

Sigue funcionando y hay muchísimo código escrito con él. Las diferencias: sort.Slice recibe índices en vez de elementos y devuelve un bool en vez de un número. Para código nuevo, slices.

Para Usa
Ordenar números o cadenas slices.Sort
Ordenar por un criterio propio slices.SortFunc
Mantener el orden de los empatados slices.SortStableFunc
Saber si está slices.Contains
Buscar en algo ya ordenado slices.BinarySearch
Máximo y mínimo slices.Max, slices.Min
Quitar duplicados Ordenar y luego slices.Compact

Todo el código se compiló y ejecutó con Go 1.25 antes de publicarla; las salidas están copiadas de esa ejecución. Documentación oficial: paquete slices.