Pour supprimer les cas d’énumération Swift consécutifs en ignorant les valeurs associées, comparez une clé de cas distincte et conservez la première valeur complète de chaque série. Cet exemple local sur Mac préserve l’ordre et permet à un cas de réapparaître après un autre cas.

Définir précisément ce qui constitue un doublon

Supposons qu’un aperçu graphique sur ordinateur produise une séquence synthétique d’étapes : inactivité, dessin d’une forme nommée et affichage d’un aperçu nommé. Vous souhaitez obtenir une liste compacte des transitions entre cas. Deux étapes de dessin consécutives comptent comme des doublons même si leurs noms de forme diffèrent.

Les énumérations Swift peuvent porter différentes valeurs associées pour chaque cas. Ces valeurs font partie de la valeur de l’énumération ; les ignorer nécessite donc une règle de comparaison explicite. Le guide du langage Swift explique cette distinction dans Enumerations.

Pour cette tâche, utilisez le contrat suivant :

  • Conservez la première valeur d’entrée.
  • Supprimez chaque valeur ultérieure dont le cas correspond à celui de la série précédente.
  • Préservez la valeur associée complète de chaque élément conservé.
  • Conservez de nouveau un cas lorsqu’il suit un cas différent.
  • Préservez l’ordre d’entrée ; une entrée vide produit un résultat vide.

Voici le comportement attendu pour un exemple d’entrée. Tous les noms sont synthétiques.

Valeur d’entrée Décision Motif
.idle Conserver Première valeur
.drawing("Circle") Conserver Le cas a changé
.drawing("Square") Supprimer Toujours en dessin
.ready("Preview A") Conserver Le cas a changé
.ready("Preview B") Supprimer Toujours prêt
.drawing("Triangle") Conserver Le dessin suit l’état prêt
.idle Conserver Le cas a changé
.idle Supprimer Toujours inactif

Les valeurs de dessin conservées sont donc \"Circle\" et \"Triangle\". La valeur associée \"Square\" est délibérément exclue. Si la vue de l’ordinateur doit afficher chaque nom de forme mis à jour, ce contrat écarterait des changements dont cette vue a besoin ; choisissez une comparaison sensible aux valeurs associées pour ce consommateur.

Exécuter un exemple Swift local complet sur votre Mac

Utilisez un Mac avec un environnement de développement Swift fonctionnel. Swift.org recommande Xcode pour le développement sur les plateformes Apple et documente un flux de travail pour créer des exécutables en ligne de commande avec Swift Package Manager. Suivez Install Swift — macOS si vous devez préparer les outils.

Dans Terminal, créez un petit paquet exécutable dans un nouveau répertoire de travail :

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

Remplacez le contenu du fichier source exécutable généré par le programme complet suivant. Le programme utilise les types de la bibliothèque standard Swift et ne nécessite ni importation ni dépendance de paquet. Ses entrées et sorties restent en mémoire ; il affiche les étapes synthétiques conservées.

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)
}

Exécutez le paquet depuis son répertoire :

swift run EnumCaseDemo

La sortie attendue du programme, après les éventuels messages de compilation, est :

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

Ces exemples ont été vérifiés par rapport à la documentation récupérée ; ils n’ont pas été exécutés ici, car l’environnement de recherche ne dispose pas d’exécutable Swift. Les préconditions incluses vérifient les valeurs associées attendues, l’entrée vide, les valeurs associées consécutives différentes et le retour d’un cas après une transition.

Pourquoi une clé de cas distincte fonctionne

La propriété kind convertit chaque valeur complète de l’énumération en une petite clé sans valeur associée. .drawing("Circle") et .drawing("Square") produisent toutes deux la clé .drawing. La fonction de filtrage compare ces clés tout en ajoutant le step d’origine, de sorte que les valeurs associées conservées restent intactes.

La fonction n’a besoin de mémoriser qu’une seule clé. Au départ, previousKind vaut nil, ce qui laisse passer le premier élément. Après avoir conservé un élément, elle mémorise son type. Les éléments supprimés ont le même type ; mettre à jour la clé mémorisée pour ceux-ci ne changerait donc rien.

Cette implémentation parcourt l’entrée une seule fois. Pour cette énumération fixe et ses clés de taille constante, elle effectue une comparaison de clé de cas par élément à partir du deuxième et utilise un état auxiliaire constant, en plus du tableau retourné. La documentation de Sequence de Swift décrit l’itération séquentielle et le modèle habituel de parcours linéaire.

Le type complet PreviewStep est également conforme à Equatable, ce qui permet aux préconditions de vérifier les résultats complets, y compris les chaînes conservées. Swift prend en charge l’égalité synthétisée pour les énumérations admissibles dont les valeurs associées sont conformes à Equatable ; consultez Protocols. Garder la comparaison des cas dans Kind donne à cette opération de filtrage sa propre règle explicite sans redéfinir l’égalité dans tout le programme.

Le switch exhaustif dans kind fournit également un point de contrôle utile pour la maintenance. Lorsque vous ajoutez un nouveau cas, ajoutez sa clé et son mappage correspondants. Évitez un mappage générique qui regrouperait accidentellement un nouveau cas avec un cas existant. Swift explique les commutations exhaustives d’énumérations dans Enumerations.

Utiliser la même comparaison avec Combine

Si votre code Mac utilise déjà un publisher Combine, Apple fournit removeDuplicates(by:) pour effectuer une comparaison personnalisée. Son prédicat renvoie true lorsque la deuxième valeur doit être considérée comme un doublon. Apple prend explicitement en charge cet opérateur lorsque la comparaison souhaitée diffère de l’égalité ordinaire du type de sortie. Il est disponible sur macOS 10.15 et versions ultérieures. Consultez removeDuplicates(by:).

Ce qui suit est un fragment d’extension, et non un programme autonome. Ajoutez-le à l’exemple complet ci-dessus sur un Mac où Combine est disponible. Il réutilise la définition existante de PreviewStep et le tableau input :

import Combine

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

Cela affiche les mêmes cinq étapes conservées avec le préfixe \"Combine: \". Le prédicat compare les clés de cas, et le publisher transmet les valeurs d’énumération d’origine qui ont été conservées. L’exemple d’opérateur d’Apple suit le même principe en comparant une propriété tout en en ignorant une autre. removeDuplicates(by:)

Conservez l’abonnement retourné pendant toute la durée nécessaire. Apple précise que la désallocation de la valeur renvoyée par sink(receiveValue:) met fin à l’abonnement. Cette surcharge s’applique aux publishers dont le type d’échec est Never, comme dans les exemples de publishers de séquence d’Apple. sink(receiveValue:)

Vérifier les limites avant d’adapter le code

Utilisez la fonction pour tableau lorsque vous disposez déjà d’une liste locale finie. Utilisez l’opérateur Combine lorsque les valeurs arrivent déjà par un publisher. Les deux versions présentées ici expriment le même contrat de transition entre cas.

Exigence Comparaison ou approche appropriée
Conserver la première valeur complète de chaque série de cas Comparer kind, comme indiqué
Conserver les changements des chaînes associées Comparer les valeurs complètes avec l’égalité ordinaire
Conserver la dernière valeur complète de chaque série Mettre en tampon une valeur en attente et la vider lors d’une transition de cas et à la fin
Conserver un cas qui réapparaît après un autre cas Comparer les séries consécutives plutôt que de tenir une liste globale des cas déjà vus
Filtrer des tableaux distincts indépendamment Appeler la fonction séparément ; chaque appel commence avec un état de comparaison vide

Conserver la dernière valeur associée nécessite un algorithme différent, car ce programme ajoute immédiatement la première valeur. De même, diviser une séquence logique en tableaux distincts réinitialise l’état de comparaison : une valeur de dessin à la fin d’un lot et une autre au début du lot suivant seront toutes deux conservées lors d’appels séparés. Si les lots doivent être traités comme une seule séquence, préservez la clé précédente d’un lot à l’autre.

Foundation fournit des collections et d’autres fonctionnalités de base pour les apps sur les plateformes Apple, mais ce calcul est entièrement implémenté avec des types Swift. La variante publisher nécessite précisément Combine ; ajouter import Foundation n’importe pas cet opérateur. Apple documente ces frameworks séparément dans Foundation et removeDuplicates(by:).

La fonction synchrone pour tableau ne nécessite pas non plus de mécanisme de concurrence : son état de comparaison appartient à chaque appel. Si vous introduisez plus tard un état partagé pour la présentation sur ordinateur, définissez séparément l’isolation de cet état. Apple décrit MainActor comme un acteur singleton dont l’exécuteur est équivalent à la file d’exécution principale ; la suppression des doublons ne sélectionne pas elle-même un acteur. MainActor

Pour la tâche locale présentée ici, le choix déterminant est la valeur associée qui survit à une série. Une fois la première valeur choisie, une clé de cas distincte rend le comportement facile à lire et à vérifier : conserver la valeur d’origine lors d’une transition de cas, supprimer les clés consécutives correspondantes et laisser passer les cas qui réapparaissent.

Sources