tags:

views:

1254

answers:

2

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"
}
+1  A: 

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]}
Greg Campbell
how can I do this recursively? I forgot to mention that in the question :\
Chris Drappier
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"})?
Greg Campbell
added some more rules at the bottom of the question, thx for helping :)
Chris Drappier
+3  A: 

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
Vincent Robert
NameError: undefined local variable or method `key' for #<Object:0x841c99c> 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!
Chris Drappier
You've got some interesting ideas wrt indenting here :)
thenduks
oups, fixing code :D
Vincent Robert