La librería estándar de Swift es sólida, pero hay operaciones que aparecen constantemente en código real y que no están incluidas: partir un array en grupos de N elementos, generar la ventana deslizante sobre una secuencia, obtener todas las combinaciones de un conjunto, o una estructura de cola eficiente. Apple mantiene dos paquetes oficiales que añaden exactamente eso: swift-algorithms y swift-collections.
Swift Algorithms: operaciones sobre secuencias
El paquete swift-algorithms añade más de 40 algoritmos sobre Sequence y Collection. Los más usados:
chunked: dividir en grupos
import Algorithms
let numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// Grupos de tamaño fijo
let grupos = numeros.chunks(ofCount: 3)
// [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
// Grupos según un criterio de cambio
let letras = ["a", "a", "b", "b", "b", "c", "a"]
let porLetra = letras.chunked(by: { $0 == $1 })
// [["a", "a"], ["b", "b", "b"], ["c"], ["a"]]
// Por clave
let palabras = ["hola", "hey", "adios", "ala"]
let porInicial = palabras.chunked(on: { $0.first! })
// [["hola", "hey"], ["adios", "ala"]]
windows: ventana deslizante
let precios = [10.0, 12.0, 11.5, 13.0, 14.5, 13.0]
// Media móvil de 3 elementos
let mediaMovil = precios.windows(ofCount: 3).map { ventana in
ventana.reduce(0, +) / Double(ventana.count)
}
// [11.17, 12.17, 13.0, 13.5]
// Detectar tendencias
for ventana in precios.windows(ofCount: 2) {
let tendencia = ventana[ventana.startIndex] < ventana[ventana.index(after: ventana.startIndex)]
print(tendencia ? "sube" : "baja")
}
product: producto cartesiano
let colores = ["rojo", "verde", "azul"]
let tallas = ["S", "M", "L", "XL"]
for (color, talla) in product(colores, tallas) {
print("(color)-(talla)")
}
// rojo-S, rojo-M, rojo-L, rojo-XL, verde-S, ...
// Generar todas las combinaciones de parámetros para tests:
let bases = [2, 8, 10, 16]
let exponentes = [0, 1, 2, 3, 4]
let casosTest = Array(product(bases, exponentes))
combinations y permutations
let elementos = [1, 2, 3, 4]
// Combinaciones de 2 (sin importar orden, sin repetición)
for combo in elementos.combinations(ofCount: 2) {
print(combo) // [1,2], [1,3], [1,4], [2,3], [2,4], [3,4]
}
// Permutaciones de 3
for perm in elementos.permutations(ofCount: 3) {
print(perm) // todas las permutaciones de 3 elementos
}
// Caso real: generar todas las contraseñas posibles de longitud N para auditoría
let caracteres = Array("abcdefgh")
let contraseñas = caracteres.permutations(ofCount: 4).prefix(100)
Otros algoritmos útiles
// firstNonNil: el primer resultado no nil de un transform
let strings = ["uno", "2", "tres", "4", "5"]
let primeroNumero = strings.firstNonNil { Int($0) } // 2
// minAndMax: mínimo y máximo en una sola pasada
let (min, max) = numeros.minAndMax()!
// uniqued: eliminar duplicados preservando orden
let conDuplicados = [1, 3, 2, 1, 4, 3, 5]
let sinDuplicados = conDuplicados.uniqued() // [1, 3, 2, 4, 5]
// Con clave personalizada
let usuarios = [Usuario(id: 1, nombre: "Ana"), Usuario(id: 2, nombre: "Ana")]
let usuariosUnicos = usuarios.uniqued(on: .nombre) // solo Ana (el primero)
// interspersed: insertar separador entre elementos
let partes = ["uno", "dos", "tres"]
let conComas = partes.interspersed(with: ", ") // ["uno", ", ", "dos", ", ", "tres"]
Swift Collections: estructuras de datos avanzadas
El paquete swift-collections añade estructuras de datos que la librería estándar no incluye por razones de scope.
Deque: cola de doble extremo
Deque (double-ended queue) permite inserciones y eliminaciones eficientes tanto por el principio como por el final, a diferencia de Array que es lento en el principio:
import Collections
var cola: Deque = [1, 2, 3, 4, 5]
// O(1) en ambos extremos (Array.prepend es O(n))
cola.prepend(0) // [0, 1, 2, 3, 4, 5]
cola.append(6) // [0, 1, 2, 3, 4, 5, 6]
let primero = cola.popFirst() // 0
let ultimo = cola.popLast() // 6
// Implementar un historial con límite de tamaño
var historial: Deque<String> = []
let maxHistorial = 50
func registrar(_ accion: String) {
historial.append(accion)
if historial.count > maxHistorial {
historial.removeFirst()
}
}
OrderedDictionary: diccionario con orden garantizado
var config: OrderedDictionary = [
"host": "localhost",
"port": "5432",
"database": "produccion",
"ssl": "true"
]
// A diferencia de Dictionary, el orden de inserción se preserva
for (clave, valor) in config {
print("(clave)=(valor)") // Siempre en orden host, port, database, ssl
}
// Acceso por índice (imposible con Dictionary)
print(config.elements[0]) // ("host", "localhost")
config.move(fromIndex: 3, toIndex: 0) // Mueve "ssl" al principio
// Útil para formularios donde el orden de campos importa
var campos: OrderedDictionary<String, String> = [:]
campos["nombre"] = ""
campos["apellidos"] = ""
campos["email"] = ""
OrderedSet: conjunto con orden
var etiquetas: OrderedSet = ["swift", "ios", "programacion", "apple"]
etiquetas.insert("swiftui") // Se añade al final
etiquetas.insert("swift") // Ya existe, no se añade
let primera = etiquetas[0] // "swift" acceso O(1) por índice
etiquetas.sort() // Ordena manteniendo la unicidad
// Combinar conjuntos preservando orden
let etiquetasPost1: OrderedSet = ["swift", "ios"]
let etiquetasPost2: OrderedSet = ["ios", "macos", "swift"]
let todas = etiquetasPost1.union(etiquetasPost2) // ["swift", "ios", "macos"]
Heap: cola de prioridad
var colaPrioridad = Heap<Int>()
colaPrioridad.insert(5)
colaPrioridad.insert(3)
colaPrioridad.insert(8)
colaPrioridad.insert(1)
// Siempre extrae el mínimo (min-heap por defecto)
print(colaPrioridad.popMin()) // 1
print(colaPrioridad.popMin()) // 3
// Max-heap: el comparador invierte el orden
var tareasPrioridad = Heap<Tarea> { $0.prioridad > $1.prioridad }
tareasPrioridad.insert(Tarea(nombre: "Normal", prioridad: 1))
tareasPrioridad.insert(Tarea(nombre: "Urgente", prioridad: 10))
let siguiente = tareasPrioridad.popMax() // La de prioridad 10
Añadir los paquetes al proyecto
// Package.swift
dependencies: [
.package(url: "https://github.com/apple/swift-algorithms", from: "1.2.0"),
.package(url: "https://github.com/apple/swift-collections", from: "1.1.0")
],
targets: [
.target(
name: "MiApp",
dependencies: [
.product(name: "Algorithms", package: "swift-algorithms"),
.product(name: "Collections", package: "swift-collections")
]
)
]
Resumen
swift-algorithms aporta operaciones de alto nivel sobre secuencias que evitan tener que implementar patrones comunes como chunked, windows o product, con implementaciones eficientes y bien testeadas. swift-collections llena el hueco de estructuras de datos que la librería estándar omite deliberadamente: Deque para colas eficientes, OrderedDictionary y OrderedSet cuando el orden importa, y Heap para colas de prioridad. Ambos son mantenidos por Apple y siguen la misma filosofía de la librería estándar.
