【问题标题】:Sort/Filter dictionary by property of object in Key按 Key 中对象的属性对字典进行排序/过滤
【发布时间】:2016-03-23 19:02:25
【问题描述】:

我有一本像 例如:

let dict = Dictionary<Month, Array<Int32>>()

Obj1.price = "10"
Obj1.value = "abc"
Obj1.title = "January"

Obj2.price = "10"
Obj2.value = "def"
Obj2.title = "April"

Obj3.price = "10"
Obj3.value = "pqr"
Obj3.title = "February"

Obj4.price = "4"
Obj4.value = "mnq"
Obj4.title = "April"

dict = [ Obj1: [3,4], Obj2 : [1,2], Obj3: [8,9], Obj4: [3,3] ]

我有一个自定义月份数组

let sortTemplate = ["April", "May", "June", "July", "August", "September", "October", "November", "December", "January", "February", "March"]

我想让字典排序为 [ Obj2:[1,2],Obj4:[3,3],Obj1:[3,4],Obj3:[8,9]]

简而言之,我希望根据键属性上的自定义引用数组对字典进行排序。 我知道我们不能对字典进行排序,但想根据自定义 sortTemplate 进行排序并插入到字典数组中

这方面的任何提示都会很有用。 我知道我们可以使用值和键进行排序

【问题讨论】:

  • @SyedAliSalman 为什么?他不希望数组中字典的键。他只是想让它们分类。是因为您对字典在 Objective-C 中未排序的评论吗?我认为在 Swift 中他们可以对字典进行排序。
  • Month类是否符合协议Hashable
  • 编辑了问题,我希望根据 sortTemplate 对键进行排序并将字典插入到字典数组中,以便它们可以保持排序顺序。
  • @dfri: 我添加了 override var hashValue: Int 函数,它返回标题、价值和价格的哈希值

标签: ios swift sorting dictionary custom-properties


【解决方案1】:

这是一种可能的解决方案,它使用Dictionarys 内置排序功能,但是在您的示例中将title 属性显示为自定义enum 而不是String。然后通过enum 中的月份顺序隐式给出“排序模板”。

enum MonthSortTemplate 和你的班级MyClass(后者你没有给我们洗澡,所以我自己做了一个 MWE):

enum MonthSortTemplate: Int {
    case April = 1
    case January
    case February
    // ... rest of months follows, in the order you prefer
}

class MyClass {
    var price = ""
    var value = ""
    var title: MonthSortTemplate = .April    
}

// Hashable (just some dummy, don't know how you've set this up)
extension MyClass: Hashable {
    var hashValue: Int {
        return price.hashValue ^ value.hashValue
    }
}

// Equatable (just some dummy, don't know how you've set this up)
func ==(lhs: MyClass, rhs: MyClass) -> Bool {
    return lhs.price == rhs.price && lhs.value == rhs.value
}

创建您的 MyClass 实例,添加到您的字典中,并使用后者的 .sort(...) 函数进行自定义闭包,为这种特定类型的比较指定。

var Obj1 = MyClass()
var Obj2 = MyClass()
var Obj3 = MyClass()

Obj1.price = "10"
Obj1.value = "abc"
Obj1.title = .January

Obj2.price = "10"
Obj2.value = "def"
Obj2.title = .April

Obj3.price = "10"
Obj3.value = "pqr"
Obj3.title = .February

var dict = Dictionary<MyClass, Array<Int32>>()
dict = [ Obj1: [3,4], Obj2 : [1,2], Obj3: [8,9]]

// your custom sort closure, for Dictionary.sort(...) method
let byMonthTemplate = {
    (elem1:(key: MyClass, val: [Int32]), elem2:(key: MyClass, val: [Int32]))->Bool in
    if elem1.key.title.rawValue < elem2.key.title.rawValue {
        return true
    } else {
        return false
    }
}

let sortedDict = dict.sort(byMonthTemplate)
print("\(dict)")

另一种选择---如果你真的喜欢你的类属性titleString 类型---是为MyClass 对象定义&lt; 运算符:

func <(lhs: MyClass, rhs: MyClass) -> Bool {
    // do comparison stuff with strings lhs.title and rhs.title
    // with regard to your ordering of choice (array sortTemplate)
    return ... 
}

在这种情况下,“凌乱”的东西最终会出现在这个函数中,而实际的排序可以非常优雅地执行

let sortedDict = dict.sort { $0.0 < $1.0 }

就我个人而言,我更喜欢enum 解决方案(不过,这是题外话)。


编辑:

根据您的要求,我将为您的班级MyClass 提供一个&lt; 运算符示例。这绝不是一个最佳的,但也许你可以从我的例子中改进它。

// add sortTemplate as a static property of MyClass
class MyClass {
    var price = ""
    var value = ""
    var title = "" 
    static let sortTemplate = ["April", "May", "June", "July", "August", "September", "October", "November", "December", "January", "February", "March"]
}

// define < operator for MyClass objects
func <(lhs: MyClass, rhs: MyClass) -> Bool {
    let indexOfLhs = MyClass.sortTemplate.indexOf({$0 == lhs.title})
    let indexOfRhs = MyClass.sortTemplate.indexOf({$0 == rhs.title})
    return indexOfLhs < indexOfRhs
}

// you can now sort your dictionary according to
let sortedDict = dict.sort { $0.0 < $1.0 }

【讨论】:

  • Enum 确实是一个不错的解决方案,但此时我必须更改很多代码。你能在String中提出一些建议吗?我们将如何编写 func
  • sortTemplate 修复了吗?即,此模板对您的所有 MyClass() 对象都有效吗?
  • 另外我正在使用标题在 UI 中显示为部分标题
  • 是的 sortTemplate 是固定的,我从 API 接收到
  • 那么:让sortTemplate 成为MyClass 的静态成员是否合适?我已经为适用的“字符串解决方案”添加了一个建议。
【解决方案2】:

希望这种帮助,快速而肮脏的方式

let dict = ["Obj1":[3,4],"Obj2":[1,2],"Obj3":[8,9],"Obj4":[3,3]]

let dict1 =  dict.sort { $0.1[0] < $1.1[0]} //Sort by first object in array
let dict2 =  dict1.sort { $0.1[1] < $1.1[1]} //Sort by second object in array

print(dict2)

输出:[("Obj2", [1, 2]), ("Obj4", [3, 3]), ("Obj1", [3, 4]), ("Obj3", [8, 9])]

【讨论】:

  • 这将根据 Int32 数组中的第一个/第二个整数条目进行排序,但发布者希望对 w.r.t. 进行排序。他的自定义月份排序模板,将他的MyClass 对象中的成员属性title 与此模板进行比较。
  • 转换为月份字符串也很容易,只需使用另一个带有num:month 或枚举甚至本地化字符串的字典并基于它进行转换
猜你喜欢
  • 1970-01-01
  • 2012-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-08
  • 2019-01-25
  • 2011-03-21
  • 2013-02-12
相关资源
最近更新 更多