Swift中含层级关联的Parameter数组转换为字典的算法实现疑问
如何将带有父子关系的Parameter数组转换为嵌套字典(支持无序数组)
我正在尝试将一个包含父子关系的Parameter结构体数组转换为嵌套字典,但在处理带有parent的元素时遇到了困难,尤其是当数组无序(子节点可能比父节点先出现)的情况。
场景说明
我的Parameter结构体定义如下:
struct Parameter { let type: String let name: String let value: Any let parent: String? }
示例输入数组:
let array = [ Parameter(type: "string", name: "name1", value: "bar", parent: nil), Parameter(type: "bool", name: "name2", value: true, parent: nil), Parameter(type: "array", name: "name3", value: [], parent: nil), Parameter(type: "bool", name: "name3.1", value: false, parent: "name3"), Parameter(type: "array", name: "name3.2", value: [], parent: "name3"), Parameter(type: "string", name: "name4.1", value: "baz", parent: "name3.2") ]
期望输出的嵌套字典:
[ "name1": "bar", "name2": true, "name3": [ "name3.1": false, "name3.2": [ "name4.1": "baz" ] ] ]
我目前的代码只能处理无parent的根节点,不知道如何处理有父节点的情况,尤其是数组无序时子节点先被遍历到的场景:
func makeDictionary(from array: [Parameter]) -> [String: Any] { var dictionary: [String: Any] = [:] for parameter in array { if let parent = parameter.parent { #warning("how to handle this case?") } else { dictionary[parameter.name] = parameter.value } } return dictionary }
解决方案
核心问题是数组无序导致子节点可能先于父节点被处理,以及需要在嵌套结构中准确定位父节点的位置。我们可以通过以下思路解决:
- 先建立所有节点的映射表,方便快速查找任意节点(包括父节点);
- 使用引用类型的
NSMutableDictionary来处理嵌套结构的修改(避免值类型字典的拷贝问题); - 递归创建父节点的容器,确保子节点处理时父容器已经存在;
- 避免覆盖已创建的容器(比如父节点的初始值会被子节点创建的容器替换)。
完整实现代码:
struct Parameter { let type: String let name: String let value: Any let parent: String? } func makeDictionary(from array: [Parameter]) -> [String: Any] { let root = NSMutableDictionary() // 建立节点名称到节点的映射,快速查找任意节点 let nodeMap = Dictionary(uniqueKeysWithValues: array.map { ($0.name, $0) }) // 递归函数:获取或创建指定节点的父容器 func getParentContainer(for nodeName: String) -> NSMutableDictionary? { guard let currentNode = nodeMap[nodeName] else { return nil } if let parentName = currentNode.parent { // 先尝试获取父节点的容器 if let parentContainer = getParentContainer(for: parentName) { // 检查父容器中是否已有当前节点的容器,没有则创建 if let existingContainer = parentContainer[nodeName] as? NSMutableDictionary { return existingContainer } else { let newContainer = NSMutableDictionary() parentContainer[nodeName] = newContainer return newContainer } } else { // 父节点不存在,先创建父节点的容器 guard let parentNode = nodeMap[parentName] else { return nil } let parentContainer = NSMutableDictionary() // 递归创建父节点的父容器(如果有) if let grandparentName = parentNode.parent { getParentContainer(for: grandparentName)?[parentName] = parentContainer } else { // 父节点是根节点,直接加入顶层字典 root[parentName] = parentContainer } // 创建当前节点的容器并加入父容器 let newContainer = NSMutableDictionary() parentContainer[nodeName] = newContainer return newContainer } } else { // 当前节点是根节点,父容器就是顶层字典 return root } } // 遍历所有节点,加入对应容器 for parameter in array { if let parentName = parameter.parent { guard let parentContainer = getParentContainer(for: parentName) else { continue } // 如果父容器中已经存在当前节点的容器(子节点先创建的),不覆盖 if parentContainer[parameter.name] is NSMutableDictionary { continue } parentContainer[parameter.name] = parameter.value } else { // 根节点如果已经存在容器(子节点先创建的),不覆盖初始值 if root[parameter.name] is NSMutableDictionary { continue } root[parameter.name] = parameter.value } } // 将NSMutableDictionary转换为Swift原生字典返回 return root as! [String: Any] }
代码说明
- 节点映射表:
nodeMap让我们可以O(1)时间查找任意节点,避免重复遍历数组; - 递归创建容器:
getParentContainer会逐层检查并创建父节点的容器,确保子节点处理时父结构已经存在; - 避免覆盖容器:当子节点先处理并创建了父节点的容器后,后续处理父节点时不会用初始值(比如
[])覆盖已有的容器,保证子节点内容不丢失; - 引用类型优势:使用
NSMutableDictionary可以直接修改嵌套结构中的字典,不需要处理值类型的拷贝问题,简化了代码逻辑。
内容的提问来源于stack exchange,提问作者dehlen
相关产品推荐
相关产品推荐

