You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 21:35:02