ActiveSupport undercore behaviour

I tried to use ActiveSupport 'underscore' method to convert some
non-conventional to RoR data to be able to update later some attributes
as follows:

TECHS = {"platform"=>[
                {"AssignedName"=>"P1", "ExportPrice"=>"USD", "Id"=>"1",
"Delivery"=>"Y",
                    "RegisteredMembers"=>[
                      {"Name"=>"Admin", "Id"=>"3",
                          "AvailablePosts"=>[
                              {"Seat"=>"2", "Colour"=>18, },
                              {"Seat"=>"3", "Colour"=>19, },
                              {"Seat"=>"4", "Colour"=>181, },
                              {"Seat"=>"5", "Colour"=>183, }
                          ]
                      }
                     ]
                }
              ]
            }

TECHS.each_value do |value|
  arr = value.map {|k,v| [ActiveSupport::Inflector.underscore(k.to_s),
v]}
  puts "ARR: #{arr.inspect}"
end

The result is strange even if the values look underscored:

ARR: [["{\"assigned_name\"=>\"p1\", \"export_price\"=>\"usd\",
\"id\"=>\"1\", \"delivery\"=>\"y\",
\"registered_members\"=>[{\"name\"=>\"admin\", \"id\"=>\"3\",
\"available_posts\"=>[{\"seat\"=>\"2\", \"colour\"=>18},
{\"seat\"=>\"3\", \"colour\"=>19}, {\"seat\"=>\"4\", \"colour\"=>181},
{\"seat\"=>\"5\", \"colour\"=>183}]}]}", nil]]

Why are there some extra quotes and nil in the very end ? Thkx

···

--
Posted via http://www.ruby-forum.com/.

Not 100% sure what you're seeing as extra quotes, but the nil is what you get from declaring two passed in values from the map method -- it only passes in the current value of the array that your original TECH['platform'] is pointing at, so k is current value, v is always nil.

2.0.0-p247 :008 > [1, 2, 3, 4].map{|k,v| [k,v]}
=> [[1, nil], [2, nil], [3, nil], [4, nil]]

Even if your array elements are hashes, there's still only one value passed in from map:
2.0.0-p247 :009 > [{a: 1, b: 2, c: 3}, {aa: 11, bb: 22, cc: 33}].map{|k,v| [k,v]}
=> [[{:a=>1, :b=>2, :c=>3}, nil], [{:aa=>11, :bb=>22, :cc=>33}, nil]]

···

On Jul 20, 2013, at 2:51 PM, Serguei Cambour <lists@ruby-forum.com> wrote:

I tried to use ActiveSupport 'underscore' method to convert some
non-conventional to RoR data to be able to update later some attributes
as follows:

TECHS = {"platform"=>[
               {"AssignedName"=>"P1", "ExportPrice"=>"USD", "Id"=>"1",
"Delivery"=>"Y",
                   "RegisteredMembers"=>[
                     {"Name"=>"Admin", "Id"=>"3",
                         "AvailablePosts"=>[
                             {"Seat"=>"2", "Colour"=>18, },
                             {"Seat"=>"3", "Colour"=>19, },
                             {"Seat"=>"4", "Colour"=>181, },
                             {"Seat"=>"5", "Colour"=>183, }
                         ]
                     }
                    ]
               }
             ]
           }

TECHS.each_value do |value|
arr = value.map {|k,v| [ActiveSupport::Inflector.underscore(k.to_s),
v]}
puts "ARR: #{arr.inspect}"
end

The result is strange even if the values look underscored:

ARR: [["{\"assigned_name\"=>\"p1\", \"export_price\"=>\"usd\",
\"id\"=>\"1\", \"delivery\"=>\"y\",
\"registered_members\"=>[{\"name\"=>\"admin\", \"id\"=>\"3\",
\"available_posts\"=>[{\"seat\"=>\"2\", \"colour\"=>18},
{\"seat\"=>\"3\", \"colour\"=>19}, {\"seat\"=>\"4\", \"colour\"=>181},
{\"seat\"=>\"5\", \"colour\"=>183}]}]}", nil]]

Why are there some extra quotes and nil in the very end ? Thkx

--
Posted via http://www.ruby-forum.com/\.

tamouse m. wrote in post #1116149:

                             {"Seat"=>"2", "Colour"=>18, },
TECHS.each_value do |value|
\"available_posts\"=>[{\"seat\"=>\"2\", \"colour\"=>18},
{\"seat\"=>\"3\", \"colour\"=>19}, {\"seat\"=>\"4\", \"colour\"=>181},
{\"seat\"=>\"5\", \"colour\"=>183}]}]}", nil]]

Why are there some extra quotes and nil in the very end ? Thkx

--
Posted via http://www.ruby-forum.com/\.

Not 100% sure what you're seeing as extra quotes, but the nil is what
you get from declaring two passed in values from the map method -- it
only passes in the current value of the array that your original
TECH['platform'] is pointing at, so k is current value, v is always nil.

2.0.0-p247 :008 > [1, 2, 3, 4].map{|k,v| [k,v]}
=> [[1, nil], [2, nil], [3, nil], [4, nil]]

Even if your array elements are hashes, there's still only one value
passed in from map:
2.0.0-p247 :009 > [{a: 1, b: 2, c: 3}, {aa: 11, bb: 22, cc:
33}].map{|k,v| [k,v]}
=> [[{:a=>1, :b=>2, :c=>3}, nil], [{:aa=>11, :bb=>22, :cc=>33}, nil]]

Thanks a lot for the reply. My hash has one value - an array of nested
hashes, that's why I looped on that array and tried to format the keys
with 'underscore' method. I hoped to get them formatted with
'underscore' as follows:

ARR: [[{"assigned_name"=>"p1", "export_price"=>"usd"... etc.

but got all of them prefixed with a backslash, just before every quote:

"{\"assigned_name\"=>\"p1\"

I'd like to pass that hash to update an ActiveRecord value with
update_attributes and it will fails with the above hashes. Any idea?
Thank you.

···

On Jul 20, 2013, at 2:51 PM, Serguei Cambour <lists@ruby-forum.com> > wrote:

--
Posted via http://www.ruby-forum.com/\.

I am on the phone, but my guess is that the quotes and backslashes are
generated by #inspect.

Regarding the output, the input structure is nested. Your iterators do no
match the nesting. Try going one level at a time and compare.

···

On Sunday, July 21, 2013, Serguei Cambour wrote:

tamouse m. wrote in post #1116149:
> On Jul 20, 2013, at 2:51 PM, Serguei Cambour <lists@ruby-forum.com<javascript:;> > > > > wrote:
>
>> {"Seat"=>"2", "Colour"=>18, },
>> TECHS.each_value do |value|
>> \"available_posts\"=>[{\"seat\"=>\"2\", \"colour\"=>18},
>> {\"seat\"=>\"3\", \"colour\"=>19}, {\"seat\"=>\"4\", \"colour\"=>181},
>> {\"seat\"=>\"5\", \"colour\"=>183}]}]}", nil]]
>>
>> Why are there some extra quotes and nil in the very end ? Thkx
>>
>> --
>> Posted via http://www.ruby-forum.com/\.
>>
>
> Not 100% sure what you're seeing as extra quotes, but the nil is what
> you get from declaring two passed in values from the map method -- it
> only passes in the current value of the array that your original
> TECH['platform'] is pointing at, so k is current value, v is always nil.
>
> 2.0.0-p247 :008 > [1, 2, 3, 4].map{|k,v| [k,v]}
> => [[1, nil], [2, nil], [3, nil], [4, nil]]
>
> Even if your array elements are hashes, there's still only one value
> passed in from map:
> 2.0.0-p247 :009 > [{a: 1, b: 2, c: 3}, {aa: 11, bb: 22, cc:
> 33}].map{|k,v| [k,v]}
> => [[{:a=>1, :b=>2, :c=>3}, nil], [{:aa=>11, :bb=>22, :cc=>33}, nil]]

Thanks a lot for the reply. My hash has one value - an array of nested
hashes, that's why I looped on that array and tried to format the keys
with 'underscore' method. I hoped to get them formatted with
'underscore' as follows:

ARR: [[{"assigned_name"=>"p1", "export_price"=>"usd"... etc.

but got all of them prefixed with a backslash, just before every quote:

"{\"assigned_name\"=>\"p1\"

I'd like to pass that hash to update an ActiveRecord value with
update_attributes and it will fails with the above hashes. Any idea?
Thank you.

--
Posted via http://www.ruby-forum.com/\.

Oh, I see -- the "extra" quotes and backslashes are the output of inspect. Note the difference in irb:

2.0.0-p247 :011 > TECHS.inspect
=> "{\"platform\"=>[{\"AssignedName\"=>\"P1\", \"ExportPrice\"=>\"USD\", \"Id\"=>\"1\", \"Delivery\"=>\"Y\", \"RegisteredMembers\"=>[{\"Name\"=>\"Admin\", \"Id\"=>\"3\", \"AvailablePosts\"=>[{\"Seat\"=>\"2\", \"Colour\"=>18}, {\"Seat\"=>\"3\", \"Colour\"=>19}, {\"Seat\"=>\"4\", \"Colour\"=>181}, {\"Seat\"=>\"5\", \"Colour\"=>183}]}]}]}"
2.0.0-p247 :012 > TECHS
=> {"platform"=>[{"AssignedName"=>"P1", "ExportPrice"=>"USD", "Id"=>"1", "Delivery"=>"Y", "RegisteredMembers"=>[{"Name"=>"Admin", "Id"=>"3", "AvailablePosts"=>[{"Seat"=>"2", "Colour"=>18}, {"Seat"=>"3", "Colour"=>19}, {"Seat"=>"4", "Colour"=>181}, {"Seat"=>"5", "Colour"=>183}]}]}]}
2.0.0-p247 :013 >

The second one is the actual value of TECHS.

Let's take another stab at this.

TECHS is a hash with only one key: 'platform':

2.0.0-p247 :019 > TECHS.class
=> Hash
2.0.0-p247 :020 > TECHS.keys
=> ["platform"]

which contains an array of 1 item:

2.0.0-p247 :021 > TECHS['platform']
=> [{"AssignedName"=>"P1", "ExportPrice"=>"USD", "Id"=>"1", "Delivery"=>"Y", "RegisteredMembers"=>[{"Name"=>"Admin", "Id"=>"3", "AvailablePosts"=>[{"Seat"=>"2", "Colour"=>18}, {"Seat"=>"3", "Colour"=>19}, {"Seat"=>"4", "Colour"=>181}, {"Seat"=>"5", "Colour"=>183}]}]}]
2.0.0-p247 :022 > TECHS['platform'].class
=> Array
2.0.0-p247 :023 > TECHS['platform'].count
=> 1

That one item is the thing you actually want to convert to pass to you model, yes? So without all the hoopla, try:

2.0.0-p247 :027 > params = Hash[TECHS['platform'].first.map{|k,v| [ActiveSupport::Inflector.underscore(k.to_s),v]}]
=> {"assigned_name"=>"P1", "export_price"=>"USD", "id"=>"1", "delivery"=>"Y", "registered_members"=>[{"Name"=>"Admin", "Id"=>"3", "AvailablePosts"=>[{"Seat"=>"2", "Colour"=>18}, {"Seat"=>"3", "Colour"=>19}, {"Seat"=>"4", "Colour"=>181}, {"Seat"=>"5", "Colour"=>183}]}]}

I'm not sure what you want to be doing with the registered_members value, and the AvailablePosts key underneath that. Are these nested parameters for going in other tables with relationships? If so, you'll need to run those through the same method above. It would probably be very worthwhile to make that an actual method, that could be called recursively if v is something you want to work on (Hash, Array, Object).

···

On Jul 21, 2013, at 12:21 AM, Serguei Cambour <lists@ruby-forum.com> wrote:

tamouse m. wrote in post #1116149:

On Jul 20, 2013, at 2:51 PM, Serguei Cambour <lists@ruby-forum.com> >> wrote:

                            {"Seat"=>"2", "Colour"=>18, },
TECHS.each_value do |value|
\"available_posts\"=>[{\"seat\"=>\"2\", \"colour\"=>18},
{\"seat\"=>\"3\", \"colour\"=>19}, {\"seat\"=>\"4\", \"colour\"=>181},
{\"seat\"=>\"5\", \"colour\"=>183}]}]}", nil]]

Why are there some extra quotes and nil in the very end ? Thkx

--
Posted via http://www.ruby-forum.com/\.

Not 100% sure what you're seeing as extra quotes, but the nil is what
you get from declaring two passed in values from the map method -- it
only passes in the current value of the array that your original
TECH['platform'] is pointing at, so k is current value, v is always nil.

2.0.0-p247 :008 > [1, 2, 3, 4].map{|k,v| [k,v]}
=> [[1, nil], [2, nil], [3, nil], [4, nil]]

Even if your array elements are hashes, there's still only one value
passed in from map:
2.0.0-p247 :009 > [{a: 1, b: 2, c: 3}, {aa: 11, bb: 22, cc:
33}].map{|k,v| [k,v]}
=> [[{:a=>1, :b=>2, :c=>3}, nil], [{:aa=>11, :bb=>22, :cc=>33}, nil]]

Thanks a lot for the reply. My hash has one value - an array of nested
hashes, that's why I looped on that array and tried to format the keys
with 'underscore' method. I hoped to get them formatted with
'underscore' as follows:

ARR: [[{"assigned_name"=>"p1", "export_price"=>"usd"... etc.

but got all of them prefixed with a backslash, just before every quote:

"{\"assigned_name\"=>\"p1\"

I'd like to pass that hash to update an ActiveRecord value with
update_attributes and it will fails with the above hashes. Any idea?
Thank you.

I changed as follows and it works for the first nest level for the
moment:

def format_hash_key(hash)
  Hash[hash.map {|k, v| [ActiveSupport::Inflector.underscore(k.to_s), v]
}]
end

arr = []
TECHS.each_value do |array_values|
  array_values.each do |hash|
    arr << format_hash_key(hash)
  end
end
puts arr

Output:

{"assigned_name"=>"P1", "export_price"=>"USD", "id"=>"1",
"delivery"=>"Y", "registered_members"=>[{"Name"=>"Admin", "Id"=>"3",
"AvailablePosts"=>[{"Seat"=>"2", "Colour"=>18}, {"Seat"=>"3",
"Colour"=>19}, {"Seat"=>"4", "Colour"=>181}, {"Seat"=>"5",
"Colour"=>183}]}]}

Is there any elegant way to call the same method for other nested levels
(values) ?

Thank you.

···

--
Posted via http://www.ruby-forum.com/.

I'd probably do it like this. (I like using the word "json" in the name b/c
it makes it informs the caller of what the data structure can contain)

require 'active_support/inflector'

TECHS = {"platform"=>
  [{"AssignedName"=>"P1", "ExportPrice"=>"USD", "Id"=>"1",
    "Delivery"=>"Y",
    "RegisteredMembers"=>[
      {"Name"=>"Admin", "Id"=>"3",
        "AvailablePosts"=>[
          {"Seat"=>"2", "Colour"=>18, },
          {"Seat"=>"3", "Colour"=>19, },
          {"Seat"=>"4", "Colour"=>181, },
          {"Seat"=>"5", "Colour"=>183, }
        ]
      }
    ]
  }]
}

def normalize_json(deserialized_json)
  case deserialized_json
  when Array
    deserialized_json.map { |v| normalize_json v }
  when Hash
    deserialized_json.each_with_object Hash.new do |(k, v), new_hash|
      new_hash[ActiveSupport::Inflector.underscore(k)] = normalize_json(v)
    end
  else
    deserialized_json
  end
end

require 'pp'
pp normalize_json TECHS

# >> {"platform"=>
# >> [{"assigned_name"=>"P1",
# >> "export_price"=>"USD",
# >> "id"=>"1",
# >> "delivery"=>"Y",
# >> "registered_members"=>
# >> [{"name"=>"Admin",
# >> "id"=>"3",
# >> "available_posts"=>
# >> [{"seat"=>"2", "colour"=>18},
# >> {"seat"=>"3", "colour"=>19},
# >> {"seat"=>"4", "colour"=>181},
# >> {"seat"=>"5", "colour"=>183}]}]}]}

···

On Sun, Jul 21, 2013 at 7:03 AM, Serguei Cambour <lists@ruby-forum.com>wrote:

I changed as follows and it works for the first nest level for the
moment:

def format_hash_key(hash)
  Hash[hash.map {|k, v| [ActiveSupport::Inflector.underscore(k.to_s), v]
}]
end

arr =
TECHS.each_value do |array_values|
  array_values.each do |hash|
    arr << format_hash_key(hash)
  end
end
puts arr

Output:

{"assigned_name"=>"P1", "export_price"=>"USD", "id"=>"1",
"delivery"=>"Y", "registered_members"=>[{"Name"=>"Admin", "Id"=>"3",
"AvailablePosts"=>[{"Seat"=>"2", "Colour"=>18}, {"Seat"=>"3",
"Colour"=>19}, {"Seat"=>"4", "Colour"=>181}, {"Seat"=>"5",
"Colour"=>183}]}]}

Is there any elegant way to call the same method for other nested levels
(values) ?

Thank you.

--
Posted via http://www.ruby-forum.com/\.

@tamouse m. :Thanks a lot for such a detailed explanation. Yes the
problew was to underscore all the has keys to be able to call
'update_attributes' method to update an AR model.

@Josh Cheek: Your stuff seems to be what I exactly need. Thank you so
much. Sorry for the stupid question, but what is 'pp' that you required
to be able to call your method?

Regards

···

--
Posted via http://www.ruby-forum.com/.

It stands for "pretty print", it's what prints it in the format at the
bottom. If I'd just used `p`, the whole data structure would have appeared
on the same line.

-Josh

···

On Mon, Jul 22, 2013 at 5:29 AM, Serguei Cambour <lists@ruby-forum.com>wrote:

@Josh Cheek: Your stuff seems to be what I exactly need. Thank you so
much. Sorry for the stupid question, but what is 'pp' that you required
to be able to call your method?

Josh Cheek wrote in post #1116234:

···

On Mon, Jul 22, 2013 at 5:29 AM, Serguei Cambour > <lists@ruby-forum.com>wrote:

@Josh Cheek: Your stuff seems to be what I exactly need. Thank you so
much. Sorry for the stupid question, but what is 'pp' that you required
to be able to call your method?

It stands for "pretty print", it's what prints it in the format at the
bottom. If I'd just used `p`, the whole data structure would have
appeared
on the same line.

-Josh

REAAAAALY Cool, Josh ! Thanks a lot, it does the stuff !
Thank you both for your help.

Cheers

--
Posted via http://www.ruby-forum.com/\.