Para suprimir casos de enumeración Swift consecutivos mientras se ignoran los valores asociados, compare una clave de caso separada y conserve el primer valor completo en cada secuencia. Este ejemplo local para Mac preserva el orden y permite que un caso aparezca de nuevo después de otro caso.

Definir exactamente qué cuenta como duplicado

Supongamos que una vista previa gráfica de escritorio produce una secuencia sintética de pasos: inactivo, dibujando una forma con nombre y mostrando una vista previa con nombre. Quiere una lista compacta de transiciones de caso. Dos pasos de dibujo consecutivos cuentan como duplicados incluso cuando sus nombres de forma difieren.

Las enumeraciones Swift pueden transportar diferentes valores asociados para cada caso. Esos valores forman parte del valor de la enumeración, por lo que elegir ignorarlos necesita una regla de comparación explícita. La guía del lenguaje Swift explica esta distinción en Enumeraciones.

Para esta tarea, use el siguiente contrato:

  • Mantenga el primer valor de entrada.
  • Suprima cada valor posterior cuyo caso coincida con el caso de la secuencia anterior.
  • Preserve el valor asociado completo de cada elemento retenido.
  • Mantenga un caso de nuevo cuando sigue a un caso diferente.
  • Preserve el orden de entrada; una entrada vacía produce un resultado vacío.

Aquí está el comportamiento previsto para una entrada ilustrativa. Cada nombre es sintético.

Valor de entrada Decisión Razón
.idle Mantener Primer valor
.drawing("Circle") Mantener Caso cambiado
.drawing("Square") Suprimir Todavía dibujando
.ready("Preview A") Mantener Caso cambiado
.ready("Preview B") Suprimir Todavía listo
.drawing("Triangle") Mantener Dibujar sigue a listo
.idle Mantener Caso cambiado
.idle Suprimir Todavía inactivo

Los valores de dibujo retenidos son, por tanto, "Circle" y "Triangle". La carga útil "Square" se excluye deliberadamente. Si la vista de escritorio debe mostrar cada nombre de forma actualizado, este contrato descartaría cambios que la vista necesita; elija comparación sensible a la carga útil para ese consumidor.

Ejecutar un ejemplo completo de Swift local en su Mac

Use una Mac con un entorno de desarrollo Swift funcional. Swift.org recomienda Xcode para desarrollo en plataformas Apple y documenta un flujo de trabajo ejecutable de línea de comandos usando Swift Package Manager. Siga Instalar Swift — macOS si necesita preparar las herramientas.

En Terminal, cree un pequeño paquete ejecutable en un nuevo directorio de trabajo:

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

Reemplace el contenido del archivo fuente ejecutable generado con el siguiente programa completo. El programa usa tipos de la biblioteca estándar de Swift y no requiere importaciones ni dependencias de paquetes. Su entrada y salida permanecen en memoria; imprime los pasos sintéticos retenidos.

enum PreviewStep: Equatable {
    case idle
    case drawing(String)
    case ready(String)

    enum Kind: Equatable {
        case idle
        case drawing
        case ready
    }

    var kind: Kind {
        switch self {
        case .idle:
            return .idle
        case .drawing(_):
            return .drawing
        case .ready(_):
            return .ready
        }
    }

    var label: String {
        switch self {
        case .idle:
            return "idle"
        case .drawing(let shape):
            return "drawing: \(shape)"
        case .ready(let name):
            return "ready: \(name)"
        }
    }
}

func suppressConsecutiveCases(
    _ steps: [PreviewStep]
) -> [PreviewStep] {
    var result: [PreviewStep] = []
    var previousKind: PreviewStep.Kind? = nil

    for step in steps {
        let currentKind = step.kind
        if let previous = previousKind,
           previous == currentKind {
            continue
        }

        result.append(step)
        previousKind = currentKind
    }

    return result
}

let input: [PreviewStep] = [
    .idle,
    .drawing("Circle"),
    .drawing("Square"),
    .ready("Preview A"),
    .ready("Preview B"),
    .drawing("Triangle"),
    .idle,
    .idle
]

let expected: [PreviewStep] = [
    .idle,
    .drawing("Circle"),
    .ready("Preview A"),
    .drawing("Triangle"),
    .idle
]

let output = suppressConsecutiveCases(input)

precondition(output == expected)
precondition(suppressConsecutiveCases([]).isEmpty)
precondition(
    suppressConsecutiveCases([
        .drawing("Circle"), .drawing("Square")
    ]) == [.drawing("Circle")]
)
precondition(
    suppressConsecutiveCases([
        .idle, .drawing("Circle"), .idle
    ]) == [.idle, .drawing("Circle"), .idle]
)

for step in output {
    print(step.label)
}

Ejecute el paquete desde su directorio:

swift run EnumCaseDemo

La salida esperada del programa, después de cualquier mensaje de compilación, es:

idle
drawing: Circle
ready: Preview A
drawing: Triangle
idle

Estos ejemplos se inspeccionaron contra la documentación recuperada; no se ejecutaron aquí porque el entorno de investigación no tiene ejecutable Swift. Las precondiciones incluidas verifican las cargas útiles retenidas esperadas, entrada vacía, cargas útiles diferentes consecutivas y un caso que retorna después de una transición.

Por qué funciona una clave de caso separada

La propiedad kind convierte cada valor de enumeración completo en una clave pequeña, libre de carga útil. Tanto .drawing("Circle") como .drawing("Square") producen la clave .drawing. La función de filtrado compara esas claves mientras adjunta el step original, por lo que los valores asociados retenidos permanecen intactos.

La función necesita solo una clave recordada. Al inicio, previousKind es nil, permitiendo el paso del primer elemento. Después de retener un elemento, recuerda el tipo de ese elemento. Los elementos suprimidos tienen el mismo tipo, por lo que actualizar la clave recordada para ellos no haría diferencia.

Esta implementación recorre la entrada una vez. Para esta enumeración fija y sus claves de tamaño constante, realiza una comparación de clave de caso por elemento subsiguiente y usa estado auxiliar constante, más el array devuelto. La documentación de Sequence de Swift describe la iteración secuencial y el modelo de recorrido lineal habitual.

El PreviewStep completo también conforma a Equatable, permitiendo que las precondiciones verifiquen resultados completos, incluidas las cadenas retenidas. Swift admite igualdad sintetizada para enumeraciones elegibles cuyos valores asociados conforman a Equatable; vea Protocolos. Mantener la comparación de caso en Kind da a esta operación de filtrado su propia regla explícita sin redefinir la igualdad en todo el programa.

El switch exhaustivo en kind también proporciona un punto de control de mantenimiento útil. Cuando agregue un nuevo caso, agregue su clave y mapeo correspondientes. Evite un mapeo general que agrupe accidentalmente un nuevo caso con uno existente. Swift documenta el switching exhaustivo de enumeraciones en Enumeraciones.

Usar la misma comparación con Combine

Si su código de Mac ya usa un publicador de Combine, Apple proporciona removeDuplicates(by:) para una comparación personalizada. Su predicado devuelve true cuando el segundo valor debe contar como duplicado. Apple soporta explícitamente este operador cuando la comparación deseada difiere de la igualdad ordinaria del tipo de salida. Está disponible en macOS 10.15 y posterior. Vea removeDuplicates(by:).

Lo siguiente es un fragmento de extensión, no un programa independiente. Adjúntelo al ejemplo completo anterior en una Mac con Combine disponible. Reutiliza la definición existente de PreviewStep y el array input:

import Combine

let subscription = input.publisher
    .removeDuplicates { previous, current in
        previous.kind == current.kind
    }
    .sink { step in
        print("Combine: \(step.label)")
    }

Esto imprime los mismos cinco pasos retenidos con un prefijo "Combine: ". El predicado compara claves de caso, y el publicador pasa los valores de enumeración originales retenidos. El propio ejemplo de operador de Apple sigue el mismo principio al comparar una propiedad mientras ignora otra. removeDuplicates(by:)

Mantenga la suscripción devuelta viva durante el tiempo de vida de suscripción requerido. Apple documenta que la desasignación del valor devuelto por sink(receiveValue:) desmonta la suscripción. Esta sobrecarga se aplica a publicadores cuyo tipo de fallo es Never, como en los ejemplos de publicador de secuencia de Apple. sink(receiveValue:)

Verificar los límites antes de adaptar el código

Use la función de array cuando ya tenga una lista local finita. Use el operador Combine cuando los valores ya lleguen a través de un publicador. Ambas versiones aquí expresan el mismo contrato de transición de caso.

Requisito Comparación o enfoque apropiado
Mantener el primer valor completo en cada secuencia de caso Comparar kind, como se muestra
Mantener cambios en cadenas asociadas Comparar valores completos usando igualdad ordinaria
Mantener el último valor completo en cada secuencia Almacenar un valor pendiente en búfer y vaciarlo en una transición de caso y al final
Mantener un caso que retorna después de otro caso Comparar secuencias consecutivas en lugar de mantener una lista global de casos vistos
Filtrar arrays separados independientemente Llamar a la función por separado; cada llamada comienza con estado de comparación vacío

Mantener la última carga útil requiere un algoritmo diferente porque este programa adjunta el primer valor inmediatamente. Del mismo modo, dividir una secuencia lógica en arrays separados reinicia el estado de comparación: un valor de dibujo al final de un lote y otro al inicio del siguiente sobrevivirán a ambas llamadas separadas. Si los lotes deben comportarse como una secuencia, preserve la clave anterior a través de los límites de lote.

Foundation proporciona colecciones y otra funcionalidad base para aplicaciones en plataformas Apple, pero este cálculo se implementa completamente con tipos de Swift. La variante de publicador requiere específicamente Combine; agregar import Foundation no importa ese operador. Apple documenta estos frameworks por separado en Foundation y removeDuplicates(by:).

La función de array síncrona tampoco necesita maquinaria de concurrencia: su estado de comparación pertenece a cada invocación. Si luego introduce estado de presentación de escritorio compartido, defina el aislamiento de ese estado por separado. Apple documenta MainActor como un actor singleton cuyo ejecutor es equivalente a la cola de envío principal; la supresión de duplicados en sí no selecciona un actor. MainActor

Para la tarea local mostrada aquí, la elección decisiva es qué carga útil sobrevive a una secuencia. Una vez que elige la primera carga útil, una clave de caso separada hace que el comportamiento sea sencillo de leer y verificar: retenga el valor original en una transición de caso, suprima claves consecutivas coincidentes y permita el paso de casos que retornan.

Fuentes