2017-02-25 50 views
3

爲了簡化。可以說我有一些獨特的價值 - >從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://stackoverflow.com/q/2147505/2976878)可能是一個很好的起點。 – Hamish

回答

5

如果你堅持要用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 ..這意味着二分查找方法會更快。我期待看到我可以如何使用他的解決方案,但製作搜索二進制文件。 –

2

這是在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"] 
相關問題