Forum: Ruby ActiveSupport undercore behaviour

F7141e066d691d7f18765c1ea6389b18?d=identicon&s=25 Serguei Cambour (javix)
on 2013-07-20 23:51
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
Aa082c8b00a50928e5860dcd70bf2368?d=identicon&s=25 tamouse m. (tamouse_m)
on 2013-07-21 08:46
(Received via mailing list)
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]]
F7141e066d691d7f18765c1ea6389b18?d=identicon&s=25 Serguei Cambour (javix)
on 2013-07-21 09:21
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.
7223c62b7310e164eb79c740188abbda?d=identicon&s=25 Xavier Noria (fxn)
on 2013-07-21 11:09
(Received via mailing list)
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.
F7141e066d691d7f18765c1ea6389b18?d=identicon&s=25 Serguei Cambour (javix)
on 2013-07-21 14:03
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.
Aa082c8b00a50928e5860dcd70bf2368?d=identicon&s=25 tamouse m. (tamouse_m)
on 2013-07-21 18:53
(Received via mailing list)
On Jul 21, 2013, at 12:21 AM, Serguei Cambour <lists@ruby-forum.com>
wrote:

>>> Why are there some extra quotes and nil in the very end ? Thkx
>> 2.0.0-p247 :008 > [1, 2, 3, 4].map{|k,v| [k,v]}
> with 'underscore' method. I hoped to get them formatted with
> Thank you.
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).
B31e7abd14f1ceb4c4957da08933c630?d=identicon&s=25 Josh Cheek (josh-cheek)
on 2013-07-21 20:31
(Received via mailing list)
On Sun, Jul 21, 2013 at 7:03 AM, Serguei Cambour
<lists@ruby-forum.com>wrote:

>   array_values.each do |hash|
> "AvailablePosts"=>[{"Seat"=>"2", "Colour"=>18}, {"Seat"=>"3",
>
>
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}]}]}]}
F7141e066d691d7f18765c1ea6389b18?d=identicon&s=25 Serguei Cambour (javix)
on 2013-07-22 12:29
@tamouse m. :Thanks a lot for such a detailed explanation. Yes the
problew was to underscore all the hash 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? Is it pretty print by Ruby or smth else
?

Regards
B31e7abd14f1ceb4c4957da08933c630?d=identicon&s=25 Josh Cheek (josh-cheek)
on 2013-07-22 15:39
(Received via mailing list)
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
F7141e066d691d7f18765c1ea6389b18?d=identicon&s=25 Serguei Cambour (javix)
on 2013-07-22 16:39
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
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.