【问题标题】:How to Properly Sort Array of Objects on Numeric Property如何在数值属性上正确排序对象数组
【发布时间】:2018-08-23 07:39:02
【问题描述】:

我有一个这样的对象数组,但有 16 个属性:

class anObject: NSObject {
   @objc var number: Int
   @objc var name: String
   @objc var price: Double

   subscript(key: String) -> Any? {
       return self.value(forKey: key)
   }
}

我可以很容易地对任何属性的数组进行排序,例如:

sortedArray = unsortedArray.sorted(by: { $0.name < $1.name } )

现在我正在对我的数组进行分组,以便我可以用部分和行填充 UITableView。我这样分组:

var groupedArray = Dictionary<String, Array<myObject>>()

for item in myArray {
    // Verify each grouping is initialized only once    
    if groupedArray[item[byProperty] as! String] == nil {
        groupedArray[item[byProperty] as! String] = Array<Items>()
    }

    // Add the item into the correct subarray
    groupedArray[item[byProperty] as! String]?.append(item)
}

然后我可以通过这样做对分组数组进行排序:

return groupedArray.sorted { $0.0 < $1.0 }

这很好用,除了我的两个属性是双打。当我对这两个属性进行排序时,Swift 会按字母顺序对组进行排序:

10.5, 11.5, 12, 1.5, 2.0 . . .

而不是数字

1.5, 2.0, 10.5, 11.5, 12 . . . 

我已经设法通过检查双打是否太短并在字符串的前面插入一个 0 来填充双打。这样做的原因是它们现在按正确的顺序排序,但最终我将不得不去掉前面的前导 0,这似乎是一个丑陋的解决方案。

鉴于 Doubles 必须用作字符串,我如何正确排序分组数组?

【问题讨论】:

标签: arrays swift sorting format


【解决方案1】:

当您开始在各处投射字符串时,您可能需要开始更改事物的设计。为什么不让字典键成为您设计的对象而不是字符串?这是我的意思的一个示例:

struct DoubleKey {
    let value: Double
}
extension DoubleKey: Hashable {
    var hashValue: Int {
        return value.hashValue
    }

    static func ==(lhs: DoubleKey, rhs: DoubleKey) -> Bool {
        return lhs.value == rhs.value
    }
}
extension DoubleKey: Comparable {
    static func <(lhs: DoubleKey, rhs: DoubleKey) -> Bool {
        return lhs.value < rhs.value
    }
}

let a = DoubleKey(value: 10.0)
let b = DoubleKey(value: 20.0)
let c = DoubleKey(value: -10.0)
let dictionary: [DoubleKey: String] = [a: "10", b: "20", c: "-10"]
let sortedDictionary = dictionary.sorted { $0.0 < $1.0 }

所以你有 [DoubleKey: Array&lt;MyObject&gt;[IntegerKey: Array&lt;MyObject&gt;] 甚至 [StringKey: Array&lt;MyObject&gt; 而不是 [String: Array&lt;myobject&gt;]

您可以实现自己的专用密钥的许多变体,并在需要其他功能时编写一些协议。如果您需要在密钥中存储一个字符串,则添加一个属性,或者更好的是,将其符合定义您需要的行为并实现它的协议。


附加键

struct StringKey {
    let value: String
}
extension StringKey: Hashable {
    var hashValue: Int {
        return value.hashValue
    }

    static func ==(lhs: StringKey, rhs: StringKey) -> Bool {
        return lhs.value == rhs.value
    }
}
extension StringKey: Comparable {
    static func <(lhs: StringKey, rhs: StringKey) -> Bool {
        return lhs.value < rhs.value
    }
}

let a = StringKey(value: "a")
let b = StringKey(value: "c")
let c = StringKey(value: "b")
let dictionary: [StringKey: String] = [a: "10", b: "20", c: "-10"]
let sortedDictionary = dictionary.sorted { $0.0 < $1.0 }

现在呢?

//EXAMPLE
protocol ViewableString {
    func view() -> String
}

extension StringKey: ViewableString {
    func view() -> String {
        return value
    }
}

extension DoubleKey: ViewableString {
    func view() -> String {
        return String(value)
    }
}

let array: [ViewableString] = [a, b, c]
array[0].view()

按照协议编程!

希望这会有所帮助!

【讨论】:

  • 我尝试将我的 Key 设为 Any,这样我就可以使用 String 或 Double,但 Any 不是 Hashable。似乎我需要一个可以是两种类型的键,字符串或双精度。我喜欢你的建议,但不确定如何修改你写的内容。
  • 我再给你补充一些
  • @AugustDanowski 添加了一些内容。
  • @AugustDanowski 不要使用任何类型,使用显式类型。为什么需要使用 Any?您存储的键是否必须在同一个字典中包含字符串和双精度值?
【解决方案2】:

好的。完全不同的答案。同样,您试图在同一个容器中放置许多不同类型的对象。这对我来说是个坏主意。也许你必须这样做。但这是使用枚举的一种方法:

enum SpecialKey {
    case integer(Int)
    case double(Double)
    case string(String)

    func asString() -> String {
        switch self {
            case let .integer(a):
                return String(a)
            case let .double(a):
                return String(a)
            case let .string(a):
                return a
        }
    }
}
extension SpecialKey: Comparable {
    static func <(lhs: SpecialKey, rhs: SpecialKey) -> Bool {
        switch (lhs, rhs) {
        case let (.double(a), .double(b)):
            return a < b
        case let (.integer(a), .integer(b)):
            return a < b
        case let (.string(a), .string(b)):
            return a < b
        default:
            return false //Add more cases with different comparisons!
        }
    }

    static func ==(lhs: SpecialKey, rhs: SpecialKey) -> Bool {
        switch (lhs, rhs) {
            case (.integer(_), .integer(_)),
                 (.double(_), .double(_)),
                 (.string(_), .string(_)):
                return true
            default:
                return false
        }
    }
}

extension SpecialKey: Hashable {
    var hashValue: Int {
        switch self {
        case let .integer(a):
            return a.hashValue
        case let .double(a):
            return a.hashValue
        case let .string(a):
            return a.hashValue
        }
    }
}

let a = SpecialKey.integer(10)
let b = SpecialKey.string("something")
let c = SpecialKey.double(10.5)
let dictionary: [SpecialKey: String] = [a: "a", b: "b", c: "c"]

这可能更像您正在寻找的。​​p>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-02
    • 1970-01-01
    相关资源
    最近更新 更多