Para mantener únicos los valores sintéticos de Swift sin cambiar su orden de inserción, recorre el array de entrada una vez, registra las claves aceptadas en un conjunto y añade solo las primeras apariciones a un array de salida. Esta guía crea un pequeño cálculo local que puedes ejecutar en un Mac.

La tarea consiste en procesar una lista ordenada de valores sintéticos de formas, eliminar los repetidos y calcular un total a partir de las entradas conservadas. Todo ocurre en memoria; el ejemplo no lee datos externos ni guarda resultados de cálculo.

Define qué significa «único» antes de elegir una colección

Los arrays de Swift conservan el orden y permiten duplicados. Los conjuntos contienen valores únicos, pero no tienen un orden definido. Por lo tanto, convertir un array en un conjunto y luego volver a convertirlo en un array no conserva el orden de la primera aparición. Ordenar después establece un orden de clasificación, que puede diferir del orden de inserción. Estos comportamientos de las colecciones están documentados en Tipos de colecciones de Swift.

En este ejemplo, cada valor sintético tiene un nombre de forma y un tamaño. El contrato predeterminado considera que dos entradas son duplicadas solo cuando coinciden ambas propiedades. Por lo tanto, un círculo de tamaño 12 y un círculo de tamaño 20 siguen siendo valores separados.

Pregunta sobre el contrato Regla para este cálculo
¿Qué determina el orden de entrada? La posición en el array proporcionado
¿Qué hace que dos valores sean duplicados? La misma forma y el mismo tamaño
¿Qué aparición se conserva? La primera aparición
¿Una aparición duplicada posterior mueve el valor conservado? No
¿Qué ocurre con una entrada vacía? Devuelve un array vacío y un total de cero
¿Qué valores contribuyen al total? Solo los valores conservados

Este contrato evita un error común: eliminar entradas por nombre de forma cuando el cálculo necesita combinaciones distintas de forma y tamaño. El ejemplo resuelto también calcula el resultado considerando solo la forma, para que puedas ver las consecuencias de cambiar esa regla.

Crea un ejemplo completo de Swift local en tu Mac

Necesitas tener instalado un conjunto de herramientas de Swift. En Terminal, ejecuta swift --version para confirmar que el comando está disponible. El tutorial oficial de Swift Crear una herramienta de línea de comandos documenta esa comprobación, la creación de un paquete ejecutable y cómo ejecutarlo.

Crea un directorio para el ejemplo e inicializa un paquete ejecutable:

mkdir OrderDemo
cd OrderDemo
swift package init --name OrderDemo --type executable

Reemplaza el archivo de código fuente ejecutable generado dentro de Sources por el programa completo que aparece a continuación. El nombre del archivo generado y la estructura de directorios pueden variar según el conjunto de herramientas; reemplaza el punto de entrada existente en lugar de añadir un segundo. Este programa usa un punto de entrada @main, así que colócalo en un archivo llamado OrderDemo.swift y, si hace falta, cambia el nombre de un main.swift generado. El tutorial de línea de comandos de Swift explica cómo elegir entre un archivo main.swift y un punto de entrada @main.

No se requieren importaciones ni dependencias de paquetes. El programa usa tipos y funciones de la biblioteca estándar.

struct Tile: Hashable {
    let shape: String
    let size: Int
}

func uniqueFirst<Value, Key: Hashable>(
    _ values: [Value],
    by key: (Value) -> Key
) -> [Value] {
    var seen: Set<Key> = []
    var result: [Value] = []

    for value in values {
        if seen.insert(key(value)).inserted {
            result.append(value)
        }
    }

    return result
}

@main
struct OrderDemo {
    static func main() {
        let input = [
            Tile(shape: "circle", size: 12),
            Tile(shape: "square", size: 8),
            Tile(shape: "circle", size: 12),
            Tile(shape: "circle", size: 20),
            Tile(shape: "square", size: 8)
        ]

        let retained = uniqueFirst(input, by: { $0 })
        let total = retained.reduce(0) { $0 + $1.size }
        let skipped = input.count - retained.count

        let expected = [
            Tile(shape: "circle", size: 12),
            Tile(shape: "square", size: 8),
            Tile(shape: "circle", size: 20)
        ]
        precondition(retained == expected)
        precondition(total == 40)
        precondition(skipped == 2)

        let empty: [Tile] = []
        precondition(uniqueFirst(empty, by: { $0 }).isEmpty)
        precondition(
            uniqueFirst([input[0], input[0]], by: { $0 })
                == [input[0]]
        )

        let byShape = uniqueFirst(input, by: { $0.shape })
        precondition(byShape == [input[0], input[1]])
        let shapeTotal = byShape.reduce(0) { $0 + $1.size }
        precondition(shapeTotal == 20)

        for tile in retained {
            print("\(tile.shape): \(tile.size)")
        }
        print("Total: \(total)")
        print("Skipped duplicates: \(skipped)")
        print("Shape-only total: \(shapeTotal)")
    }
}

Desde el directorio del paquete, ejecuta:

swift run OrderDemo

A continuación se muestra la salida esperada del programa, después de cualquier mensaje de compilación. El programa se revisó comparándolo con la documentación de API consultada, pero no se ejecutó aquí porque este entorno de investigación no tiene un ejecutable de Swift.

circle: 12
square: 8
circle: 20
Total: 40
Skipped duplicates: 2
Shape-only total: 20

¿Por qué el enfoque de array y conjunto conserva el orden?

El array es la única fuente del orden de salida. El bucle visita cada posición de entrada en secuencia y append(_:) coloca al final del resultado el valor aceptado. El conjunto solo indica si la clave seleccionada ya apareció; el programa nunca lo recorre.

La expresión clave es seen.insert(key(value)).inserted. Swift documenta que insert(_:) devuelve inserted como true cuando un elemento es nuevo y false cuando ya existe un elemento igual. El elemento existente permanece sin cambios. Ese valor de retorno permite que una sola operación decida si se añade el valor original. Consulta la documentación de inserción en Set de Apple.

Con la entrada proporcionada, se aceptan el primer círculo y el cuadrado. Se omite el segundo círculo idéntico. El círculo más grande se acepta porque su valor completo es distinto, y se omite el cuadrado repetido. Por lo tanto, los tamaños conservados son 12 + 8 + 20 = 40.

La implementación mantiene una invariante útil: después de cada iteración, el resultado contiene exactamente el primer valor encontrado para cada clave en seen, en orden de aparición. Una clave nueva amplía ambas colecciones; una repetida no modifica ninguna. Este razonamiento explica la garantía de orden independientemente de cómo el conjunto almacene sus elementos.

¿Cómo deben expresar la igualdad los valores Swift personalizados?

La declaración struct Tile: Hashable solicita la conformidad sintetizada. Ambas propiedades almacenadas, String e Int, admiten hash, así que Swift puede sintetizar el comportamiento requerido. Apple documenta estas condiciones para la síntesis en Hashable.

Usar { $0 } como clave hace que el valor completo de Tile determine la unicidad. Usar { $0.shape } cambia el contrato para conservar una entrada por forma. En el ejemplo, eso elimina el círculo posterior de tamaño 20, lo que reduce el total a 20. Se trata de una comparación intencional entre dos reglas, no de una segunda implementación de la misma regla.

Mantén determinista la selección de la clave durante todo el cálculo. Derívala del valor que se está procesando, no de un contador ni de una variable externa cambiante. De lo contrario, entradas iguales podrían recibir claves distintas y conservarse como elementos separados.

Si implementas manualmente la igualdad y el cálculo de hash, usa las mismas propiedades esenciales en ambos. Apple exige que las instancias iguales aporten los mismos valores al hasher en el mismo orden. Evita usar hashValue como clave de unicidad: un hash no sustituye al contrato de igualdad del valor. Si conservas la clave real que admite hash, el conjunto puede aplicar ese contrato. Consulta los requisitos de conformidad de Hashable.

¿Cuándo conviene usar una colección ordenada?

Para este único cálculo, un array y un conjunto local mantienen explícito el comportamiento. Si una aplicación añade, elimina, reordena y consulta repetidamente elementos únicos, una colección ordenada específica puede facilitar el mantenimiento de esas operaciones.

Opción Condición adecuada Límite relevante
Array y conjunto Un recorrido de valores sintéticos de Swift, posiblemente con una clave seleccionada Tu función define el comportamiento de la primera aparición
OrderedSet de Swift Collections Uso continuado de una colección ordenada de valores únicos que admiten hash Requiere el paquete Swift Collections por separado
NSOrderedSet de Foundation Una API ya trabaja con colecciones ordenadas de objetos de Foundation Una clase que representa una colección ordenada estática; las operaciones mutables usan NSMutableOrderedSet

La documentación oficial de Swift Collections describe OrderedSet como una colección ordenada de elementos únicos, que se importa mediante OrderedCollections. Proporciona operaciones que tienen en cuenta la posición, como append(_:) y insert(_:at:). Son API del paquete, así que añadir solo import OrderedCollections no configura un proyecto que, de otro modo, no tenga dependencias. Consulta OrderedSet.

La documentación de Foundation de Apple describe NSOrderedSet como una colección ordenada estática de objetos distintos e identifica NSMutableOrderedSet como su contraparte mutable. Para el cálculo de valores nativos de Swift anterior, la función genérica expresa directamente la clave de igualdad elegida sin introducir esa colección de objetos. Consulta NSOrderedSet.

¿Qué cambia cuando el cálculo alimenta una interfaz de escritorio?

El ejemplo completo es síncrono y mantiene sus colecciones temporales dentro de la función. Para una lista sintética pequeña, esto proporciona un orden de procesamiento claro sin introducir tareas.

En una interfaz de macOS, aísla adecuadamente el estado y las actualizaciones que afectan a la interfaz. Apple documenta MainActor como un actor global cuyo ejecutor equivale a la cola de despacho principal; la guía de concurrencia de Swift explica cómo aplicar @MainActor a funciones y tipos que acceden al estado de la interfaz. Consulta MainActor y Concurrencia.

La concurrencia también requiere una definición explícita del orden. Swift no garantiza el orden de finalización de las tareas secundarias de un grupo de tareas. Por lo tanto, añadir resultados a medida que terminan las tareas conserva el orden de finalización, que puede diferir del orden de entrada original. La guía oficial de concurrencia documenta este límite.

Si una versión futura realiza cálculos sintéticos independientes en paralelo, conserva la posición de cada entrada junto con su resultado, restablece el orden según la posición de entrada después de recopilar los resultados y, luego, aplica el proceso de unicidad. Este diseño se deriva del límite documentado sobre el orden de finalización. Mantén síncrono el ejemplo pequeño actual, salvo que el trabajo adicional justifique la coordinación extra.

Comprueba el contrato antes de reutilizar la función

El programa incluye comprobaciones del orden exacto de los elementos conservados, de la entrada vacía, de valores idénticos repetidos y de la regla alternativa que considera solo la forma. Estas comprobaciones se centran en las decisiones con más probabilidades de cambiar el sentido del cálculo.

Al adaptarlo, comprueba tres condiciones: la entrada debe tener un orden significativo, la clave debe expresar la regla de igualdad que quieres y conservar la primera aparición debe ser lo adecuado. La función omite deliberadamente las entradas coincidentes posteriores; no combina sus tamaños ni reemplaza el valor conservado. El recuento de duplicados omitidos permite ver ese efecto.

Para la lista sintética de formas proporcionada, el resultado final contiene tres valores en orden de primera aparición y un total de 40. Deja que el array se encargue del orden, el conjunto de la pertenencia y la clave seleccionada de definir qué significa «único».

Fuentes