从 Ruby 中的 Mongo 集合递归创建分层哈希

Recursively create hierarchical hash from Mongo collection in Ruby

我正在尝试创建一个如下所示的散列:

{"difficulty"=>{"easy"=>{}, "normal"=>{}, "hard"=>{}}, "terrain"=>{"snow"=>{"sleet"=>{}, "powder"=>{}}, "jungle"=>{}, "city"=>{}}}

来自 MongoDB 集合,它是可枚举的哈希列表,如下所示:

{
"_id" : "globalSettings",
"groups" : [
    "difficulty",
    "terrain"
],
"parent" : null,
"settings" : {
    "maxEnemyCount" : 10,
    "maxDamageInflicted" : 45,
    "enemyHealthPoints" : 40,
    "maxEnemySpeed" : 25,
    "maxPlayerSpeed" : 32,
    "lightShader" : "diffuse",
    "fogDepth" : 12,
    "terrainModifier" : 9
}
},
{
"_id" : "difficulty",
"groups" : [
    "easy",
    "normal",
    "hard"
],
"parent" : "globalSettings",
"settings" : {

}
}
{
"_id" : "terrain",
"groups" : [
    "snow",
    "jungle",
    "city"
],
"parent" : "globalSettings",
"settings" : {
}
}
{
"_id" : "snow",
"groups" : [
    "sleet",
    "powder"
],
"parent" : "terrain",
"settings" : {
    "fogDepth" : 4
}
}
{
"_id" : "jungle",
"groups" : [ ],
"parent" : "terrain",
"settings" : {
    "terrainModifier" : 6
}
}
{
"_id" : "city",
"groups" : [ ],
"parent" : "terrain",
"settings" : {
    "lightShader" : "bumpedDiffuse"
}
}
{
"_id" : "easy",
"groups" : [ ],
"parent" : "difficulty",
"settings" : {
    "maxEnemyCount" : 5
}
}
{
"_id" : "normal",
"groups" : [ ],
"parent" : "difficulty",
"settings" : {

}
}
{
"_id" : "hard",
"groups" : [ ],
"parent" : "difficulty",
"settings" : {
    "maxEnemyCount" : 20
}
}
{
"_id" : "sleet",
"groups" : [ ],
"parent" : "snow",
"settings" : {
    "fogDepth" : 2
}
}
{
"_id" : "powder",
"groups" : [ ],
"parent" : "snow",
"settings" : {
    "terrainModifier" : 2
}
}

每次尝试编写函数时,我都会在设置组的父项时卡住。我如何递归并跟踪层次结构的路径?

我最接近的是这个:

def dbCurse(nodes, parent = nil)
  withParent, withoutParent = nodes.partition { |n| n['parent'] == parent }
  withParent.map do |node|
    newNode={}
    newNode[node["_id"]]={}
    newNode[node["_id"]].merge(node["_id"] => dbCurse(withoutParent, node['_id']))
  end
end

这让我疯狂地混合了数组和散列:

{"globalSettings"=>[{"difficulty"=>[{"easy"=>[]}, {"normal"=>[]}, {"hard"=>[]}]}, {"terrain"=>[{"snow"=>[{"sleet"=>[]}, {"powder"=>[]}]}, {"jungle"=>[]}, {"city"=>[]}]}]}

我认为数组从#map 中混入其中,但我不确定如何摆脱它们以获得我在问题顶部显示的哈希的干净哈希。

谢谢, 大卫

因此,根据您的示例输入,我将做出一个核心假设:

输入列表中散列对象的顺序有些明确。即,如果 globalSettings 散列引用组 difficulty,则列表中具有 _id == 'difficulty'parent == 'globalSettings' 的下一个对象是正确的匹配项。

如果这是真的,那么您可以编写一个函数来接受您正在寻找的内容的描述(即带有 _id == 'difficulty'parent == 'globalSettings' 的对象)以及对您希望将数据存储在何处,然后可以使用更深层次的引用进行递归。

def doit(obj_list, work = {})
  work.each do |key, data|
    # fetch the node
    node_i = obj_list.index { |n| n['_id'] == key && n['parent'] == data[:parent] } or next
    node = obj_list.delete_at(node_i)

    # for each group of this node, create a new hash
    # under this node's output pointer and queue it for parsing
    new_work = {}
    node['groups'].each do |group|
      data[:output][group] = {}
      new_work[group] = { parent: key, output: data[:output][group] }
    end

    # get the group data for this node
    doit(obj_list, new_work)
  end
end

input_data = JSON.parse(IO.read './input.json')
output_data = {}
doit( input_data, 'globalSettings' => { parent: nil, output: output_data } )

这里的技巧是我将递归调用传递给 doit 我要从列表中查找的下一个对象的名称(使用当前对象的组列表)并将每个对象配对那些想要的名称及其父名称以及对我希望函数放置找到的数据的位置的引用。对 doit 的每次递归调用都会对原始输出哈希使用越来越深的引用。