如何将Ruby哈希表转换为数组的最佳方法?

5
我有一个像这样的Ruby哈希表:
{ "stuff_attributes" => {
     "1" => {"foo" => "bar", "baz" => "quux"}, 
     "2" => {"foo" => "bar", "baz" => "quux"} 
   }
}

我希望你能够将其转换为以下格式的哈希值
{ "stuff_attributes" => [
    { "foo" => "bar", "baz" => "quux"},
    { "foo" => "bar", "baz" => "quux"}
  ]
}

我还需要保留键的数字顺序,并且键的数量是可变的。上面是超级简化的,但我在底部包含了一个真实的例子。最好的方法是什么?
附言:
它还需要递归
就递归而言,我们可以假设以下内容:
1)需要操作的键将匹配/_attributes$/ 2)哈希将有许多其他不匹配/_attributes$/的键 3)哈希内部的键始终是一个数字 4)_attributes哈希可以在任何其他键下的哈希的任何级别中
此哈希实际上是控制器中创建操作的params哈希。这是需要使用此例程解析的真实示例。
{
    "commit"=>"Save", 
    "tdsheet"=>{
    "team_id"=>"43", 
    "title"=>"", 
    "performing_org_id"=>"10", 
    "tdsinitneed_attributes"=>{ 
        "0"=>{
            "title"=>"", 
            "need_date"=>"", 
            "description"=>"", 
            "expected_providing_organization_id"=>"41"
            }, 
        "1"=>{
            "title"=>"", 
            "need_date"=>"", 
            "description"=>"", 
            "expected_providing_organization_id"=>"41"
            }
        }, 
        "level_two_studycollection_id"=>"27", 
        "plan_attributes"=>{
            "0"=>{
                "start_date"=>"", "end_date"=>""
            }
        }, 
        "dataitem_attributes"=>{
            "0"=>{
                "title"=>"", 
                "description"=>"", 
                "plan_attributes"=>{
                    "0"=>{
                        "start_date"=>"", 
                        "end_date"=>""
                        }
                    }
                }, 
            "1"=>{
                "title"=>"", 
                "description"=>"", 
                "plan_attributes"=>{
                    "0"=>{
                        "start_date"=>"", 
                        "end_date"=>""
                        }
                    }
                }
            }
        }, 
    "action"=>"create", 
    "studycollection_level"=>"", 
    "controller"=>"tdsheets"
}
3个回答

8
请注意,在转换之前检查所有键是否为数字可能会很长时间...
def array_from_hash(h)
  return h unless h.is_a? Hash

  all_numbers = h.keys.all? { |k| k.to_i.to_s == k }
  if all_numbers
    h.keys.sort_by{ |k| k.to_i }.map{ |i| array_from_hash(h[i]) }
  else
    h.each do |k, v|
      h[k] = array_from_hash(v)
    end
  end
end

NameError: 未定义局部变量或方法 key' for #<Object:0x841c99c> 来自 /storage/cait/development/app/helpers/application_helper.rb:6:in array_from_hash' - Chris Drappier
在将 k.to_i.to_s == key 更改为 k.to_i.to_s == k 之后,它完美地工作了!谢谢! - Chris Drappier
你在缩进方面有一些有趣的想法 :) - rfunduk

4

如果我们能假设所有的键都是可以转换为整数的字符串,那么以下代码应该能够正常工作:

# "hash" here refers to the main hash in your example, since you didn't name it
stuff_hash = hash["stuff"]
hash["stuff"] = stuff_hash.keys.sort_by {|key| key.to_i}.map {|key| stuff_hash[key]}

怎样可以用递归的方式来实现这个?我在问题中忘记提到了。 - Chris Drappier
你能提供一些完整结构如何组合的示例吗?递归需要多深,我们如何区分需要转换的哈希和不需要转换的哈希(例如内部的 {"foo" => "bar", "baz" => "quux"})? - Greg Campbell
在问题底部添加了一些更多的规则,感谢您的帮助 :) - Chris Drappier

0
有点冒昧,我要发布一个与Vincent Robert非常相似的代码示例。
这个示例是对Hash类进行补丁,添加了一个.to_array方法。
 class Hash
   def to_array(h = self)
     return h unless h.is_a? Hash
     if h.keys.all? { |k| k.to_i.to_s == k } # all keys are numbers so make an array.
       h.keys.sort_by{ |k| k.to_i }.map{ |i| self.to_array(h[i]) }
     else
       h.each do |k, v|
         h[k] = self.to_array(v)
       end
     end
   end
 end

这使得使用稍微更加方便。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接