#example.each do |e|
#do something here
end
Here I want to do something different with the first and last element in each, how should I achieve this? Certainly I can use a loop variable i and keep track if i==0 or i==#example.size but isn't that too dumb?
One of the nicer approaches is:
#example.tap do |head, *body, tail|
head.do_head_specific_task!
tail.do_tail_specific_task!
body.each { |segment| segment.do_body_segment_specific_task! }
end
You can use each_with_index and then use the index to identify the first and last items. For example:
#data.each_with_index do |item, index|
if index == 0
# this is the first item
elsif index == #data.size - 1
# this is the last item
else
# all other items
end
end
Alternately, if you prefer you could separate the 'middle' of the array like so:
# This is the first item
do_something(#data.first)
#data[1..-2].each do |item|
# These are the middle items
do_something_else(item)
end
# This is the last item
do_something(#data.last)
With both these methods you have to be careful about the desired behaviour when there are only one or two items in the list.
A fairly common approach is the following (when there are certainly no duplicates in the array).
#example.each do |e|
if e == #example.first
# Things
elsif e == #example.last
# Stuff
end
end
If you suspect array may contain duplicates (or if you just prefer this method) then grab the first and last items out of the array, and handle them outside of the block.
When using this method you should also extract the code that acts on each instance to a function so that you don't have to repeat it:
first = #example.shift
last = #example.pop
# #example no longer contains those two items
first.do_the_function
#example.each do |e|
e.do_the_function
end
last.do_the_function
def do_the_function(item)
act on item
end
Related
I'm using Rails and have an array that I'm looking to control the output of.
In short, if a project with an ID exists in #projects, then I don't want to output the corresponding part of the array, if that exists. So if a project with ID of 1 exists, then I don't want to output #array[1], even if that's present.
The following hardcoded case statement works successfully, to avoid outputting #array[1], #array[2] and #array[3]:
#array.each do |key, value|
case key when '1','2','3' then
next # Skip this array key
end
# Otherwise, do something else
end
But instead of hardcoding '1','2','3', I actually want these to be passed in dynamically, via ActiveRecord. i tried to build a string:
#projects_string = #projects.map {|element|
"'#{element.id}'"
}.join(',')
This outputs successfully as '1','2','3'. But I can't figure out a way to pass this into the case statement. This fails to match:
#array.each do |key, value|
case key when "#{#projects_string}" then
next # Skip this array key
end
# Otherwise, do something else
end
Is there a way to achieve this using this method, or maybe a better way to achieve this using some of Rails' capabilities?
I did look through this answer, which seemed most relevant, but couldn't see how to implement it.
You can do it in this way as well.
#project_arr = #projects.map{|p| p.id.to_s}
#array.each do |key, value|
unless #project_arr.include?(key.to_s)
#YOUR CODE GOES HERE
else
# SKIP IT
end
end
You should not use string but use array and do like
#array = Array.new
#array = #projects.map{|arr| arr.id}
#array.each do |key, value|
if #array.include?(key)
#code to skip if project is included
else
#your code
end
end
Array can be used easily for comparisons than string
I feel like this depends on the type of collection you are dealing with: Array or ActiveRelation.
If they are just arrays and you want elements of #array that aren't in #parent, you can just do:
#array - #parent
If they are both ActiveRecord objects, why not just filter out the #array when it's created?
#array = Project.where.not(id: #projects.pluck(:id))
It seems inefficient (n^2) to have to scan a lookup array (#projects) for each element in the collection you are iterating over.
You can try the following:
#array = [1,2,3,4]
#projects = [2,3]
#array.each do |key|
case
when true === #projects.include?(key)
next
end
puts "Doing something else for #{key}"
end
Outputs:
Doing something else for 1
Doing something else for 4
I have active record object of array.
#obj = User.post
Now I want to loop through #obj from third element to last. I want something like below
#obj.third-to-last.each do
#output here
end
I can do this using a counter and if else condition. I want know if there is any better and simple way to do this.
Use the Array#drop method to drop the first two elements of #obj:
#obj.drop(2).each do |obj|
# whatever...
end
Just do using method ary[range] :
#obj[2..-1].each do |el|
# your code here
end
What is an elegant 'ruby' way to alter a loop's function every Nth iteration? I would prefer not to use (1..50).each do |i| because I want to iterate over every object in a list objects.
objects.each do |object|
#Do this with object information
#Do not do this if this is the third time through the loop
end
objects.each_with_index do |object, idx|
if idx == 2 # third time
# or
# if idx % 3 == 2 # every third time
# do special thing
else
# do normal thing
end
end
I wish I described this better, but it's the best I know how. I have two classes Cars and Colors. Each can have many of each other through a association class CarColors. The association is set up correctly I'm positive of this but I can't seem to get this to work:
#carlist = Cars.includes(:Colors).all
#carlist.colors
ERROR
#carlist[0].colors
WORKS
My question is how can I iterate over the #carlist without declaring a index as in the successful example? Below is a few things I have tried which also fail:
#carlist.each do |c|
c.colors
end
#carlist.each_with_index do |c,i|
c[i].colors
end
Your first example fails because Car.includes(:colors).all returns an array of cars, not a single car, so the following will fail, because #colors is not defined for the array
#cars = Car.includes(:colors).all
#cars.colors #=> NoMethodError, color is not defined for Array
The following will work, because the iterator will have an instance of car
#cars.each do |car|
puts car.colors # => Will print an array of color objects
end
each_with_index will work as well, but it is a bit different, as the first object
is the same as the each loop car object, the second object is the index
#cars.each_with_index do |car, index|
puts car.colors # => Will print an array of color objects
puts #cars[index].colors # => Will print an array of color objects
puts car == #cars[index] # => will print true
end
I'm doing this:
#snippets = Snippet.find :all, :conditions => { :user_id => session[:user_id] }
#snippets.each do |snippet|
snippet.tags.each do |tag|
#tags.push tag
end
end
But if a snippets has the same tag two time, it'll push the object twice.
I want to do something like if #tags.in_object(tag)[...]
Would it be possible? Thanks!
I think there are 2 ways to go about it to get a faster result.
1) Add a condition to your find statement ( in MySQL DISTINCT ). This will return only unique result. DBs in general do much better jobs than regular code at getting results.
2) Instead if testing each time with include, why don't you do uniq after you populate your array.
here is example code
ar = []
data = []
#get some radom sample data
100.times do
data << ((rand*10).to_i)
end
# populate your result array
# 3 ways to do it.
# 1) you can modify your original array with
data.uniq!
# 2) you can populate another array with your unique data
# this doesn't modify your original array
ar.flatten << data.uniq
# 3) you can run a loop if you want to do some sort of additional processing
data.each do |i|
i = i.to_s + "some text" # do whatever you need here
ar << i
end
Depending on the situation you may use either.
But running include on each item in the loop is not the fastest thing IMHO
Good luck
Another way would be to simply concat the #tags and snippet.tags arrays and then strip it of duplicates.
#snippets.each do |snippet|
#tags.concat(snippet.tags)
end
#tags.uniq!
I'm assuming #tags is an Array instance.
Array#include? tests if an object is already included in an array. This uses the == operator, which in ActiveRecord tests for the same instance or another instance of the same type having the same id.
Alternatively, you may be able to use a Set instead of an Array. This will guarantee that no duplicates get added, but is unordered.
You can probably add a group to the query:
Snippet.find :all, :conditions => { :user_id => session[:user_id] }, :group => "tag.name"
Group will depend on how your tag data works, of course.
Or use uniq:
#tags << snippet.tags.uniq