How to group an array of ActiveRecord objects? - ruby-on-rails

I have an array:
<% #widgets.each do |w| %>
...
<% end %>
How can I display them in groups? Let's say by 4:
<div>1, 2, 3, 4</div>
<div>5, 6, 7, 8</div>
etc.
Thank you.

For the specific example you gave, you want each_slice:
<% #widgets.each_slice(4) do |ws| %>
<div><%= ws.join(', ') %></div>
<% end %>
You might also be interested in each_cons (each consecutive, e.g. "1,2,3", "2,3,4", "3,4,5", etc.) or group_by for arbitrary groupings.
Person = Struct.new(:name,:age,:male) do
def inspect
"<#{'fe' unless male}male '#{name}' #{age}>"
end
end
all = [
Person.new("Diane", 12, false),
Person.new("Harold", 28, true),
Person.new("Gavin", 38, true),
Person.new("Judy", 55, false),
Person.new("Dirk", 59, true)
]
p all.group_by(&:male)
#=> {
#=> false=>[ <female 'Diane' 12>, <female 'Judy' 55> ],
#=> true =>[ <male 'Gavin' 38>, <male 'Harold' 28>, <male 'Dirk' 59> ]
#=> }
p all.group_by{ |person| (person.age / 10) * 10 }
#=> {10=>[<female 'Diane' 12>],
#=> 20=>[<male 'Harold' 28>],
#=> 30=>[<male 'Gavin' 38>],
#=> 50=>[<female 'Judy' 55>, <male 'Dirk' 59>]}

Try to use each_slice(n):
require 'erb'
#widgets = (1..8).to_a
template = <<EOF
<% #widgets.each_slice(4) do |w| %>
<div><%= w.join(', ') %></div>
<% end %>
EOF
puts ERB.new(template).result(binding)
# =>
<div>1, 2, 3, 4</div>
<div>5, 6, 7, 8</div>

Related

Subtract current item with next indexed item

I'm trying to subtract the current item in the loop with the next indexed item, but I'm just getting the following error: undefined method `[]' for 0.0:Float
<% #trial.methods.each_with_index do |e, index| %>
<%= (e.total - e.total[index+1]) %><br />
<%= Time.at(e.try(:assessment).try(:assessment_date)/1000).strftime("%d/%m/%Y") %><br />
<%= e.try(:assessment).try(:degrees) %>
<% end %>
I think there is an easier way of doing that. Look at the following example.
# An array of 5 random numbers
a = [7,12,1,2,3]
# Iterate through the indices of the array
a.each_index do |i|
# We only show the result of a[i+1] - a[i]
# given i+1 is still in range of the array
puts "#{a[i+1] - a[i]}" if (i+1) < a.length
end
This should output 5 -11 1 1 each on a new line.
Similarly, you could do something like this:
<% #trial.methods.each_index do |i| %>
<% if i + 1 < e.total.length %>
<%= (e.total[i] - e.total[index+1]) %>
<% else %>
<%= 0 %>
<% end %>
<% end %>
Use #each_cons to build a subarray of consecutive pairs, then map it:
array = [7,12,1,2,3]
array.each_cons(2).map{ |e| e.last - e.first }
# => [5, -11, 1, 1]
A slight alternative:
array.each_cons(2).map{ |a, b| b - a }
Of course, you can change to a - b or whatever you need.
The first part does this:
array.each_cons(2).each {|e| p e}
# => [7, 12]
# => [12, 1]
# => [1, 2]
# => [2, 3]
You can use Enumerable#reduce to achieve this:
a = [7,12,1,2,3]
a.reduce(&:-) # -11
So for your case:
#trial.methods.map{|e| e.total}.reduce(&:-)
See https://ruby-doc.org/core-2.2.3/Enumerable.html#method-i-reduce on its usage

Rails traverse deep array of hashes

I got this very confusing array of hashes as an API response.
http://jsfiddle.net/PP9N5/
( the full response is massive. Posting only a part of it but it covers all elements of the response)
How can I get to "airlines".
I tried this
<% #flight["air_search_result"]["onward_solutions"]["solution"].each do|h| %>
<strong><%=h["pricing_summary"]["total_fare"] %></strong> -
<% h["flights"]["flight"]["segments"]["segment"].each do |s| %>
<%= s['airline'] %>
<% end %> <br> <hr>
<% end %>
And I get this error
can't convert String into Integer
I did some modifications like
<%= h["flights"]["flight"]["segments"]["segment"].first["airline"] %>
Error received - can't convert String into Integer
and
<%= h["flights"]["flight"]["segments"]["segment"][0]["airline"] %>
Error received - undefined method '[]' for nil:NilClass
Isnt there a simple way, like I say to find a key "airline" and for that key it returns its value. I stumbled upon this link, though I dont get any error, I also dont get any result.
Thanks.
UPDATE
I did this
<% h["flights"]["flight"]["segments"]["segment"].each do |o,p| %>
<% if o=="airline" %> <%= p %> <% end %>
<% end %> <br> <hr>
<% end %>
I can get few values of airlines where inside segment there is no array.
For eg, i can get where departure_date_time is 2014-07-07T07:10:00, index = 5.
http://jsfiddle.net/PP9N5/1/ (scroll down)
Here is some code you can add which will extract all keys equal the parameter in any Hash within your Hash:
class Hash
def deep_find(query, &block)
flat_map do |key, value|
if key == query
yield value if block_given?
[value]
elsif value.is_a? Hash
value.deep_find(query, &block)
elsif value.is_a? Array
value.select { |i| i.is_a? Hash }.flat_map { |h| h.deep_find(query, &block) }
end
end
end
end
Example:
hash = {"h" => [{ 'x' => [1, 5] }, { 'x' => 2 }, { 'f' => { 'x' => [3, 4] } }], 'x' => 6 }
hash.deep_find('x') { |x| puts "#{x}" }
# [1, 5]
# 2
# [3, 4]
# 6
# => [[1, 5], 2, [3, 4], 6]
it's a tipical problem :D
Replace "=>" for ":" and render.
your_json = {.....}
your_json.gsub("=>", ":")
puts your_json
You can validate a JSON before to work it with http://jsonlint.com/.

how can Arrange rails params order

when i look at my console after submitting a form i can see like
Parameters: {"authenticity_token"=>"l0dqmb95MydzCWMugWdYt/2bGYyRyDF5ZfOGjrKhjfc=", "project_id"=>"second", "esthour"=>{"nonmodulhours_attributes"=>{"0"=>{"nonmodul_id"=>"61", "nonmodul_est_hours"=>"1"}, "1"=>{"nonmodul_id"=>"62", "nonmodul_est_hours"=>"9"}, "2"=>{"nonmodul_id"=>"63", "nonmodul_est_hours"=>""}}, "modul1hours_attributes"=>{"0"=>{"modul1_est_hours"=>"8", "modul1_id"=>"25"}, "1"=>{"modul1_est_hours"=>"", "modul1_id"=>"26"}**, "2"=>{"modul1_est_hours"=>"88", "modul1_id"=>"27"}}**, "ecommerce_est_hours"=>"", "modulhours_attributes"=>{"0"=>{"modul_est_hours"=>"8", "modul_id"=>"53"}, "1"=>{"modul_est_hours"=>"1", "modul_id"=>"54"}, "2"=>{"modul_est_hours"=>"8", "modul_id"=>"55"}}, "cms_est_hours"=>"", "nonmodul1hours_attributes"=>{"0"=>{"nonmodul1_id"=>"25", "nonmodul1_est_hours"=>"2"}, "1"=>{"nonmodul1_id"=>"26", "nonmodul1_est_hours"=>""}, "2"=>{"nonmodul1_id"=>"27", "nonmodul1_est_hours"=>"5"}}, "rfp_id"=>"35"}, "commit"=>"Add Todo", "utf8"=>"✓"}
here how can i arrange the attributes like line
"2"=>{"modul1_est_hours"=>"88", "modul1_id"=>"27"}}
to
"2"=>{ "modul1_id"=>"27","modul1_est_hours"=>"88",}}
**mean modul1_id before modul1_est_hours
form.html.erb**
<% #m1.map(&:id).each do |id|%>
<%= modul1(id) %> <%= f.hidden_field :modul1_id, :value => id %>
<%= f.number_field :modul1_est_hours, :size => 30 %>
</tr>
<% end %>
Edit
def get_issue_attribute_param1(u)
u.each do |key, value|
value.is_a?(Hash) ? get_issue_attribute_param1(value) : update_issue(key,value)
end
end
def update_issue(q,p)
if q.include?("_")
q1 = q.split("_")
q0 = q1[0]
if q1[0].include?("modul") && q1[1] == "id"
$id_map = p
puts $id_map
end
end
end
Here you go(in irb):
a = {"modul1_est_hours"=>"88", "modul1_id"=>"27"}
=> {"modul1_est_hours"=>"88", "modul1_id"=>"27"}
Hash[a.sort{|x,y| y.first <=> x.first }]
=> {"modul1_id"=>"27", "modul1_est_hours"=>"88"}
But, as everybody pointed out. If you could tell what you're trying to do, or what do you want with this? Then maybe what you're looking for maybe little more easy to understand and answer. :)
Edit
Here are the updated methods:
def get_issue_attribute_param1(params)
ids = []
params["esthour"].select{|hour| hour.include?('modul') }.each_pair do |key, value|
ids << update_issue(value)
end unless params["esthour"].nil?
ids # will have array of array like this: [["61", "62", "63"], ["25", "26", "27"], ["53", "54", "55"], ["25", "26", "27"]] for which you can do: `.flatten`
end
def update_issue(id_with_hours)
id_with_hours.values.map{|m| m.select{|v| v.include?('id') } }.map(&:values).flatten
end
Though I am not clear for what purpose you're using $id_map and hence I had to leave that scenario for you to handle. :)
I hope it helps.

Rails array help selecting column

I have this array:
[:dk, #<Domain dk: 8, id: 12, se: 5, com: 5>]
I want to select the dk: 8?
So that the input is: 8
My view:
<% #prices.each do |price| %>
<%= price %><br />
<% end %>
The output:
[:dk, #<Domain dk: 8, id: 12, se: 5, com: 5>]
[:com, #<Domain dk: 8, id: 12, se: 5, com: 5>]
My controller:
def domain
country_codes = %w[ dk com ]
#domain = "asdsad"
#results = { }
#prices = { }
country_codes.each do |cc|
#results[cc] = Whois.whois("#{#domain}.#{cc}")
#prices[cc.to_sym] = Domain.order(cc).first
end
render :layout => false
end
You don't have an Array, you have a Hash.
Do this:
<% #prices.each do |cc,domain| %>
<%= domain.send(cc) %><br />
<% end %>
So:
first: #<Domain dk: 8, id: 12, se: 5, com: 5>.send(:dk) # => 8
second: #<Domain dk: 8, id: 12, se: 5, com: 5>.send(:com) # => 5
Upon request, further explanation.
you create a Hash: #prices = { }
then you fill it: #prices[cc.to_sym] = Domain.order(cc).first
The latest means to you add to the hash one object: Domain.order(cc).first, with it's key: cc.to_sym

Accesing hash keys and attributes from the view

in an attempt to create a model with an array as an atribute, i ended up creating an array of hashes like so:
data1 = {}
data1[:name] = "Virtual Memory"
data1[:data] = #jobs.total_virtual_memory
data2 = {}
data2[:name] = "Memory"
data2[:data] = #jobs.total_memory
#data = []
#data << data1
#data << data2
which populates #data like this:
[{:data=>[#<Job day: "2010-08-02">, #<Job day: "2010-08-04">], :name=>"Virtual Memory"}, {:data=>[#<Job day: "2010-08-02">, #<Job day: "2010-08-04">], :name=>"Memory"}]
However i do not know how to acces these variables in the view. So as tu run somethig like:
for series in #data
series:name
for d in series:data
data:[Date, Value]
end
end
which would return something along the lines of:
Name1
Date1, Value1
Date2, Value 2,
Date3, Value 3,
Date4, Value 4,
Name2
Date1, Value 1,
Date2, Value 2,
Date3, Value 3,
Date4, Value 4,
This should work:
<% for series in #data %>
<%= series[:name] %>
<% for d in series[:data] %>
<%= d.date %>, <%= d.value %>
<% end %>
<% end %>
However you might consider using a more suitable datastructure instead of hashs. A Struct for example. This could look like this:
# in lib/JobData.rb:
JobData = Struct.new(:name, :data)
# in the controller:
data1 = JobData.new("Virtual Memory", #jobs.total_virtual_memory)
data2 = JobData.new("Memory", #jobs.total_memory)
#data = [data1, data2]
# in the view:
<% for series in #data %>
<%= series.name %>
<% for d in series.data %>
<%= d.date %>, <%= d.value %>
<% end %>
<% end %>
As a style point: I used for because you used for, but in general it's considered more rubyish to use each instead.
Here is the view:
<% for d in #data %>
{ pointInterval: <%= 1.day * 1000 %>,
name:<%= "'#{d[:name]}'"%>,
pointStart: <%= 2.weeks.ago.at_midnight.to_i * 1000 %>,
data: [
<% for chart in d[:data] %>
<%= "'[#{chart.day.to_time(:utc).to_i * 1000}, #{chart.data_attribute}],'" %>
<% end %>
]
},
<% end %>
Use #{d[:name]} to access the value of the "name" key and use d[:data] to access the array, then just loop throughthe array as if it were any normal array

Resources