J'ai un tableau de Item. La commande n'est jamais garantie car la collection est servie à partir d'une API basée sur un certain nombre d'actions utilisateur ailleurs.

Cependant, j'ai besoin de commander cette collection par un champ type afin que je puisse l'itérer ailleurs correctement.

Dans le cas de l'exemple de terrain de jeu ci-dessous, j'ai besoin d'une collection réorganisée qui suivrait cette logique:

[
  Item(type: .foo),
  Item(type: .bar),

  Item(type: .boo),
  Item(type: .baz),

  Item(type: .boom),
  Item(type: .bang)

]

Plus précisément, .foo et .bar doivent être les 2 premiers éléments, .boom et bang seront les 2 derniers éléments.

Ces éléments fixes sont uniques, aucun doublon n'existera dans la réponse.

Tout ce qui reste doit se trouver entre ces 2 groupes, dans le même ordre que dans la collection d'origine.

J'ai essayé de diviser l'article en 2 collections dans var output: [Item] et d'insérer la deuxième collection à un index, mais la commande est toujours désactivée.

Comment puis-je atteindre cet objectif?

import UIKit

enum ItemType: String {
  case foo
  case bar
  case boo
  case baz
  case boom
  case bang
}

struct Item {
  let type: ItemType
}

let props = [
  Item(type: .bang),
  Item(type: .bar),
  Item(type: .baz),
  Item(type: .boo),
  Item(type: .boom),
  Item(type: .foo)
]

/*
 case foo
 case bar
 > ----------- should be ordered in same order as in `props`
 case boom
 case bang
*/

var output: [Item] {
  var fixedPosition: [Item] = []
  var dynamicPosition: [Item] = []

  props.forEach { item in
    if (item.type == .foo || item.type == .bar || item.type == .boom || item.type == .bang ){
      fixedPosition.append(item)
    } else {
      dynamicPosition.append(item)
    }
  }

  var result: [Item] = fixedPosition
  result.insert(contentsOf: dynamicPosition, at: 1)

  return result
}

print(output.map { $0.type })
0
Harry Blue 1 juin 2020 à 12:17

3 réponses

Meilleure réponse

Vous pouvez donner à chaque cas d'énumération une valeur int et le trier de manière stable:

func intValue(for itemType: ItemType) -> Int {
    switch itemType {
    case .foo:
        return 0
    case .bar:
        return 1
    case .boo, .baz: // the order of these should remain the same, so they get the same number
        return 2
    case .boom:
        return 3
    case .bang:
        return 4
    }
}

let sorted = props.stableSorted(by: { intValue(for: $0.type) < intValue(for: $1.type) })

stableSort est tiré de cette réponse:

extension RandomAccessCollection {

    /// return a sorted collection
    /// this use a stable sort algorithm
    ///
    /// - Parameter areInIncreasingOrder: return nil when two element are equal
    /// - Returns: the sorted collection
    public func stableSorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element] {

        let sorted = try enumerated().sorted { (one, another) -> Bool in
            if try areInIncreasingOrder(one.element, another.element) {
                return true
            } else {
                return one.offset < another.offset
            }
        }
        return sorted.map { $0.element }
    }
}
1
Sweeper 1 juin 2020 à 09:34

Variante avec ItemType implémentation du protocole Comparable:

extension ItemType: Comparable {
    static func < (lhs: ItemType, rhs: ItemType) -> Bool {
        // function returns `true` iff lhs is smaller than res

        // order of cases matters here, switch matches them left-to-right, top-to-bottom
        switch (lhs, rhs) {
        case (.foo, _): // .foo is before everyone else
            return true
        case (_, .foo): // nothing is "smaller" than .foo
            return false
        case (.bar, _):  // .bar can be preceded only by .foo (previous 2 cases handle it)
            return true
        case (_, .bang): // .bang is always last
            return true
        case (.bang, _): // nothing is "bigger" than .bang
            return false
        case (_, .boom): // .boom can be only before .bang (previous 2 cases handle it)
            return true
        default: // otherwise we don't care about comparing items, neither item is "smaller" than other
            return false
        }
    }
}
0
user28434 1 juin 2020 à 11:34

Vous pouvez mapper les index comme Int (décrivant l'odeur). Et triez par index après avoir récupéré et analysé votre énumération ItemType.

 extension ItemType {
    var index: Int {
        switch self {
        case .foo: return 0
        case .bar: return 1
        case .boo: return 2
        case .baz: return 3
        case .boom: return 4
        case .bang: return 5
        }
    }
}

fixedPosition = dynamicPosition.sorted(by: { $0.index < $1.index })
0
omerfarukozturk 1 juin 2020 à 10:09