是否可以在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是可能的?

+0

这种数据结构的问题哟我建议你需要处理的情况下,你有重叠范围作为'关键' - 这些范围的交集中的值应该映射到什么值? – Hamish

+0

还要注意,在字典的上下文中,符合'Hashable'的实例的哈希值仅用于将“keys”放置在不同的bin中(基于hash值),并且只有在尝试访问一个带有几个键的二进制文件将进入字典,以检测被搜索的实际_unique_键的相等性(所有这些都允许通过字典中的键对O(1)的值进行分段访问)。这意味着(理论上'范围'扩展到'Hashable'),'0 ... 10'是一个唯一的键,'1 ... 10'也是一个唯一的键,即使它们的hashvales是相同的。 – dfri

+1

不是特别的Swift,但[这个问答](http://*.com/q/2147505/2976878)可能是一个很好的起点。 – Hamish

如果你坚持要用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]) 
+0

上面的一个很好的答案,但是可能很好地明确指出,当使用上述新的下标实现时,按键访问的值将是'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

+0

......并将其与应用于排序列表的二进制搜索方法进行比较(我相信OP指出了非重叠/唯一的范围),其将具有“O(log n)”访问的最差情况性能。 – dfri

+0

@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"]