2017-02-25 3 views
3

簡素化のため。私はいくつかのユニークな値を持っていると言うことができます - >1 to 10スウィフトディクショナリでキーとして範囲を使用することはできますか?

から数字が今、私は「最初」の値に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の値を持つことができます。だから私は範囲が動作することができますかと思います。

structの代わりにclassに値を作ることができるので、複数のキーを同じクラスオブジェクトにマップできるようになっていますが、上記のように機能する辞書を作成することは可能でしたか?

+0

データ構造の種類に関する問題あなたは「キー」として範囲が重複している状況を扱う必要があるということを提案しています - これらの範囲の交差点の値をどのような値にマッピングすればよいでしょうか? – Hamish

+0

dict。の文脈では、 'Hashable'に準拠するインスタンスのハッシュ値は、' keys 'を(ハッシュ値に基づいて)異なるビンに配置するために単に使用され、アクセスが試行される場合いくつかのキーを持つビンは、検索されている実際の_unique_キー(これはすべて、辞書内のキーによって償却されたO(1)の値へのアクセスを許可する目的で)の等価性をテストするために進められます。これは、(理論的には 'Hashable'に拡張された' Range 'に対して) '0 ... 10'は一意のキーであり、' 1 ... 10'もハッシュ値が同じであっても一意のキーであることを意味します。 – dfri

+1

特にスウィフトではありませんが、[こちらのQ&A](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'実装の本体を単一の式に凝縮することができます:' return keys。flatMap {明細[$ 0.0]} 'のためにflatMap {self [$ 0.0]}'と '返されるrange.enumerated()上部実装と下部実装をそれぞれ示します。 – 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"] 
関連する問題