什么是将ruby哈希转换为数组的最佳方法

时间:2023-01-24 21:43:10

I have a ruby hash that looks like this

我有一个看起来像这样的ruby哈希

{ "stuff_attributes" => {
     "1" => {"foo" => "bar", "baz" => "quux"}, 
     "2" => {"foo" => "bar", "baz" => "quux"} 
   }
}

and I want to turn it into a hash that looks like this

我想把它变成一个看起来像这样的哈希

{ "stuff_attributes" => [
    { "foo" => "bar", "baz" => "quux"},
    { "foo" => "bar", "baz" => "quux"}
  ]
}

I also need to preserve the numerical order of the keys, and there is a variable number of keys. The above is super-simplified, but I've included a real example at the bottom. What's the best way to do this?

我还需要保留键的数字顺序,并且键数可变。以上是超简化的,但我在底部包含了一个真实的例子。最好的方法是什么?

P.S

It also needs to be recursive

它还需要递归

As far as the recursion goes, here's what we can assume:

就递归而言,这是我们可以假设的:

1) the key that needs to be manipulated will match /_attributes$/ 2) the hash will have many other keys that do not match /_attributes$/ 3) the keys inside the hash will always be a number 4) an _attributes hash can be at any level of the hash under any other key

1)需要操作的键将匹配/ _attributes $ / 2)哈希将有许多其他键不匹配/ _attributes $ / 3)哈希中的键将始终是数字4)_attributes哈希可以在任何其他键下的任何哈希级别

this hash is actually the params hash from a create action in the controller. This is a real example of what will need to be parsed with this routine.

这个哈希实际上是来自控制器中的创建动作的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 个解决方案

#1


8  

Note that this might be long to test if all keys are numbers before converting...

请注意,这可能很长,以测试转换前所有键是否为数字...

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

#2


4  

If we can assume that all the keys are in fact strings which convert cleanly to integers, the following ought to work:

如果我们可以假设所有的键实际上都是干净地转换为整数的字符串,那么以下应该可以工作:

# "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]}

#3


0  

To take a bit of a liberty, I'm posting a very similar code example to Vincent Robert's.

为了获得一点*,我发布了一个与Vincent Robert's非常相似的代码示例。

This one is patches the Hash class with a .to_array method.

这个是使用.to_array方法修补Hash类。

 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

It makes usage slightly more convenient.

它使用起来更方便。

#1


8  

Note that this might be long to test if all keys are numbers before converting...

请注意,这可能很长,以测试转换前所有键是否为数字...

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

#2


4  

If we can assume that all the keys are in fact strings which convert cleanly to integers, the following ought to work:

如果我们可以假设所有的键实际上都是干净地转换为整数的字符串,那么以下应该可以工作:

# "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]}

#3


0  

To take a bit of a liberty, I'm posting a very similar code example to Vincent Robert's.

为了获得一点*,我发布了一个与Vincent Robert's非常相似的代码示例。

This one is patches the Hash class with a .to_array method.

这个是使用.to_array方法修补Hash类。

 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

It makes usage slightly more convenient.

它使用起来更方便。