Clear all values in nested ruby hash - ruby-on-rails

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

Related

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 : )

Iteration on rails hash

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

Create array and append key and value in each

I want to create a array and then to insert values for each key (key should be the value from each). But seems to not working. This is my code.
#options = %w(Services Resources)
#images = []
#options.each do |value|
#images[value] << Media::Image.where(type: "Media::#{value.singularize}Image")
end
#images is an array so referencing an element in it should be #images[Integer] and value is a string (in the first iteration it's "Services" and in the second "Resources"). Instead, what would work for you is Hash:
#options = %w(Services Resources)
#images = {}
#options.each do |value|
#images[value] = Media::Image.where(type: "Media::# {value.singularize}Image")
end
#images is a Array, Array can not use as a Hash.
Maybe you want create a Hash like this
#images = Hash.new {|h,k| h[k]=[]}

Search for key-value from array of nested hash in ruby

I have array of nested hash that is,
#a = [{"id"=>"5", "head_id"=>nil,
"children"=>
[{"id"=>"19", "head_id"=>"5",
"children"=>
[{"id"=>"21", "head_id"=>"19", "children"=>[]}]},
{"id"=>"20", "head_id"=>"5",
"children"=>
[{"id"=>"22", "head_id"=>"20", "children"=>[]}, {"id"=>"23"}]
}]
}]
I need array of all values which have key name 'id'. like #b = [5,19,21,20,22,23]
I have already try this '#a.find { |h| h['id']}`.
Is anyone know how to get this?
Thanks.
You can create new method for Array class objects.
class Array
def find_recursive_with arg, options = {}
map do |e|
first = e[arg]
unless e[options[:nested]].blank?
others = e[options[:nested]].find_recursive_with(arg, :nested => options[:nested])
end
[first] + (others || [])
end.flatten.compact
end
end
Using this method will be like
#a.find_recursive_with "id", :nested => "children"
It can be done like this, using recursion
def traverse_hash
values = []
#a = [{"id"=>"5", "head_id"=>nil,
"children"=>
[{"id"=>"19", "head_id"=>"5",
"children"=>
[{"id"=>"21", "head_id"=>"19", "children"=>[]}]},
{"id"=>"20", "head_id"=>"5",
"children"=>
[{"id"=>"22", "head_id"=>"20", "children"=>[]}, {"id"=>"23"}]
}]
}]
get_values(#a)
end
def get_values(array)
array.each do |hash|
hash.each do |key, value|
(value.is_a?(Array) ? get_values(value) : (values << value)) if key.eql? 'id'
end
end
end

TypeError: no implicit conversion of Symbol into Integer

I encounter a strange problem when trying to alter values from a Hash. I have the following setup:
myHash = {
company_name:"MyCompany",
street:"Mainstreet",
postcode:"1234",
city:"MyCity",
free_seats:"3"
}
def cleanup string
string.titleize
end
def format
output = Hash.new
myHash.each do |item|
item[:company_name] = cleanup(item[:company_name])
item[:street] = cleanup(item[:street])
output << item
end
end
When I execute this code I get: "TypeError: no implicit conversion of Symbol into Integer" although the output of item[:company_name] is the expected string. What am I doing wrong?
Your item variable holds Array instance (in [hash_key, hash_value] format), so it doesn't expect Symbol in [] method.
This is how you could do it using Hash#each:
def format(hash)
output = Hash.new
hash.each do |key, value|
output[key] = cleanup(value)
end
output
end
or, without this:
def format(hash)
output = hash.dup
output[:company_name] = cleanup(output[:company_name])
output[:street] = cleanup(output[:street])
output
end
This error shows up when you are treating an array or string as a Hash. In this line myHash.each do |item| you are assigning item to a two-element array [key, value], so item[:symbol] throws an error.
You probably meant this:
require 'active_support/core_ext' # for titleize
myHash = {company_name:"MyCompany", street:"Mainstreet", postcode:"1234", city:"MyCity", free_seats:"3"}
def cleanup string
string.titleize
end
def format(hash)
output = {}
output[:company_name] = cleanup(hash[:company_name])
output[:street] = cleanup(hash[:street])
output
end
format(myHash) # => {:company_name=>"My Company", :street=>"Mainstreet"}
Please read documentation on Hash#each
myHash.each{|item|..} is returning you array object for item iterative variable like the following :--
[:company_name, "MyCompany"]
[:street, "Mainstreet"]
[:postcode, "1234"]
[:city, "MyCity"]
[:free_seats, "3"]
You should do this:--
def format
output = Hash.new
myHash.each do |k, v|
output[k] = cleanup(v)
end
output
end
Ive come across this many times in my work, an easy work around that I found is to ask if the array element is a Hash by class.
if i.class == Hash
notation like i[:label] will work in this block and not throw that error
end

Resources