Lección 10 · 10 de 20
sort.Slice y sort.Interface · Go
Ordene structs en Go con sort.Slice, sort.SliceStable y sort.Interface, invierta el orden con sort.Reverse y busque con sort.Search.

El paquete sort ordena de dos formas. La clásica es `sort.Interface`: un tipo con tres métodos, Len, Less y Swap. ByName es un []Product con otro nombre y esos tres métodos; sort.Sort(ByName(ps)) convierte el slice (sin copiarlo: comparten los datos) y lo ordena por nombre.
La forma rápida es `sort.Slice`: recibe el slice y una función less(i, j) que dice si el elemento i va antes que el j. El closure usa ps directamente, y así se ordena por precio en una línea, sin declarar ningún tipo.
Con el slice ordenado por precio, `sort.Search` hace una búsqueda binaria: devuelve el primer índice en el que la condición es verdadera, aquí el primer producto de 30 EUR o más, Lamp. Solo funciona si el slice está ordenado por lo mismo que se busca; si ninguno cumple, devuelve len(ps).
sort.Slice no garantiza el orden de los elementos iguales. `sort.SliceStable` sí: al ordenar por categoría, dentro de office y de tech se conserva el orden anterior por precio (Lamp antes que Desk, Cable antes que Mouse). Así se ordena por varios criterios: primero el secundario y luego, estable, el principal.
`sort.Reverse` envuelve cualquier sort.Interface y le da la vuelta a Less: orden alfabético inverso sin escribir otro tipo. El método String de Product (lección del Stringer) hace que %v escriba cada producto como Mouse/tech/25.
Código · main.go
package mainimport ( "fmt" "sort")type Product struct { Name, Category string Price float64}// ByName implements sort.Interface: Len, Less and Swap.type ByName []Productfunc (p ByName) Len() int { return len(p) }func (p ByName) Less(i, j int) bool { return p[i].Name < p[j].Name }func (p ByName) Swap(i, j int) { p[i], p[j] = p[j], p[i] }func (p Product) String() string { return fmt.Sprintf("%s/%s/%.0f", p.Name, p.Category, p.Price) }func main() { ps := []Product{{"Mouse", "tech", 25}, {"Desk", "office", 180}, {"Lamp", "office", 40}, {"Cable", "tech", 9}} sort.Sort(ByName(ps)) fmt.Printf("%-12s%v\n", "by name", ps) sort.Slice(ps, func(i, j int) bool { return ps[i].Price < ps[j].Price }) fmt.Printf("%-12s%v\n", "by price", ps) i := sort.Search(len(ps), func(i int) bool { return ps[i].Price >= 30 }) // binary search fmt.Println("first from 30 EUR:", ps[i].Name) sort.SliceStable(ps, func(i, j int) bool { return ps[i].Category < ps[j].Category }) fmt.Printf("%-12s%v\n", "by category", ps) // stable: keeps the price order inside each category sort.Sort(sort.Reverse(ByName(ps))) fmt.Printf("%-12s%v\n", "name desc", ps)}Salida · go run main.go
by name [Cable/tech/9 Desk/office/180 Lamp/office/40 Mouse/tech/25] by price [Cable/tech/9 Mouse/tech/25 Lamp/office/40 Desk/office/180] first from 30 EUR: Lamp by category [Lamp/office/40 Desk/office/180 Cable/tech/9 Mouse/tech/25] name desc [Mouse/tech/25 Lamp/office/40 Desk/office/180 Cable/tech/9]
Para recordar
sort.InterfacepideLen,LessySwap.sort.Sliceordena con una funciónless(i, j).sort.SliceStableconserva el orden de los iguales.sort.Reverseinvierte cualquiersort.Interface.sort.Searchbusca en un slice ya ordenado.
Ejercicio
Ordene los productos por categoría y, dentro de cada categoría, por nombre, con una sola llamada a sort.Slice cuyo less compare primero Category y, si coinciden, Name.