Swift:嵌套字典删除所有匹配的键

时间:2018-11-11 21:00:44

标签: swift dictionary nested mutate

我需要一个字典扩展名,该扩展名可以从任意字典([String: Any])中删除所有匹配的键。

一个用例示例如下:

从给定字典中删除与以下任一匹配的所有键:["test", "staging"]

[
"foo":  [ "bar": "tralala" ]
"test": [ "foo": "bar", "staging": "hi"]
"aaa":  [ "bbb": "cccc", "staging": "jjj"]
]

预期结果:

[
"foo": [ "bar": "tralala" ]
"aaa":  [ "bbb": "cccc"]
]

2 个答案:

答案 0 :(得分:0)

不建议将Any用作字典值的类型。在这种情况下,最好将字典定义为[String : [String : String]]

如果您真的无法避免使用任何字典,让我们定义一下具有多个嵌套字典的字典:

let dictionary = [
    "foo" : [ "bar": "tralala" ],
    "test": [ "foo": "bar", "staging": "hi"],
    "aaa" : [ "bbb": "cccc", "staging": "jjj"],
    "d"   : [ "e": "f", "g": ["h": "i", "test": "j"]],
]

并声明我们要删除的密钥:

let badKeys = ["test", "staging"]

这是一个删除不需要的键的递归函数:

func remove(_ keys: [String], from dict: [String: Any]) -> [String: Any] {
    var filtered = dict.filter { !keys.contains($0.key) }
    for entry in filtered {
        if let value = entry.value as? [String : String] {
            filtered[entry.key] = remove(badKeys, from: value)
        }
    }
    return filtered
}

您可以像这样使用它:

let result = remove(badKeys, from: dictionary)

哪种产量:

  

[“ aaa”:[“ bbb”:“ cccc”],“ foo”:[“ bar”:“ tralala”],“ d”:[“ e”:“ f”,“ g”:[ “ h”:“ i”]]]

(请注意,字典是无序集合,因此结果的顺序可能会有所不同)

答案 1 :(得分:-1)

目前还不清楚您尝试了什么 以及您遇到了什么困难,您确实应该包括此内容-不这样做将成为您遭到否决的原因。 < / p>

但是,让我们看看是否可以提供帮助。您声明字典的类型为[String:Any],并且没有给出任何嵌套限制,因此我们将使用以下测试数据:

let sampleDict : [String:Any] =
[
   "foo":  [ "bar": "tralala" ],
   "test": [ "foo": "bar", "staging": "hi"],
   "staging" : 3,
   "one" : [ "two" : [ "three" : 3, "staging" : 4.2]],
   "aaa":  [ "bbb": "cccc", "staging": "jjj"]
]

如果我们的算法能够应付任何问题(著名的遗言……)。

使用预定义方法并避免循环的简单算法:

  1. 过滤字典,删除需要删除键的所有键/值对。
  2. 映射过滤后的字典中的值,对于本身就是[String:Any]字典的任何值,将此算法递归地应用于该值。

在Swift中:

func removeMatchingKeys(_ dict : [String:Any], _ keysToRemove : [String]) -> [String:Any]
{
   return dict
      // filter keeping only those key/value pairs
      // where the key isn't in keysToRemove
      .filter { !keysToRemove.contains($0.key) }
      // map the values in the filtered dictionary recursing
      // if the value is itself a [String:Any] dictionary
      .mapValues
      {  if let nested = $0 as? [String:Any]
         // value is dictionary, recurse
         { return removeMatchingKeys(nested, keysToRemove) }
         else
         // value isn't a dictionary, leave as is
         { return $0 }
      }
}

使用以下按键进行测试:

let sampleKeys = ["test", "staging"]

声明:

print( removeMatchingKeys(sampleDict, sampleKeys) )

产生:

["foo": ["bar": "tralala"], "aaa": ["bbb": "cccc"], "one": ["two": ["three": 3.0]]]

以上算法对数据进行了两次传递,首先对其进行过滤,然后对其进行映射。如果是且仅当这是一个性能问题,则可以用一个简单的手写循环将两个预定义的函数filtermap替换为一个合并了操作的手写循环并且只传递一次数据。

注意:以上代码使用的是Xcode 10 / Swift 4.2,其他任何版本和YMMV(即语法和预定义函数可能很容易不同),但是该算法仍然适用。