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

On Jul 20, 2013, at 2:51 PM, Serguei C. [email protected]
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]]

tamouse m. wrote in post #1116149:

On Jul 20, 2013, at 2:51 PM, Serguei C. [email protected]
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 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.

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.

@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 C.: 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

On Jul 21, 2013, at 12:21 AM, Serguei C. [email protected]
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).

On Mon, Jul 22, 2013 at 5:29 AM, Serguei C.
[email protected]wrote:

@Josh C.: 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

On Sun, Jul 21, 2013 at 7:03 AM, Serguei C.
[email protected]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}]}]}]}

Josh C. wrote in post #1116234:

On Mon, Jul 22, 2013 at 5:29 AM, Serguei C.
[email protected]wrote:

@Josh C.: 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