I feel like this is such an easy question
<% #state.cities.each do |city| %>
<%= city.id %>
<% end %>
puts the ids as follows:
1
2
3 etc...
How do I turn the iteration into an array?
so it outputs as follows:
[1,2,3,4,etc...]
There is a method that does just that!
What you are looking for is the map method.
Creates a new array containing the values returned by the block.
http://apidock.com/ruby/Array/map
The documentation states, creates an array containing the values returned by a block.
#state.map do |state|
state.id
end
=> [1,2,3,...]
Which is the same as:
#state.map(&:id)
=> [1,2,3,...]
But uses the Ruby Enumerable shorthand.
#state.map(&:id) would give you the same result!
You can use map:
<%= #state.map(&:id) %>
Related
I have an instance variable #tally_property, and if there are photos on that object I would like to cycle through the photos and show them.
So my code snippet looks like this:
<% if #tally_property.photos.present? %>
<% #tally_property.photos.each_with_index do |photo, index| %>
The issue is that based on the above, if #tally_property is nil, then the entire first line throws an error.
So is there a 'nil' check I can do that isn't bulky, i.e. I don't want to do if #tally_property.nil?, on both the primary object and the association, and is elegant and ruby & rails-esque?
I would use the safe navigation operator (&.) and write something like this:
<% #tally_property&.photos&.each_with_index do |photo, index| %>
...
<% end %>
In Ruby 2.3.0+ you can use the safe navigation operator:
#tally_property&.photos
ActiveSupport has a .try method that can be used to the same end in older versions of ruby:
#tally_property.try(:photos)
You can add a simple conditional to be able to safely iterate through the collection:
<% (#tally_property.try(:photos)||[]).each_with_index do |photo, index| %>
<% end %>
Rails 4 adds ActiveRecord::Relation#none and a change in behaviour so that associations always return a ActiveRecord::Relation. So its perfectly acceptable to write:
<% #tally_property.try(:photos).try(:each_with_index) do |photo, index| %>
<% end %>
After upgrading your app. Or you can use a partial and render:
<%= render partial: 'photos', collection: #tally_property.photos if #tally_property %>
Which removes the need for writing the iteration.
Use && (or and, they each have their sweetspot).
Taking it out of Erb for a moment, I would generally write something like this:
if #tally_property and #tally_property.photos.present?
Depending on photos I might use:
if #tally_property and #tally_property.photos
or perhaps:
if #tally_property and not #tally_property.photos.empty?
Sometimes I'll use a temporary variable:
if (photos = #tally_property && #tally_property.photos)
photos.each #…
That kind of thing.
I would recommend this episode of Ruby Tapas, And/Or for a longer (but still quick) look at it.
One more way, just select all photos connected to this tally_property:
example how it might be:
Photo.joins(:tally_property).each_with_index do |photo, index|
Having some trouble with nested hash parameters. Product has many features. Say product 1 has features foo and baz.
I'd like to pass both the product and all of the features to query string parameters in a link_to so that it ends up looking like this:
"/puppies/new?features%5Bbaz%5D=qux&features%5Bfoo%5D=bar&product=1"
I am currently trying this, which gets a syntax error for reasons that are probably obvious to people that are not me.
<% Product.each do | product | %>
<%= link_to(new_puppy_path(product: product, features: { product.features.each
{ | feature| feature.name : 'feature.'} } ), class: 'slorp') do %>
// stuff inside the link
<% end %>
<% end %>
Any idea what I'm doing stupid here?
UPDATE: I have updated the code to:
<%= link_to(new_puppy_path(product: product, features: product.features.each{|feature| {feature.name.to_sym => feature.feature_colors.first}}), class: 'image') do %>
This is much closer, as my output URL is now:
/puppies/new?features%5B%5D=3&features%5B%5D=2&product=2
I am just missing the name of the feature in-between the %5B and %5D - not sure why the feature name is not showing up.
You need to use map instead of each. Each returns the original array itself on which it is operating. While map returns the elements from the block.
Fixed. Pulled creating the hash up into the model:
def reco_features
list = Hash.new
feature_colors.each do |feature_color|
list[feature_color.feature.name] = feature_color.id
end
return list
end
Then updated the link_to:
<%= link_to(new_puppies_path(product: product, features: features.reco_features ), class: 'slorp') do %>
Not sure why the inline wasn't working, but this fixed it. Required some additional context outside of the initial question, which I did not realize at the time. Apologies and thanks to all.
I am trying to compare the names of the element at the current index and the previous index for each element to determine if they are the same name so I don't print out the name twice.
Unfortunately, trying to access the array element using array[i] doesn't work, but if I hard-code an index or just print out the index, it works fine so I'm not sure where it's messing up.
I need to be able to access the previous element though so I can't use other loops so only suggest something where I can access the previous element in the array.
<% for i in 1..count %>
<% if array[i].count > 1 %>
<% if array[i-1].name == array[i].name %>
<%= array[i].name %>
<%= array[7].name %>
<%= i %>
<% end %>
<% end %>
Does anyone know the correct way to access an element in an array?
This should do it if your list is sorted and all you care about is printing names:
<% array.map(&:name).uniq.each do |name| %>
<%= name %>
<% end %>
More generally, you can do it like this:
array.each_with_index do |el, i|
prev_el = array[i-1] #will be nil for the first element
next_el = array[i+1] #will be nil for the last element
if prev_el && el.name == prev_el.name
#name same as previous
end
if next_el && el.name == next_el.name
#name same as next
end
end
You should avoid index-based array access for loops, not because they don't work but because there are much nicer and more readable ways of looping through arrays in Ruby.
You can keep in mind that you are using Ruby, and the motto of Ruby is "Do More in Less Work".
You can use uniq to filter out all similar elements, then iterate through them to do whatever you want to do. uniq works like this:
a = [ "a", "a", "b", "b", "c" ]
a.uniq # => ["a", "b", "c"]
This should do it all what you tried to do:
<% array.uniq.each do |obj| %>
<%= obj.name %>
<% end %>
Just thought of another way of doing this:
grouped = array.group_by(&:name)
Now you have a hash where each key is a unique name and the corresponding value is all the array elements with that name. So next you can do stuff like
#list of names in alphabetical order
grouped.keys.sort
#get one element for each unique name
grouped.map{|name,els| els.first}
#print out how many you have for each name
grouped.each{|name, els| puts "#{name.inspect} => #{els.size} elements"};false
I want to implement a search functionality in my Rails app by using the pg_search gem. I've set up everything like it says in the documentation. Then I've set up a search controller with a show action:
def show
#pg_search_documents = PgSearch.multisearch(search_params)
end
The search itself works but I have a really annoying problem in my view. Whatever I do, it always outputs an array of PgSearch::Document objects. Even when I only write this in my view:
<%= #pg_search_documents.each do |document| %>
<% end %>
I get this (I've shortened it):
[#<PgSearch::Document id: 2, content: "…", searchable_id: 28, searchable_type: "Vessel">, #<PgSearch::Document id: 3, content: "…", searchable_id: 27, searchable_type: "Vessel">]
I know that pg_search sets up a polymorphic association which I've never dealt with before — could that be the problem?
Thanks in advance
<%= #pg_search_documents.each do |document| %>
<% end %>
This is a classic error, one I remember being puzzled over when I first started learning Rails. The mistake is using <%= %> with each. The return value of each is the array that you're iterating over (in this case, #pg_search_documents), and by using <%=, you're telling Rails to create a string from that array and insert it into your view. That generally isn't what you want: you want the view to be generated by the code inside the block you're passing to each.
Use <% #pg_search_documents.each do |document| %> instead (omitting the =) and you'll avoid the dump of the array's content.
You may also need to use .searchable as #blelump suggests, but I wanted to answer the other half of your question, as it's a common pitfall.
To get back to the original source model, searchable call is needed on these search result records, e.g:
<% #pg_search_documents.each do |document| %>
<%= document.searchable %>
<% end %>
You can also switch back to the source model within your controller, e.g:
#pg_search_documents = PgSearch.multisearch(search_params).collect(&:searchable)
Then, the #pg_search_documents will contain Vessel elements.
I have 2 instance objects : #original_files and #version2_files
I want to retrieve the elements found in #original_files but NOT FOUND in #version2_files.
The code below:
<% #original_files.each do |original_file| %>
<% #version2_files.each do |ver2_file| %>
<% if original_file.name == ver2_file.name%>
<%= original_file.name %> is common to both sets
<% break %>
<% else %>
<% end %>
<% end %>
<% end %>
I would appreciate it if someone could help me figure out how to find the elements found in #original_files only.
Thanks
They are arrays? You can subtract...
#original_files - #version2_files
Hashes can use diff.
#original_files.diff(#version2_files)
I understand this to mean that #original_files and #version2_files are hashes, and you want to find elements in #original_files that are not in #version2_files
#original_files.keys - #version2_files.keys
disclaimer: new to ruby and may be ignorant of how hashes/arrays work exactly.
#danh's answer is right if you want to compare on key/value pairs. But if you're comparing the 2 hashes based on their values only and the keys are not important, then diff may not be what you need. In which case, you would do something like:
original.values - version2.values => [ ... ]
which returns an array of elements found only in original but not version2.
Also, this and this may help.