是否可以在Swift Dictionary中使用范围作为键?
为了简化。可以说我有一些独特的价值 - >从1 to 10
是否可以在Swift Dictionary中使用范围作为键?
数字现在我想1-5
地图的价值“第一”,我想6-10
地图值“第二”
有没有一种方法我可以创建或者扩展字典以像下面一样工作?
let dict: [Range<Int> : String]
的目标是有以下结果:
print(dict[1]) // prints first
print(dict[2]) // prints first
print(dict[3]) // prints first
print(dict[7]) // prints second
print(dict[8]) // prints second
print(dict[9]) // prints second
我目前做的方式是简单地有多个键映射到相同的值。但我的字典有时可能有60k值。所以我想知道如果一个范围可以工作。
我知道我可以将值变成class
而不是struct
,这样多个键可以映射到同一个类对象,但我想知道是否简单地创建一个像上面那样工作的Dictionary是可能的?
如果你坚持要用Dictionary
,你必须等待,直到雨燕3.1(目前处于测试阶段):
extension CountableClosedRange : Hashable {
public var hashValue: Int {
return "\(lowerBound) to \(upperBound)".hashValue
}
}
// This feature is called concrete-type extension and requires Swift 3.1
extension Dictionary where Key == CountableClosedRange<Int> {
subscript(rawValue rawValue: Int) -> Value? {
for k in self.keys {
if k ~= rawValue {
return self[k]
}
}
return nil
}
}
let dict : [CountableClosedRange<Int>: String] = [
1...5: "first",
6...10: "second"
]
print(dict[rawValue: 1])
print(dict[rawValue: 2])
print(dict[rawValue: 3])
print(dict[rawValue: 7])
print(dict[rawValue: 8])
print(dict[rawValue: 9])
但是,如果你实现你自己的数据模型是一个更加清晰:
struct MyRange {
var ranges = [CountableClosedRange<Int>]()
var descriptions = [String]()
mutating func append(range: CountableClosedRange<Int>, description: String) {
// You can check for overlapping range here if you want
self.ranges.append(range)
self.descriptions.append(description)
}
subscript(value: Int) -> String? {
for (i, range) in self.ranges.enumerated() {
if range ~= value {
return descriptions[i]
}
}
return nil
}
}
var range = MyRange()
range.append(range: 1...5, description: "one")
range.append(range: 6...10, description: "second")
print(range[1])
print(range[2])
print(range[6])
print(range[7])
print(range[100])
上面的一个很好的答案,但是可能很好地明确指出,当使用上述新的下标实现时,按键访问的值将是'O(n)'而不是'O(1)'的摊销(正如人们所期望的后者用于词典)。此外,如果您想全力以赴地实现功能,可以将上述'subscript'实现的主体压缩为单个表达式:'返回键。first(其中:{$ 0.1〜= rawValue})。flatMap {self [$ 0.0]}'和'return ranges.enumerated()。first(其中:{$ 0.1〜= value})。flatMap {descriptions [$ 0.0]}'for顶部和底部的实现,分别。 – dfri
......并将其与应用于排序列表的二进制搜索方法进行比较(我相信OP指出了非重叠/唯一的范围),其将具有“O(log n)”访问的最差情况性能。 – dfri
@dfri'O(log n)'hmm ..这意味着二分查找方法会更快。我期待看到我可以如何使用他的解决方案,但制作搜索二进制文件。 –
这是在Swift 3.0中,它可能不像Code Different的答案那么好。现在
class MyRange: Hashable, Equatable {
public var hashValue: Int {
get {
return (self.range.lowerBound + self.range.upperBound).hashValue
}
}
var range: Range<Int>!
public static func ==(_ lhs: MyRange, _ rhs: MyRange) -> Bool {
return lhs.range == rhs.range
}
init(range: Range<Int>) {
self.range = range
}
}
extension Dictionary where Key: MyRange, Value: ExpressibleByStringLiteral {
internal subscript(index: Int) -> [String] {
return self.filter({$0.key.range.contains(index)}).map({$0.value as! String})
}
}
,你可以让你的字典,像这样:
var dict = Dictionary<MyRange, String>()
dict[MyRange(range: 0..<5)] = "first"
dict[MyRange(range: 5..<10)] = "second"
获取值与整数作品和范围:
print(dict[1]) // ["first"]
print(dict[5]) // ["second"]
print(dict[11]) // []
print(dict[MyRange(range: 0..<5)]) // "first"
print(dict[MyRange(range: 0..<6)]) // nil
字典应该是这样的:
print(dict)
// [MyRange: "first", MyRange: "second"]
这种数据结构的问题哟我建议你需要处理的情况下,你有重叠范围作为'关键' - 这些范围的交集中的值应该映射到什么值? – Hamish
还要注意,在字典的上下文中,符合'Hashable'的实例的哈希值仅用于将“keys”放置在不同的bin中(基于hash值),并且只有在尝试访问一个带有几个键的二进制文件将进入字典,以检测被搜索的实际_unique_键的相等性(所有这些都允许通过字典中的键对O(1)的值进行分段访问)。这意味着(理论上'范围'扩展到'Hashable'),'0 ... 10'是一个唯一的键,'1 ... 10'也是一个唯一的键,即使它们的hashvales是相同的。 – dfri
不是特别的Swift,但[这个问答](http://*.com/q/2147505/2976878)可能是一个很好的起点。 – Hamish