Keyの配列とDictionaryを見比べて、配列になくてDictionaryにある値を削除する


タイトルが長く、わかりにくいです

つまり何がしたいの?

ここにStringの配列があります。

let keys = ["A", "B", "C", "D"]

Dictionaryもあります。

var dictionary = ["A": 0, "B": 1, "C": 2, "D": 3, "E": 4]

見比べると、"E"というkeyが、keysにはなくてdictionaryにはあります。
この"E"を削除したい

どちらにもあるものを取得

dictionaryのkeyを取得して、keysに含まれるかを確認する。
keysに含まれるものだけの配列を取得できた

let keys = ["A", "B", "C", "D"]
var dictionary = ["A": 0, "B": 1, "C": 2, "D": 3, "E": 4]
let contains = dictionary.keys.filter { (key) -> Bool in
    keys.contains(key)
}
print(contains) // ["B", "A", "C", "D"]

Dictionaryにしかないものを取得

先ほどの判定を逆にしたら良い。
dictionaryにしか含まれないものの配列を取得できた

let nocontains = dictionary.keys.filter { (key) -> Bool in
    !keys.contains(key) // !をつける
}
print(nocontains) // ["E"]

Dictionaryにしかないものを削除

あとはdictionaryから削除するだけ。

nocontains.forEach { (removeKey) in
    dictionary.removeValue(forKey: removeKey)
}
print(dictionary) // ["B": 1, "A": 0, "C": 2, "D": 3]

Dictionaryを拡張

ついでに、keysに含まれるものを削除する関数も作成

extension Dictionary {

    // 引数の配列に含まれていないものを削除
    // mutating func removeUnincludedValue(in keys: Array<Key>) {
    //  self.keys.filter { (key) -> Bool in
    //      !keys.contains(key)
    //      }.forEach { (removeKey) in
    //          self.removeValue(forKey: removeKey)
    //  }
    // }

    // 追記: コメント欄で教えてもらった
    func removeUnincludedValue(in keys: Array<Key>) -> Dictionary<Key, Value> {
        return self.filter { keys.contains($0.key) }
    }

    // 追記: コメント欄で教えてもらった
    func filtered(byKeys keys: Array<Key>) -> Dictionary {

        var newDictionary: Dictionary = [:]
        var keysIterator = keys.makeIterator()

        newDictionary.reserveCapacity(self.count)

        while let key = keysIterator.next() {
            if let value = self[key] {
                newDictionary[key] = value
            }
        }

        return newDictionary

    }

    // 追記: コメント欄で教えてもらった
    mutating func filter(byKeys keys: [Key]) {

        self = self.filtered(byKeys: keys)

    }

    // 追記: コメント欄で教えてもらった
    // func filtered(keys: [Key]) -> Dictionary {
    //  return keys.reduce(into: [Key: Value]()) { dict, key in dict[key] = self[key] }
    // }

    // 追記: コメント欄で教えてもらったのを、swiftっぽく書いてみた
    func filtered(keys: [Key]) -> Dictionary {
        return keys.reduce(into: [Key: Value]()) { $0[$1] = self[$1] }
    }



    // 引数の配列に含まれているものを削除
    // mutating func removeIncludedValue(in keys: Array<Key>) {
    //  self.keys.filter { (key) -> Bool in
    //      keys.contains(key)
    //  }.forEach { (removeKey) in
    //      self.removeValue(forKey: removeKey)
    //  }
    // }

    // 追記: もっと簡単に書けた
    mutating func removeIncludedValue(in keys: Array<Key>) {
        keys.forEach { (removeKey) in
            self.removeValue(forKey: removeKey)
        }
    }
}

keysに含まれてないけど、dictionaryに含まれているものを削除

let keys = ["A", "B", "C", "D"]
var dictionary = ["A": 0, "B": 1, "C": 2, "D": 3, "E": 4]
dictionary.removeUnincludedValue(in: keys) // ["B": 1, "A": 0, "C": 2, "D": 3]

keysに含まれていて、dictionaryにも含まれているものを削除

let keys = ["A", "B", "C", "D"]
var dictionary = ["A": 0, "B": 1, "C": 2, "D": 3, "E": 4]
dictionary.removeIncludedValue(in: keys) // ["E": 4]

型が違う時はコンパイルエラーでるから安心

let keys = ["A", "B", "C", "D"]
var dictionary1 = [0: 0, 1: 1, 2: 2]
dictionary1.removeUnincludedValue(in: keys)
// cannot convert value of type '[String]' to expected argument type 'Array<Int>'

dictionary1のkeyはIntなのでkeysはIntの配列でください」ということですね

最後に

ふと思ってやってみたが、簡単に作ることができました。
もっと簡単にできるとか、なにか間違いがありましたら教えてください
あと、関数のネーミングに不安が残りました