【问题标题】:How can I re order only part of a collection in swift?如何快速重新订购收藏的一部分?
【发布时间】:2020-09-19 13:38:26
【问题描述】:

我有一个Item 数组。由于集合是基于其他地方的许多用户操作从 API 提供的,因此无法保证顺序。

但是,我需要通过 type 字段对该集合进行排序,以便可以在其他地方正确地对其进行迭代。

对于下面的示例游乐场,我需要一个遵循此逻辑的重新排序集合:

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

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

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

]

具体而言,.foo 和 .bar 应该是前 2 项,.boom 和 bang 将是最后 2 项。

这些固定项是唯一的,响应中不会存在重复项。

剩下的所有东西都应该在这两组之间,与它们在原始集合中的顺序相同。

我尝试将项目拆分为 var output: [Item] 内的 2 个集合,并在索引处插入第二个集合,但排序仍然关闭。

我怎样才能做到这一点?

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

【问题讨论】:

  • 如果原始数组有多个foo或bar怎么办?
  • 对不起,我应该说清楚,“固定”项目是独一无二的,它们不会重复。

标签: ios arrays swift sorting


【解决方案1】:

您可以给每个枚举案例一个 int 值,并对其进行稳定排序:

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 取自this answer by leavez:

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

【讨论】:

  • 它甚至可以在没有intValue 铸造的情况下完成。您只需创建ItemTypeComparable,并在函数&lt; 中确保.foo .bar .boo, .baz) .boom .bang。
  • @user28434 是的,但 ItemType 具有可比性可能没有意义。而且我无法以像在大 switch 语句中列出一堆 numbers 那样清楚地表达 OP 意图的方式实现 &lt;。不过,为什么不将您的解决方案发布为答案呢?
【解决方案2】:

与ItemType 实现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
        }
    }
}

【讨论】:

    猜你喜欢
    • 2018-09-06
    • 2014-09-21
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 2019-06-05
    相关资源
    最近更新 更多