Iteration on rails hash - ruby-on-rails

I have hash like this , I get value as params in my controller
Parameters:
{
"utf8"=>"✓",
"authenticity_token"=>"WwNhv6pbXMvQWamzcKTm6gixDEUvvbrsZ7OrMR8RSAA=",
"form_holiday"=>{
"user_id"=>"3",
"1"=>{"year_before"=>"2014", "day_before"=>"22", "year_now"=>"2015", "day_now"=>"20"},
"2"=>{"year_before"=>"", "day_before"=>"", "year_now"=>"", "day_now"=>""},
"3"=>{"year_before"=>"2014", "day_before"=>"10", "year_now"=>"2015", "day_now"=>"30"}}
}
How can I iterate on this hash and get the values of "1,2,3" ?
I want to change it so it looks like the following:
{"utf8"=>"✓",
"authenticity_token"=>"WwNhv6pbXMvQWamzcKTm6gixDEUvvbrsZ7OrMR8RSAA=",
"form_holiday"=>{
"1"=>{"user_id"=>"1", "year_before"=>"2014", "day_before"=>"22", "year_now"=>"2015", "day_now"=>"20"},
"2"=>{"user_id"=>"2", "year_before"=>"", "day_before"=>"", "year_now"=>"", "day_now"=>""},
"3"=>{"user_id"=>"3", "year_before"=>"2014", "day_before"=>"10", "year_now"=>"2015", "day_now"=>"30"}}
}
and here is my current code:
year_before = ""
year_now = ""
holiday = ""
#users = User.find(:all)
if params[:form_holiday]
hash_params = params[:form_holiday]
hash_params.each do |key, value|
if value
value.each do |key2, value2|
if key2 == "user_id"
holiday = Holiday.where("user_id = #{value2}") rescue nil
end
if key2 == "year_before"
year_before += "#{value2},"
end
if key2 == "day_before"
year_before += "#{value2}"
end
if key2 == "year_now"
year_now += "#{value2},"
end
if key2 == "day_now"
year_now += "#{value2}"
end
end
holiday.year_before = year_before
holiday.year_now = year_now
holiday.save
end
end
end - it doesnt work :(
i solved my problem, this solution is good but when i get holiday i got a set of hash and i using year_before on set of hash, it was stupid mistake and i sorry for that and thanks for help

To get the values, you may try like this:
h = params[:form_holiday]
h.each do |key, value|
value.values.each do |v|
puts v # v are the values for 1,2,3 etc
end
end
And if you want to get both key and value, you may do something like this:
h = params[:form_holiday]
h.each do |key, value|
value.each do |k, v|
puts k # k is the key, which would be 1 or 2 or 3 etc.
puts v # v are the values for 1 or 2 or 3 etc respectively.
# any operation you perform on k,v
end
end

Related

Ruby hash path to each leaf

First of all I beg your pardon if this question already exists, I deeply searched for a solution here but I've been able to find it, nevertheless I feel it's a problem so common that is seems so strange to not find anything here...
My struggle is the following: given an hash, I need to return all the PATHS to each leaf as an array of strings; so, for example:
{:a=> 1} gives ['a']
{:a=>{:b=>3, :c=>4} returns an array with two results: ["a.b", "a.c"]
{:a=>[1, {:b=>2}]} will result in ["a.0", "a.1.b"]
and so on...
I have found only partial solutions to this and with dozens of codelines. like this
def pathify
self.keys.inject([]) do |acc, element|
return acc if element.blank?
if !(element.is_a?(Hash) || element.is_a?(Array))
if acc.last.is_a?(Array)
acc[acc.size-1] = acc.last.join('.')
else
acc << element.to_s
end
end
if element.is_a?(Hash)
element.keys.each do |key|
if acc.last.is_a?(Array)
acc.last << key.to_s
else
acc << [key.to_s]
end
element[key].pathify
end
end
if element.is_a?(Array)
acc << element.map(&:pathify)
end
acc
end
end
But it does not work in all cases and is extremely inefficient. Summarizing: is there any way to "pathify" an hash to return all the paths to each leaf in form of array of strings?
Thank you for the help!
Edited
Adding some specs
for {} it returns []
for {:a=>1} it returns ["a"]
for {:a=>1, :b=>1} it returns ["a", "b"]
for {:a=>{:b=>1}} it returns ["a.b"] (FAILED - 1) got: ["a"]
for {:a=>{:b=>1, :c=>2}} it returns ["a.b", "a.c"] (FAILED - 2) got: ["a"]
for {:a=>[1]} it returns ["a.0"] (FAILED - 3) got: ["a"]
for {:a=>[1, "b"]} it returns ["a.0", "a.1"] (FAILED - 4) got: ["a"]
def show(key, path)
if path.is_a? Array
path.map {|p| "#{key}.#{p}"}
else
path == "" ? key.to_s : "#{key}.#{path}"
end
end
def pathify(input)
if input.is_a? Hash
input.map do |k,v|
sub_path = pathify(v)
show(k, sub_path)
end.flatten
elsif input.is_a? Array
input.map.with_index do |v, i|
sub_path = pathify(v)
show(i, sub_path)
end.flatten
else
""
end
end
def leaf_paths(enum)
return unless [Hash, Array].include? enum.class
[].tap do |result|
if enum.is_a?(Hash)
enum.each { |k, v| result = attach_leaf_paths(k, v, result) }
elsif enum.is_a?(Array)
enum.each_with_index { |elem, index| result = attach_leaf_paths(index, elem, result) }
end
end
end
def attach_leaf_paths(key, value, result)
if (children = leaf_paths(value))
children.each { |child| result << "#{key}.#{child}" }
else
result << key.to_s
end
result
end
This is very similar to https://github.com/wteuber/yaml_normalizer/blob/b85dca7357df00757c471acb5dadb79a53dd27c1/lib/yaml_normalizer/ext/namespaced.rb
So I tweaked the code a bit to fit your needs:
module Leafs
def leafs(namespace = [], tree = {})
each do |key, value|
child_ns = namespace.dup << key
if value.instance_of?(Hash)
value.extend(Leafs).leafs child_ns, tree
elsif value.instance_of?(Array)
value.each.with_index.inject({}) {|h, (v,k)| h[k]=v; h}.extend(Leafs).leafs child_ns, tree
else
tree[child_ns.join('.')] = value
end
end
tree.keys.to_a
end
end
Here is how to use it:
h = {a: [1, "b"], c: {d:1}}
h.extend(Leafs)
h.leafs
# => ["a.0", "a.1", "c.d"]
I hope you find this helpful.
def pathify(what)
paths = []
if what.is_a?(Array)
what.each_with_index do | element, index |
paths+= pathify(element).map{|e| index.to_s + '.' + e.to_s}
end
elsif what.is_a?(Hash)
what.each do |k,v|
paths+= pathify(v).map{|e| k.to_s + '.' + e.to_s}
end
else
paths.append('')
end
paths.map{|e| e.delete_suffix('.')}
end

Ruby how to modify parameters

so i have this code that and my aim was to convert any empty string to null
def convert_empty_strings_to_null
if request.patch? || request.post?
convert_empty_strings_to_null_rec(request.params)
end
end
def convert_empty_strings_to_null_rec(param)
param = nil if param.empty? if param.is_a?(String)
param.all?{|v| convert_empty_strings_to_null_rec v} if param.is_a?(Array)
param.all?{|k,v| convert_empty_strings_to_null_rec v} if param.is_a?(Hash)
end
But i'm new to ruby on rails and i found it that it sends params by value and not by reference, so no change in params is made, how do i fix this ?
I assume that by "empty" you mean zero-with strings, meaning that strings consisting only of whitespace should be left intact. (Otherwise blank? and strip would be your friends.)
def convert_empty_strings_to_nil
if request.patch? || request.post?
request.params.each do |key, value|
request.params[key] = convert_empty_strings_to_nil_rec(value)
end
end
end
def convert_empty_strings_to_nil_rec(param)
case param
when String
param.empty? ? nil : param
when Array
param.map{ |v| convert_empty_strings_to_nil_rec(v) }
when Hash
param.map{ |k,v| [k, convert_empty_strings_to_nil_rec(v)] }.to_h
else
param
end
end
First of all, this is how your convert_empty_strings_to_null_rec method should be, for keeping the changes persistent:
def convert_empty_strings_to_null_rec(param)
if param == ""
updated_param == nil
elsif param.is_a?(Array)
updated_param == param.map{|x| nil if x.empty? }
elsif param.is_a?(Hash)
updated_param = {}
param.each do |k, v|
if v.empty?
updated_param[k] = nil
else
updated_param[k] = v
end
end
end
return updated_param
end
Further, I am assuming from your question that convert_empty_strings_to_null is a action method. It should be updated to catch what convert_empty_strings_to_null_rec method is returning.
def convert_empty_strings_to_null
if request.patch? || request.post?
updated_params = convert_empty_strings_to_null_rec(request.params)
end
# you can use the updated_params here on in this action method
end
Hope it helps : )

rails 4 data iteration from activerecord search

I am trying to iterate over a hash returned from an active record search.
the data comes back {[k, v] => v, [k,v] => v, etc.} and I need to place that data in 3 separate columns of a table.
what I have so far in a helper is
data = {}
connector = 0
us_cords = 0
eu_cords = 0
molex_connector = 0
chart_data_two.each do |key, value|
data[key[0]] ||= Hash.new
data[key[0]][key[1]] = value
end
return data
This gives me the k out of the k,v pair above and then a hash with "v" => v from above.
so I am having a hard time wrapping my head around iterating over the data hash and putting it into the view table in each column.
The view has #table_data_two = chart_qty_monthly_data(#chart_data_two) and then #table_data_two.each do |k,v| for generating each row/column.
Eventually it might be nice to do only one merged cell for month with 4 cells for model and then the quantities
Here's one way to build that:
data = Hash.new { |h, k| h[k] = {} }
chart_data_two.each do |key, value|
model_quantity = { key[0] => value }
data[key[1]].merge!(model_quantity)
end
If you want it to be in order by month, you can change the keys to integers and sort it:
data = Hash.new { |h, k| h[k] = {} }
chart_data_two.each do |key, value|
model_quantity = { key[0] => value }
data[key[1].to_i].merge!(model_quantity)
end
sorted_data = data.sort.to_h
It's not perfect, so I'll look it over again and see what could be improved, but it will at least get you started, assuming I understood your question correctly.
ended up doing it a bit different:
data = {}
#initialize hash keys in the order you want them to end up in
(1..12).each do |month|
data[month] = {"Connector" => 0, "US Cords" => 0, "EU Cords" => 0}
end
chart_data_two.each do |key, value|
#key[1] is month. we're referencing the keys we initialized above
#key[2] is model. we're adding a new key to the nested hash we initialized above
#logger.info "#{key[0]}, #{key[1]}, #{value}"
data[key[1].to_i][key[0]] = value
#logger.info "%%%%%%%% inside iterator #{data}"
end
#logger.info "$$$$$ passing #{data} into chart"
return data
end
I got help with figuring that out, but I wanted to post it here.

Clear all values in nested ruby hash

How can I remove all values from ruby has. I don't want to remove keys just values.
For example:
here is my hash: {'a'=>{'b'=>'c'},'d'=>'e','f'=>{'g'=>''}}
I want this: {'a'=>{'b'=>nil},'d'=>nil,'f'=>{'g'=>nil}}
I don't want to delete the nested hashes. The nesting level varies from one to six levels
thanx
You can write custom delete_values! method, like this:
class Hash
def delete_values!
each_key do |key|
self[key].is_a?(Hash) ? self[key].delete_values! : self[key] = nil
end
end
end
{'a'=>{'b'=>'c'},'d'=>'e','f'=>{'g'=>''}}.delete_values!
# => {"a"=>{"b"=>nil}, "d"=>nil, "f"=>{"g"=>nil}}
h = {'a'=>{'b'=>'c'},'d'=>'e','f'=>{'g'=>''}}
def clean_hash h
h.each do |key, value|
if value.instance_of? Hash
clean_hash value
else
h[key] = nil
end
end
end
clean_hash h
#{"a"=>{"b"=>nil}, "d"=>nil, "f"=>{"g"=>nil}}
h = {'a'=>{'b'=>'c'},'d'=>'e','f'=>{'g'=>''}}
def cleaned_hash(h)
h.reduce({}) do |memo, (key, val)|
memo[key] = if val.is_a? Hash
cleaned_hash(val)
else
nil
end
memo
end
end
cleaned_hash h
# => {"a"=>{"b"=>nil}, "d"=>nil, "f"=>{"g"=>nil}}
This will not modify your hash but instead give you cleaned copy

How to recursively remove all keys with empty values from (YAML) hash?

I have been trying to get rid of all hash keys in my YAML file that have empty (blank) values or empty hashes as values.
This earlier post helped me to get it almost right, but the recursive one-liner leaves my YAML dump with empty hashes whenever there is sufficiently deep nesting.
I would really appreciate any help on this. Thanks!
proc = Proc.new { |k, v| (v.kind_of?(Hash) && !v.empty? ) ? (v.delete_if(&proc); nil) : v.blank? }
hash = {"x"=>{"m"=>{"n"=>{}}}, 'y' => 'content'}
hash.delete_if(&proc)
Actual output
{"x"=>{"m"=>{}}, "y"=>"content"}
Desired output
{"y"=>"content"}
class Hash
def delete_blank
delete_if{|k, v| v.empty? or v.instance_of?(Hash) && v.delete_blank.empty?}
end
end
p hash.delete_blank
# => {"y"=>"content"}
Here's a more generic method:
class Hash
def deep_reject(&blk)
self.dup.deep_reject!(&blk)
end
def deep_reject!(&blk)
self.each do |k, v|
v.deep_reject!(&blk) if v.is_a?(Hash)
self.delete(k) if blk.call(k, v)
end
end
end
{ a: 1, b: nil, c: { d: nil, e: '' } }.deep_reject! { |k, v| v.blank? }
==> { a: 1 }
I think this the most correct version:
h = {a: {b: {c: "",}, d:1}, e:2, f: {g: {h:''}}}
p = proc do |_, v|
v.delete_if(&p) if v.respond_to? :delete_if
v.nil? || v.respond_to?(:"empty?") && v.empty?
end
h.delete_if(&p)
#=> {:a=>{:d=>1}, :e=>2}
I know this thread is a bit old but I came up with a better solution which supports Multidimensional hashes. It uses delete_if? except its multidimensional and cleans out anything with a an empty value by default and if a block is passed it is passed down through it's children.
# Hash cleaner
class Hash
def clean!
self.delete_if do |key, val|
if block_given?
yield(key,val)
else
# Prepeare the tests
test1 = val.nil?
test2 = val === 0
test3 = val === false
test4 = val.empty? if val.respond_to?('empty?')
test5 = val.strip.empty? if val.is_a?(String) && val.respond_to?('empty?')
# Were any of the tests true
test1 || test2 || test3 || test4 || test5
end
end
self.each do |key, val|
if self[key].is_a?(Hash) && self[key].respond_to?('clean!')
if block_given?
self[key] = self[key].clean!(&Proc.new)
else
self[key] = self[key].clean!
end
end
end
return self
end
end
Just a bit related thing. If you want to delete specified keys from nested hash:
def find_and_destroy(*keys)
delete_if{ |k, v| (keys.include?(k.to_s) ? true : ( (v.each { |vv| vv = vv.find_and_destroy(*keys) }) if v.instance_of?(Array) ; (v.each { |vv| vv = vv.find_and_destroy(*keys) }) if v.instance_of?(Hash); false) )}
end
.You can also customize it further
hash = {"x"=>{"m"=>{"n"=>{}}}, 'y' => 'content'}
clean = proc{ |k,v| !v.empty? ? Hash === v ? v.delete_if(&clean) : false : true }
hash.delete_if(&clean)
#=> {"y"=>"content"}
or like #sawa suggested, you can use this proc
clean = proc{ |k,v| v.empty? or Hash === v && v.delete_if(&clean) }

Resources