check if a list of object is blank in rails - ruby

lets say I have this
#objects = SampleObject.all
then I want to check if #objects is blank, I could the ff:
unless #objects.blank?
#objects.each do |object|
end
else
..
end
however, doing so will trigger rails to execute a SELECT count(*) query
so instead, I could do something like
unless #objects.length > 0
is there a way to override the .blank? given a particular class?
say
def self.empty?
self.length > 0 ? false : true <br>
end

You should use ActiveRecord::Relation#any? method:
if #objects.any?
# ...
end
which is (in this case) negation of ActiveRecord::Relation#empty? method:
unless #objects.empty?
# ...
end

blank? uses empty?, since blank? source code:
# File activesupport/lib/active_support/core_ext/object/blank.rb, line 13
def blank?
respond_to?(:empty?) ? empty? : !self
end
Now the docs about empty? says:
Returns true if the collection is empty.
If the collection has been
loaded or the :counter_sql option is provided, it is equivalent to
collection.size.zero?.
If the collection has not been loaded, it is
equivalent to collection.exists?.
If the collection has not already
been loaded and you are going to fetch the records anyway it is better
to check collection.length.zero?
So, it really depends weather the collection is loaded or not?
Both empty? & any? use SELEC COUNT(*) if the collection isn't loaded (reference), i think in your case SampleObject.all will be lazy loaded as #Marek said, thus the COUNT calls.
For your case i don't think you can avoid the COUNT call since you want to fetch all records and eager loading all records just to avoid a second call to db just feels pointless (solving one performance issue by causing bigger one), however if its a subset collection, i believe there will be no second COUNT call.

SampleObject.all gives ActiveRecord::Relation object, you can use blank? also.
blank? - Returns true if relation is blank
Thus you can write as
unless #objects.blank?
# ...
end

You can call ActiveRecord::Relation#to_a to execute the query immediately:
#objects = SampleObject.all.to_a # runs the query, returns an array
if #objects.any? # no query, this is Enumerable#any?
# ...
end

Related

Why isn't my print_linked_list_in_reverse function working?

One challenge in a Ruby course I'm doing is to print the :data values of the following linked list, in reverse:
{:data=>3, :next=>{:data=>2, :next=>{:data=>1, :next=>nil}}}
So when my method is passed the above code, it should return
1
2
3
Here's my attempt, which doesn't work for the above code. I can't figure out why, and I'd appreciate it if someone could explain what I'm doing wrong:
def print_list_in_reverse(hash)
if hash[:next].nil? #i.e. is this the final list element?
print "#{hash[:data]}\n"
return true
else
#as I understand it, the next line should run the method on `hash[:next]` as well as checking if it returns true.
print "#{hash[:data]}\n" if print_list_in_reverse(hash[:next])
end
end
Here's a solution, in case it helps you spot my mistake.
def print_list_in_reverse(list)
return unless list
print_list_in_reverse list[:next]
puts list[:data]
end
Thank you.
Your solution relies on return values, and you don't explicitly provide one in your else clause. In fact, you implicitly do because Ruby returns the result of the last statement evaluated, which for a print statement is nil. In Ruby false and nil are both logically false, causing the print to get bypassed for all but the last two calls. Your choices are to add a true at the end of the else, or make a solution that doesn't rely on return values.
To negate the need for return values, just check what logic is kosher based on info in the current invocation. You can simplify your life by leveraging the "truthiness" non-nil objects. Your basic recursive logic to get things in reverse is "print the stuff from the rest of my list, then print my stuff." A straightforward implementation based on truthiness would be:
def print_list_in_reverse(hash)
print_list_in_reverse(hash[:next]) if hash[:next]
print "#{hash[:data]}\n"
end
The problem with that is that you might have been handed an empty list, in which case you don't want to print anything. That's easy to check:
def print_list_in_reverse(hash)
print_list_in_reverse(hash[:next]) if hash[:next]
print "#{hash[:data]}\n" if hash
end
That will work as long as you get handed a hash, even if it's empty. If you're paranoid about being handed a nil:
def print_list_in_reverse(hash)
print_list_in_reverse(hash[:next]) if hash && hash[:next]
print "#{hash[:data]}\n" if hash
end
The other alternative is to start by checking if the current list element is nil and returning immediately in that case. Otherwise, follow the basic recursive logic outlined above. That results in the solution you provided.
Better to iterate over every value in your hash, and push the values until there's no any other hash as value inside the main hash.
def print_list_in_reverse(hash, results = [])
hash.each_value do |value|
if value.is_a? Hash
print_list_in_reverse(value, results)
else
results << value unless value.nil?
end
end
results.reverse
end
p print_list_in_reverse(data)
=> [1, 2, 3]
The problem in your code is in the else-case. You need to return true to print the hash[:data].
Your method always print the last 2 elements.

returning array without specific elements in ruby

I looked really hard in http://www.ruby-doc.org/core-2.1.2/Array.html but I couldn't find a quick functionality to this behaviour:
arr = [1,2,3,4,5,6]
arr.without(3,6) #=> [1,2,4,5]
I know I can write my own function/monkey-patch ruby/add a class method/write it in a few lines.
Is there a way to do this in a ruby way?
you can use subtraction :
arr - [3,6]
EDIT
if you really wanted you could alias this method
class Array
alias except -
end
then you can use:
arr.except [3,6]
This got added in Rails 5 :)
https://github.com/rails/rails/issues/19082
module Enumerable
def without(*elements)
reject { |element| element.in?(elements) }
end
end
it's just aesthetics, but it makes sense for the brain
There is another way using reject. But it is not cleaner than -
arr.reject{|x| [3,6].include? x}
Just in case anyone else comes across this and is looking for a way to delete elements from an array based on a conditional: you can use delete_if.
For example, I have a list of customers and want to merge any customers that have duplicate emails. After doing a query to get a list of all emails with the total count number for each email, I can then delete all of them that only appear once:
emails = Customer.select('count(email) as num_emails, email').group('email')
emails.delete_if { |email| email.num_emails.to_i == 1 }
The end result is I have a list of all customer emails that appear multiple times in the database.

Using an Enumerator instead of loops

I usually use loop as below to request data form an external API or DB (redis pop):
records = []
loop do
record = MyHandler.new(token).fetch
break unless record
records.push(record)
end
It works, but to make it look better, I wonder whether there is any way to use an Enumerator. Does anyone know one?
Wrapping your code in an Enumerator is quite easy:
record_enumerator = Enumerator.new do |y|
loop do
record = MyHandler.new(token).fetch
break unless record
y << record
end
end
You can now iterate over the records using a block:
record_enumerator.each { |record|
# do something with record
}
Or fetch all records with:
records = record_enumerator.to_a
If MyHandler is your class, you could implement MyHandler#each instead and include Enumerable.
How about this?
while(record = MyHandler.new(token).fetch)
records.push(record)
end
That gets rid of the ugly loop/break logic.
Alternatively, you could create an ennumerable object instead of using the records array:
class RecordList
include Enumerable
attr :token
def initialize(token)
#token = token
end
def each
MyHandler.new(token).fetch
end
end
records = RecordList.new token
You can use while loops in ruby like so:
records = []
record = MyHandler.new(token).fetch
while record do
records << record
record = MyHandler.new(token).fetch
end
There might be an even more elegent way, but from the information provided, that's the best I can come up with.
If you are looking for the really short syntax, here you go:
records << record while record = MyHandler.new(token).fetch
It will work like a charm, the condition check is done before executing the loop body, hence record is always initialized.

Is there an implicit keyword in this Ruby Array map code?

Is there a keyword I can use to explicitly tell the map function what the result of that particular iteration should be?
Consider:
a = [1,2,3,4,5]
a.map do |element|
element.to_s
end
In the above example element.to_s is implicitly the result of each iteration.
There are some situations where I don't want to rely on using the last executed line as the result, I would prefer to explicitly say what the result is in code.
For example,
a = [1,2,3,4,5]
a.map do |element|
if some_condition
element.to_s
else
element.to_f
end
end
Might be easier for me to read if it was written like:
a = [1,2,3,4,5]
a.map do |element|
if some_condition
result_is element.to_s
else
result_is element.to_f
end
end
So is there a keyword I can use in place of result_is?
return will return from the calling function, and break will stop the iteration early, so neither of those is what I'm looking for.
The last thing left on the stack is automatically the result of a block being called. You're correct that return would not have the desired effect here, but overlook another possibility: Declaring a separate function to evaluate the entries.
For example, a reworking of your code:
def function(element)
if (some_condition)
return element.to_s
end
element.to_f
end
a.map do |element|
function(element)
end
There is a nominal amount of overhead on calling the function, but on small lists it should not be an issue. If this is highly performance sensitive, you will want to do it the hard way.
Yes, there is, it's called next. However, using next in this particular case will not improve readability. On the contrary, it will a) confuse the reader and b) give him the impression that the author of that code doesn't understand Ruby.
The fact that everything is an expression in Ruby (there are no statements) and that every expression evaluates to the value of the last sub-expression in that expression are fundamental Ruby knowledge.
Just like return, next should only be used when you want to "return" from the middle of a block. Usually, you only use it as a guard clause.
The nature of map is to assign the last executed line to the array. Your last example is very similar to the following, which follows the expected behavior:
a = [1,2,3,4,5]
a.map do |element|
result = if some_condition
element.to_s
else
element.to_f
end
result
end
No, there is no language keyword in ruby you can use to determine the result mapped into the resulting array before executing other code within the iteration.
You may assign a variable which you then return when some other code has been executed:
a.map do |element|
result = some_condition ? element.to_s : element.to_f
#do something else with element
result
end
Keep in mind the reason for ruby not providing a keyword for this kind of code is that these patterns tend to have a really low readability.

Find Value in Array of ActiveRecord Objects vs. Find Value from Repeated DB Calls

I would just like to return true, if my Array of Contact(s) (model) contains a Contact with id equal to some value. For example:
#contacts = Contact.all
#someval = "alskjdf"
find_val(#contacts, #someval)
def find_val(contacts, val)
#contact.each do |c|
if c.id == val
return true
end
end
return false
end
I have to do this repeatedly in my app (at most about 100 times for this particular actions), in order to exclude some data from an external API that has a list of contacts. Is this going to be too expensive?
I thought I might be able to do something faster, similar to ActiveRecord find on the array after it's been pulled down from the db, but can't figure that out. Would it be too expensive to call ActiveRecord like this?
Contacts.find_by_id(#someval)
The above line would have to be called hundreds of times... I figure iterating through the array would be less expensive. Thanks!
The best approach would be to index the contacts in a hash, using the contact id as the key after you retrieve them all from the db:
contacts = Contact.all.inject({}) {|hash, contact| hash[contact.id] = contact; hash }
Then you can easily get a contact with contact[id] in a performant way.
One way to reduce the amount of code you have to write to search the array is to open the array class and make a custom instance method:
class Array
def haz_value?(someval)
if self.first.respond_to? :id
self.select { |contact| contact.id == someval }.length > 0
else
false
end
end
end
Then, you can call #contacts.haz_value? #someval. In terms of efficiency, I haven't done a comparison, but both ways use Array's built-in iterators. It would probably be faster to create a stored procedure in your database and call it through ActiveRecord, here is how you can do that.

Resources