Friday, April 29, 2011

what's the best way to convert a ruby hash to an array

Hi All,

I have a ruby hash that looks like this

{ "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

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.

{
    "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"
}
From stackoverflow
  • 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]}
    
    Chris Drappier : how can I do this recursively? I forgot to mention that in the question :\
    Greg Campbell : Could you provide an example of ways the full structure might be put together? How deep does the recursion need to go, and how can we differentiate between hashes that need to be converted and hashes that don't (such as the inner {"foo" => "bar", "baz" => "quux"})?
    Chris Drappier : added some more rules at the bottom of the question, thx for helping :)
  • 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
    
    Chris Drappier : NameError: undefined local variable or method `key' for # from /storage/cait/development/app/helpers/application_helper.rb:6:in `array_from_hash'
    Chris Drappier : after fixing that error by changing k.to_i.to_s == key to k.to_i.to_s == k, it works pefectly! thanks!
    thenduks : You've got some interesting ideas wrt indenting here :)
    Vincent Robert : oups, fixing code :D

0 comments:

Post a Comment