How do I loop over a hash of hashes? - ruby-on-rails

I have this hash:
h
=> {"67676.mpa"=>{:link=>"pool/sdafdsaff", :size=>4556}}
> h.each do |key, value|
> puts key
> puts value
> end
67676.mpa
linkpool/sdafdsaffsize4556
How do I access the separate values in the value hash on the loop?

Value is a Hash to so you need iterate on it or you can get only values:-
h.each do |key, value|
puts key
value.each do |k,v|
puts k
puts v
end
end
or
h.each do |key, value|
puts key
value.values.each do |v|
puts v
end
end

You'll want to recurse through the hash, here's a recursive method:
def ihash(h)
h.each_pair do |k,v|
if v.is_a?(Hash)
puts "key: #{k} recursing..."
ihash(v)
else
# MODIFY HERE! Look for what you want to find in the hash here
puts "key: #{k} value: #{v}"
end
end
end
You can Then take any hash and pass it in:
h = {
"x" => "a",
"y" => {
"y1" => {
"y2" => "final"
},
"yy1" => "hello"
}
}
ihash(h)

I little improved Travis's answer, how about this gist:
https://gist.github.com/kjakub/be17d9439359d14e6f86
class Hash
def nested_each_pair
self.each_pair do |k,v|
if v.is_a?(Hash)
v.nested_each_pair {|k,v| yield k,v}
else
yield(k,v)
end
end
end
end
{"root"=>{:a=>"tom", :b=>{:c => 1, :x => 2}}}.nested_each_pair{|k,v|
puts k
puts v
}

The simplest way to separate out all three values in this case would be as follows:
h.each do |key, value|
puts key
puts value[:link]
puts value[:size]
end

You can access the values of a hash directly by calling hash.values. In this case you could do something like
> h = {"67676.mpa"=>{:link=>"pool/sdafdsaff", :size=>4556}}
> h.values.each do |key, value|
> puts "#{key} #{value}"
> end
link pool/sdafsaff
size 4556

Related

RoR string from array

I have an array of hashes generated by map
arr = current_order.order_items.map{|oi|[{name:oi.name,price:oi.price}]
[{:name=>"Jacket", :price=>300},
{:name=>"Bag", :price=>650 },
{:name=>"Suit", :price=>300}].to_s
i need to make a string from it like this
name: Jacket,price:300
name: Bag,price:650
name: Suit,price:300
What i did it gsub every needed element like gsub(':size=>','size:')
but it looks very ugly
Need more convenient solution for this
You could do something like:
Define a function on a hash to pretty print it for you.
map over the array to gain pretty printed strings for each.
def pretty_print(hash)
hash.map {|key, value| "#{key}: #{value}"}.join(', ')
end
arr.map {|hash| pretty_print(hash)}
If keys are predetermined:
arr.map { |item| "name:#{ item[:name] }, price:#{ item[:price] }" }.join("\n")
If not:
arr.map { |item| item.map { |k, v| "#{ k }:#{ v }" }.join(', ') }.join("\n")

Ruby, Map, Object attributes

I have an object that looks like the below:
class Report
attr_accessor :weekly_stats, :report_times
def initialize
#weekly_stats = Hash.new {|h, k| h[k]={}}
#report_times = Hash.new {|h, k| h[k]={}}
values = []
end
end
I want to loop through the weekly_stats and report_times and upcase each key and assign it its value.
Right now I have this:
report.weekly_stats.map do |attribute_name, value|
report.values <<
{
:name => attribute_name.upcase,
:content => value ||= "Not Currently Available"
}
end
report.report_times.map do |attribute_name, value|
report.values <<
{
:name => attribute_name.upcase,
:content => format_date(value)
}
end
report.values
Is there a way I could map both the weekly stats and report times in one loop?
Thanks
(#report_times.keys + #weekly_stats.keys).map do |attribute_name|
{
:name => attribute_name.upcase,
:content => #report_times[attribute_name] ? format_date(#report_times[attribute_name]) : #weekly_stats[attribute_name] || "Not Currently Available"
}
end
If you are guaranteed nil or empty string in weekly_stats, and a date object in report_times, then you could use this information to work through a merged hash:
merged = report.report_times.merge( report.weekly_stats )
report.values = merged.map do |attribute_name, value|
{
:name => attribute_name.upcase,
:content => value.is_a?(Date) ? format_date(value) : ( value || "Not Currently Available")
}
end

How to treat an Array with two elements as-like a Hash key / value pair?

I am using Ruby on Rails 3.2.2. I have the following scenario:
# hash_params.class
# => Hash
# hash_params.inspect
# => { :key1 => :value1, :key2 => value2, ... => ... }
#
def self.method_1(hash_params)
hash_params.each do { |hash_param| self.method_2(hash_param) }
end
# param.class
# => Array
# param.inspect
# => [:key1, value1] # or '[:key2, value2]' or '[..., ...]', depending on cases.
#
def self.method_2(param)
logger.debug "Key => #{param[0])"
logger.debug "Value => #{param[1])"
end
Given outputs commented out in the above code, when I run the method_1 then in the logger file I have the following:
Key => :key1
Value => :value1
Key => :key2
Value => :value2
Key => ...
Value => ...
I would like to treat the param variable in method_2 as-like a key / value pair (not as an Array), for example by making something like the following
def self.method_2(param)
param do |key, value| # Note: This code line doesn't work. It is just a sample code to clarify the question.
logger.debug "Key => #{key.inspect)"
logger.debug "Value => #{value.inspect)"
end
end
? Is it possible? If so, how? What do you advice about?
Use Hash[]:
param = [:key1, 'value1']
h = Hash[*param]
puts h[:key1]
Output:
value1
How about
def self.method_1(hash_params)
hash_params.each do { |key, value| self.method_2(key, value) }
end
def self.method_2(key, value)
logger.debug "Key => #{key)"
logger.debug "Value => #{value)"
end
Otherwise you can still pass a hash in param like
def self.method_1(hash_params)
hash_params.keys.each do { |key| self.method_2(hash_params.slice(key)) }
end
edit: if you want a hash as parameter you could just do
def self.method_1(hash_params)
hash_params.each do { |key, value| self.method_2({key => value}) }
end

How do you add a title attribute to a link generated by rinku auto_link in ruby on rails?

This is the code currently in auto_html.rb
The title needs to be dynamic, basically a full URL for a truncated URL where the truncated URL is generated by the auto_link.
AutoHtml.add_filter(:link).with({}) do |text, options|
attributes = Array(options).reject { |k,v| v.nil? }.map { |k, v| %{#{k}="#{REXML::Text::normalize(v)}"} }.join(' ')
Rinku.auto_link(text, :all, attributes) do |url|
url.gsub(/https?:\/\//, "").truncate(25)
end
end
You can add additional attributes as part of the string that gets passed into the third argument:
Rinku.auto_link(text, :all, 'title="my title" target="_blank"')
In your case you could just append it to the attributes variable:
AutoHtml.add_filter(:link).with({}) do |text, options|
attributes = Array(options).reject { |k,v| v.nil? }.map { |k, v| %{#{k}="#{REXML::Text::normalize(v)}"} }.join(' ')
attributes += ' title="My title"'
Rinku.auto_link(text, :all, attributes) do |url|
url.gsub(/https?:\/\//, "").truncate(25)
end
end

to_json returns nil in a field if its black, ios can't interpret that null in ror

How to change the field values from null to "" in the output of to_json?
It currently returns
{"name":"priya","mobile":null}
instead I want
{"name":"priya","mobile":""}
or
{"name":"priya","mobile":"NA"}
Please suggest
To add to philee's answer, you could add an as_json method to your model.
def as_json(opts={})
json = super(opts)
Hash[*json.map{|k, v| [k, v || "NA"]}.flatten]
end
s = {"name" => "pryia", "mobile" => nil}
Hash[*s.map{|k, v| [k, v || "NA"]}.flatten]
# => "name"=>"pryia", "mobile"=>"NA"}
Hash[*s.map{|k, v| [k, v || "NA"]}.flatten].to_json
# => "{\"name\":\"pryia\",\"mobile\":\"NA\"}"

Resources