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
Related
I am running in issue, where I used following code.
def replace_variables(url, project)
variables = define_variables(project)
variables.each do |key, value|
url.gsub!(key, value_encode(value))
end
url
end
And issue was coming due to gsub! usage it was going up and effecting all data, where I just wanted to change data here. so I came to following solution but it is not good, any other way to do it. As it is too much variable definitions. This is working fine, but seem extra variable definitions!
def replace_variables(url, project)
variables = define_variables(project)
temp_url = url
variables.each do |key, value|
temp_url = temp_url.gsub(key, value_encode(value))
end
temp_url
end
More explaination Code creating some variables and replacing it from url and change values of it, but I have array of forms and if this runs first it changes those forms value too! which I don't want. The above code working fine but look bad as I am defining accumulator variable and iteration, is there any other way to do it elegantly. I have following define method
def define_variables(project)
{
'TODAYS_DATE' => Date.current.to_s,
'USER_NAME' => username,
'PROJECT_NAME' => project.name || '',
}
end
If #define_variables returns a key/value hash then something like this should work.
def replace_variables(url, project)
define_variables(project).inject(url) do |memo, (key, value)|
memo.gsub(key, value_encode(value))
end
end
So in my past application, I was somewhat familiar with using .includes in Rails, but for some reason I'm having a bit of a difficult time in my current scenario.
Here's what I'm working with:
# If non-existent, create. Otherwise, update.
existing_data = Page.all
updated_data = {}
new_records = []
#latest_page_data.each do |key, value|
existing_record = existing_data.find_by(symbol: key)
if existing_record != nil
updated_data[existing_record.id] = value
else
new_records << Page.new(value)
end
end
if !new_records.empty?
Page.import new_reocrds
end
if !updated_data.empty?
Page.update(updated_data.keys, updated_data.values)
end
end
The problem that I'm having is that the .find_by portion of the code results in a query every single iteration of #latest_page_data. I guess I would think that existing_data would hold all of the data it needs in memory, but obviously it doesn't work that way.
So next, I tried something like this:
# If non-existent, create. Otherwise, update.
existing_data = Page.includes(:id, :symbol)
updated_data = {}
new_records = []
#latest_currency_data.each do |key, value|
existing_record = existing_data.find_by(symbol: key)
but then rails throws an error, stating:
ActiveRecord::AssociationNotFoundError (Association named 'id' was not
found on Page; perhaps you misspelled it?):
so I can't use this example to find the id and symbol attributes.
I tried to take out :id in the Page.includes method, but I need to be able to get to the ID attribute in order to update the respective record later down in the code.
I've also saw some other posts pertaining to this topic, but I think the problem I may be running into is I'm not dealing with associations (and I believe that's what .includes is for? If this is the case, is there any other way that I can reduce all of the queries that I'm submitting here?
The includes method is used to preload associated models. I think what you are looking for is a select. Modifying your code to use select, do this :
existing_data = Page.select(:id, :symbol).load
updated_data = {}
new_records = []
#latest_currency_data.each do |key, value|
existing_record = existing_data.find_by(symbol: key)
if existing_record
updated_data[existing_record.id] = value
else
new_records << Page.new(value)
end
end
The drawbacks of using select over pluck is that since Rails constructs an object for you, so it is slower than a pluck. Benchmark: pluck vs select
Rather than trying to figure out a way to do it in Rails (since I'm not familiar with the 100% correct/accurate Rails way), I just decided to use .pluck and convert it into a hash to get the data that I'm looking for:
existing_data = Page.pluck(:id, :symbol)
existing_data = Hash[*existing_data.flatten]
updated_data = {}
new_records = []
#latest_currency_data.each do |key, value|
if existing_data.values.include? key
id = existing_data.find{|k,v| v.include? key}[0]
updated_data[id] = value
else
new_records << Page.new(value)
end
end
If anyone has a better way, it'd be gladly appreciated. Thanks!
Let's say that i have object my_obj
and i get this object from database, with some more calculation, it doesn't matter...
and then i need to iterate via such object, like:
my_obj.each do |m|
some_method(m.id)
end
this code is not good, becouse if my_obj is nil, i will get error like:
undefined method `each' for nil:NilClass
so i decide to write:
if my_obj.present?
my_obj.each do |m|
some_method(m.id)
end
end
but i think that there is one more way of doing this, without writting everywhere if construction.
so how could i iterate via object, only if it is not null?
I found the code a bit anti-pattern for normal OOP principle "Tell, Don't ask". I tried the question in console and found your worry unnecessary.
No matter what the result is, blank Array or blank ActiveRecord::Relation object, each all works and return a blank array [].
Article.count
# => 0
articles = Article.all # Return array in Rails 3
articles.each { |a| puts a.title }
# => []
articles = Article.scoped # Return ActiveRecord::Relation object in Rails 3
articles.each { |a| puts a.title }
# => []
I would suggest you to review the method and returned result of your query. If your query returns unusual things, make sure it returns at least a blank Array. Then you don't need to consider too much.
They easiest way to handle this is to surround your object with the 'Array' conversion function which will coerce your possibly nil input into an array while leaving an existing array untouched, e.g.
>> Array(nil)
=> []
and
>> Array([1])
=> [1]
So in your case:
Array(my_obj).each do |m|
some_method(m.id)
end
The collection query should always return iterable object, but there are several ways. The problem of nil checks led to a pattern named NullObjects which is often the best solution. Apart from that you can do:
my_object.to_a.each do |m|
some_method(m.id)
end
or
my_object.try(:each) do |m|
some_method(m.id)
end
or
(my_object || []).each do |m|
some_method(m.id)
end
In Rails, you can do Object#try method:
my_object.try(:each) do |m|
some_method(m.id)
end
It will call each (returning its result) with attached block if my_object is other than nil. Otherwise, it won't call each method and will return nil.
You can initialize like this;
def fields_each(fields)
if fields.present? && fields.keys.present?
fields.each do |key, value|
yield(key, value) if block_given?
end
end
end
and usage;
fields_each({a: 1, b: 2, c: 3}) do |key, value|
puts "Key: #{key} value: #{value}"
end
Everyone forgot the awesome [my_objects].flatten.compact!
in the newer ruby version, tested with 2.4.1, this can be written as
my_obj&.each do |m|
some_method(m.id)
end
when you initialize the my_obj do it like this
my_obj = Model.all || [] # Empty array will not iterate or throw error
Ex:
[].each do |x|
p "I will not execute"
end
you can try this above the loop:
if my_obj == nil then
redirect_to "something_else"
end
#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
I'm working on a rake task which imports from a JSON feed into an ActiveRecord called Person.
Person has quite a few attributes and rather than write lines of code for setting each attribute I'm trying different methods.
The closest I've got is shown below. This works nicely as far as outputing to screen but when I check the values have actually been set on the ActiveRecord itself it's always nil.
So it looks like I can't use .to_sym to solve my problem?
Any suggestions?
I should also mention that I'm just starting out with Ruby, have been doing quite a bit of Objective-c and now need to embrace the Interwebs :)
http = Net::HTTP.new(url.host, url.port)
http.read_timeout = 30
json = http.get(url.to_s).body
parsed = JSON.parse(json)
if parsed.has_key? 'code'
updatePerson = Person.find_or_initialize_by_code(parsed['code'])
puts updatePerson.code
parsed.each do |key, value|
puts "#{key} is #{value}"
symkey = key.to_sym
updatePerson[:symkey] = value.to_s
updatePerson.save
puts "#{key}....." # shows the current key
puts updatePerson[:symkey] # shows the correct value
puts updatePerson.first_name # a sample key, it's returning nil
end
You're probably looking for update_attributes():
if parsed.has_key?('code')
code = parsed.delete('code')
person = Person.find_or_initialize_by_code(code)
if person.update_attributes(parsed)
puts "#{person.first_name} successfully saved"
else
puts "Failed to save #{person.first_name}"
end
end
Your code can not assign any attribute, because you are always assigning to the single attribute named "symkey":
symkey = key.to_sym
updatePerson[:symkey] = value.to_s # assigns to attribute "symkey", not to the attribute with the name stored in variable symkey
If you want to make key into a symbol (which is probably not even necessary) and then use that as an index to access the attribute in updatePerson, you can write:
updatePerson[key.to_sym] = value.to_s
updatePerson.save
But this - more or less - is the same as
updatePerson.updateAttribute(key.to_sym, value.to_s) # update and save
except that no validation is triggered, so use with care.
And performancewise it might not be such a good idea to save the person after each assignment, so maybe you want to defer the .save() call until after you have assigned all attributes.
Nevertheless, updateAttributes(...) is something you might want to be looking into - if you do, do not forget to inform yourself on attr_protected or attr_accessible, as they protect attributes from "bulk assignment"
You can use write_attribute:
parsed.each do |key, value|
updatePerson.write_attribute(key, value)
end
updatePerson.save