新しい環境に移ってから早2週間が経ちました。
今日は実際開発中のライブラリでも使った、2つのDictionaryを結合するExtensionと演算子を紹介します。
2つのDictionaryを結合する関数を定義する
まずは、結合する関数union
を定義してみます。
public extension Dictionary {
public func union(other: Dictionary) -> Dictionary {
var tmp = self
other.forEach { tmp[$0.0] = $0.1 }
return tmp
}
}
こう定義することで、
let a = ["key1": 1]
let b = ["key2": 2]
let c = a.union(b)
と書くことができます。
もし、 mutating
に扱いたい場合は、
public mutating func united(other: Dictionary) {
other.forEach { self[$0.0] = $0.1 }
}
//----
var a = ["key1": 1]
let b = ["key2": 2]
a.united(b) // ["key1": 1, "key2": 2]
と定義してあげるといい感じになります。
演算子 +
を定義する
より直感的に使いやすくするために、2つのDictionaryを結合する演算子 +
を定義してみます。
public func +<K, V>(lhs: Dictionary<K, V>, rhs: Dictionary<K, V>) -> Dictionary<K, V> {
return lhs.union(rhs)
}
public func +=<K, V>(inout lhs: Dictionary<K, V>, rhs: Dictionary<K, V>) {
lhs = lhs + rhs
}
これで、
let a = ["key1": 1]
let b = ["key2": 2]
let c = a + b // ["key1": 1, "key2": 2]
と書くことができます。
Optionalの場合にも対応してみる
ただ、上記のままでは、どちらかがOptionalなDictionaryだった場合には、そのまま結合することができません。
let a: [String: Int] = ["key1": 1]
let b: [String: Int]? = ["key2": 2]
let c = a + b // error!
なので、Optionalでも問題なく扱えるように 演算子 +
をオーバーロードします。
また、今回の結合では、以下のように扱うことにします。
A \ B | Some(let b) | None (nil) |
---|---|---|
Some(let a) | a + b | a |
None (nil) | b | nil |
この表を基に、演算子を以下のようにオーバーロードして定義します。
public func +<K, V>(lhs: Dictionary<K, V>?, rhs: Dictionary<K, V>?) -> Dictionary<K, V>? {
switch (lhs, rhs) {
case (.Some(let l), .Some(let r)):
return l + r
case (.Some(let l), .None):
return l
case (.None, .Some(let r)):
return r
default:
return nil
}
}
public func +=<K, V>(inout lhs: Dictionary<K, V>?, rhs: Dictionary<K, V>?) {
lhs = lhs + rhs
}
※もし片方がnilのDictionaryだったら、結果としてnilにしたい!という場合条件を変えてあげれば実装できるかと思います。
パターンマッチがいい感じ
左辺、右辺のDictionaryがnilかどうかを見るのに、 if let
を使うとあまりスッキリと書けないので、
switch
文で、パターンマッチを用いてみました。
Optionalな変数に対して、 .Some(let variable)
(値がある)と、 .None
(nilである)でパターンマッチをして、 .Some()
内で定義した変数名で、アンラップした値を扱うことができます。