Ruby嵌套哈希转换:将键值结构转为name-children层级结构
Ruby 嵌套哈希转
name-children层级结构 需求说明
我有一个任意深度的Ruby嵌套哈希,父节点的键对应值为另一个哈希,最终叶子节点的键对应值是哈希数组。需要将其转换为**每个父节点包含name(原键名)和children(转换后的子结构)**的层级结构;或者直接在构建数据时生成目标结构(更倾向这种方式)。
原数据结构示例
h = { foo: { foo_1: { foo_1_1: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ], foo_2_1: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ] }, foo_2: { foo_2_1: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ], foo_3_1: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ], foo_3: { foo_2_1: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ], foo_3_1: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ] } }, bar: { bar_1: { bar_1_1: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ], bar_2: { bar_2_1: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ], bar_3: { bar_3_1: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ] } } } } } }
目标层级结构示例
result = { name: :foo, children: [ { name: :foo_1, children: [ { name: :foo_1_1, children: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ] }, { name: :foo_2_1, children: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ] } ] }, { name: :foo_2, children: [ { name: :foo_2_1, children: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ] }, { name: :foo_3_1, children: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ] }, { name: :foo_3, children: [ { name: :foo_2_1, children: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ] }, { name: :foo_3_1, children: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ] } ] } ] }, { name: :bar, children: [ { name: :bar_1, children: [ { name: :bar_1_1, children: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ] }, { name: :bar_2, children: [ { name: :bar_2_1, children: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ] }, { name: :bar_3, children: [ { name: :bar_3_1, children: [ {x1: {a:1, b:2, c:2}}, {x2: {a:5, b:2, c:2}} ] } ] } ] } ] } ] } ] }
现有数据构建代码
我当前用以下代码生成原始嵌套哈希:
@tree = Hash.new {|h,k| h[k] = Hash.new(&h.default_proc)} @valid_values = [:foo,:bar,:baz] @combinations = @valid_values.permutation(@valid_values.length) data_set.each do |data_hash| @combinations.each do |comb| @tree.dig(*comb.map{|k| data_hash[k]})&.[]=(data_hash[:x], {a: data_hash[:a], b: data_hash[:b]}) end end
解决方案
方法一:将现有嵌套哈希转换为目标结构
写一个递归方法遍历哈希,将每个节点转换为name-children格式:
def convert_to_tree(hash) hash.map do |key, value| if value.is_a?(Hash) { name: key, children: convert_to_tree(value) } else # 叶子节点:将数组元素直接作为children { name: key, children: value } end end end # 调用示例:如果@tree是根哈希,取第一个元素作为最终结构 result = convert_to_tree(@tree).first
方法二:直接构建目标结构(推荐)
修改数据构建逻辑,直接生成name-children结构,避免后续转换:
# 生成单个节点的辅助方法 def build_node(name) { name: name, children: [] } end # 递归查找或创建路径对应的节点 def find_or_create_node(parent, path) return parent if path.empty? current_name = path.first child = parent[:children].find { |node| node[:name] == current_name } unless child child = build_node(current_name) parent[:children] << child end find_or_create_node(child, path[1..-1]) end # 初始化根节点集合 @tree_nodes = [] @valid_values = [:foo,:bar,:baz] @combinations = @valid_values.permutation(@valid_values.length) data_set.each do |data_hash| @combinations.each do |comb| path = comb.map { |k| data_hash[k] } next if path.empty? # 查找或创建根节点 root_node = @tree_nodes.find { |n| n[:name] == path.first } unless root_node root_node = build_node(path.first) @tree_nodes << root_node end # 找到路径对应的最终父节点 parent_node = path.size == 1 ? root_node : find_or_create_node(root_node, path[1..-1]) # 添加叶子节点 parent_node[:children] << { data_hash[:x] => { a: data_hash[:a], b: data_hash[:b] } } end end # 最终结果:如果只有一个根节点,取@tree_nodes.first;否则用@tree_nodes数组 result = @tree_nodes.first
说明
- 叶子节点支持任意复杂结构,只需保证父节点包含
name和children字段即可。 - 方法二直接构建目标结构,避免了二次转换的开销,更符合需求中的优先选择。
内容的提问来源于stack exchange,提问作者NtroP
相关产品推荐
相关产品推荐

