【问题标题】:Sort an array A in Swift based on (1) an incomplete array B and (2) alphabetically根据(1)不完整的数组 B 和(2)按字母顺序对 Swift 中的数组 A 进行排序
【发布时间】:2020-04-24 20:41:51
【问题描述】:

我有一个数组tags,其中包括几个元素idname。我还有一个数组order,其中存在tags 中的一些 元素。这是我想要实现的目标:

  1. tags 中的所有元素都应按照order 排序。
  2. order 中不存在的元素应按字母顺序排列在order 中存在的元素之后。

我已经使用for 循环解决了它(代码在 Playground 中运行):

import Foundation
import UIKit

struct Tag: Identifiable {
    var id: Int
    var name: String
}

// Ccc > Bbb > Aaa > Ddd > Eee
var tags = [Tag(id: 1000, name: "Ccc"), Tag(id: 1001, name: "Bbb"), Tag(id: 1002, name: "Aaa"), Tag(id: 1003, name: "Ddd"), Tag(id: 1004, name: "Eee")]

// Eee > Ddd > Ccc > Bbb > Aaa
tags.sort(by: { $0.name < $1.name })

// Bbb > Ddd
var idOrdering = [1001, 1003]

// Bbb > Ddd > Aaa > Ccc > Eee
for orderIndex in idOrdering.indices {
    // Get tag id.
    let tagId = idOrdering[orderIndex]

    let tagIndex = tags.firstIndex(where: { $0.id == tagId })

    // Remove tag from original array and place it according to the `order`.
    let removedTag = tags.remove(at: tagIndex!)
    tags.insert(removedTag, at: orderIndex)
}

// Print the result.
tags.forEach {
    print($0.name)
}

原来tags中元素的顺序是Ccc &gt; Bbb &gt; Aaa &gt; Ddd &gt; Eee。其中两个名为BbbDdd的元素应根据order排序,即Bbb &gt; Ddd。其余的应按字母顺序排列。换句话说,最终结果应该是Bbb &gt; Ddd &gt; Aaa &gt; Ccc &gt; Eee。虽然上面的for 循环有效,但我怎样才能更有效地解决这个问题?

【问题讨论】:

  • 似乎我的这个答案与您想要的完全匹配:stackoverflow.com/a/43056896/3141234
  • 这个问题中的UUIDs 很笨拙,会分散核心问题的注意力。我建议你用小的Ints 替换它们,并生成一个预期的output 数组。
  • x = x.sorted { ... } 是一种不好的做法。要么只使用就地变体 (x.sort { ... }),要么更好的是,只需创建一个新变量。
  • 根据反馈更新代码。

标签: arrays swift


【解决方案1】:

我会结合两段代码:

  1. HardCodedOrdering 来自 this answer of mine,它使用固定排序(例如您的 order 数组)来派生对象的排序键。
  2. 使用此模式根据多个条件对对象进行排序:https://stackoverflow.com/a/37612765/3141234

import Foundation

struct Tag: Identifiable {
    var id = UUID()
    var name: String
}

// Bbb > Ddd
let idOrdering = HardCodedOrdering(ordering:
    UUID(uuidString: "007D8DD7-7DF7-4937-BE53-C8CCE9C34A09")!,
    UUID(uuidString: "B1792EF4-CD4C-4437-90C2-37FE56D3A18F")!,
    sortUnspecifiedItems: .last
)

// Ccc > Bbb > Aaa > Ddd > Eee
let tags = [
    Tag(id: UUID(uuidString: "20FCD62B-97B8-4097-B659-29357EABE786")!, name: "Ccc"),
    Tag(id: UUID(uuidString: "007D8DD7-7DF7-4937-BE53-C8CCE9C34A09")!, name: "Bbb"),
    Tag(id: UUID(uuidString: "36FCEBF2-9912-4D67-A0F3-5EE1BF6EFE86")!, name: "Aaa"),
    Tag(id: UUID(uuidString: "B1792EF4-CD4C-4437-90C2-37FE56D3A18F")!, name: "Ddd"),
    Tag(id: UUID(uuidString: "463927D5-F6D8-4099-99FC-D3DE226B00C9")!, name: "Eee"),
]

let result = tags.sorted {
    (idOrdering.sortKey(for: $0.id), $0.name) <
    (idOrdering.sortKey(for: $1.id), $1.name)
}

result.forEach { print($0) }

【讨论】:

  • 你的HardCodedOrdering() 比我上面的for 循环效率更高吗?
  • @Simen 从理论上讲,它具有更好的渐近时间复杂度,但显然您必须对其进行分析才能确定。但坦率地说,它的效率并不重要,它实际上是可以理解的,这方式更重要。
【解决方案2】:

基于Alexander 提到的this answer,我最终得到了以下解决方案:

tags.sort {
    (idOrdering.firstIndex(of: $0.id) ?? idOrdering.count, $0.name) <
    (idOrdering.firstIndex(of: $1.id) ?? idOrdering.count, $1.name)
}

【讨论】:

  • 为什么是idOrdering.count + 1
  • 我会使用一个常量:Int.max
  • 您要如何以及为什么要使用常量?
猜你喜欢
  • 1970-01-01
  • 2014-12-30
  • 1970-01-01
  • 2021-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多