【问题标题】:Swift: Nested dictionary remove all matching keysSwift:嵌套字典删除所有匹配的键
【发布时间】:2018-11-11 21:00:43
【问题描述】:

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

一个示例用例可能如下所示:

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

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

预期结果:

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

【问题讨论】:

  • 以上的预期结果是什么?
  • 我添加了一个更好的例子。
  • 你能从字典中删除一个键/值对吗?你能遍历数组的元素吗?

标签: swift dictionary nested dplyr


【解决方案1】:

目前尚不清楚尝试了什么以及卡在哪里,您确实应该将其包括在内 - 不这样做将是您被否决的原因。

但是,让我们看看我们是否可以提供帮助。您声明您的字典是[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(即语法和预定义函数可能很容易不同)但算法仍然适用。

【讨论】:

  • 正是我想要的;完美,因为它适用于更深层次的嵌套字典!
【解决方案2】:

不鼓励使用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"]]]

(请记住,字典是无序的集合,因此结果可能按顺序不同)

【讨论】:

  • 如果您要使用循环而不是映射(这是一个有效的选择),为什么要保留两个通道而不是在同一个循环中删除密钥(请参阅我之前的回答)?
猜你喜欢
  • 2017-03-08
  • 1970-01-01
  • 2019-11-18
  • 2020-10-20
  • 2014-01-19
  • 1970-01-01
  • 1970-01-01
  • 2018-12-24
相关资源
最近更新 更多