Benchmarking Arrays Full of Nils
2.17.2019
What is the fastest way in Ruby to check if all values of an array are nil?
I had this question recently when writing some display logic. If the record didn't have any of the desired values available, it would not be shown, but if it had at least one, it would. The gist of what I was trying to accomplish was this:
class SomePresenterClass
def show_info?
# The object has 10 values, attr1 through attr10.
# If one is non-nil, return true
# else return false
end
end
# show.slim
- if @some_presenter_instance.show_info?
# show some cool stuff
- else
# N/A
There were three candidates I could think of.
array = [attr1, attr2, attr3, etc... attr10]
# Option 1
# Iterate thought each item, and only return true if all are nil.
def show_info?
array.all? { |a| a.nil? }
end
# Option 2
# Remove all items with .compact, and see if there's anything left in the array.
def show_info?
arr.compact.empty?
end
# Option 3
# Includes an implicit { |a| a } block, returns true if any of the items is not nil or false
def show_info?
arr.any?
end
I thought I was very clever with that compact one, but when we run the benchmarks, there is a clear winner.
require "benchmark"
arr = [nil] * 10000
n = 5000
Benchmark.bmbm do |x|
x.report("compact") { n.times do; arr.compact.empty?; end }
x.report("all?") { n.times do; arr.all?(&:nil?); end }
x.report("any?") { n.times do; arr.any?; end }
end
user system total real
all? 1.943716 0.003502 1.947218 ( 1.953412)
compact 0.065184 0.000185 0.065369 ( 0.065653)
any? 0.024278 0.000057 0.024335 ( 0.024408)
In a landslide, .any?
was the fastest!
I have a couple of caveats. First, looking at any array of 10,000 nils is not a super realistic scenario (I don't think??), at least for what I was doing. Second, if you did want to return true if one of the values was false
, you'd need to go with .compact
instead. And finally, all of this ignores the fact that there is a database involved! This is all moot if you hit Postgres a million times. Although perhaps there's a way to get at this with one big elegant SQL query.
Do you have any other clever (and faster) ways to approach this? Email me!
Other Posts
The Best Projects Can Be Done in a Weekend
Everyone Has Something To Offer
Book Thoughts: Capital and Ideology
Naive Diffie-Hellman Implementation in Ruby
When Does the Magic Comment Work, and When Does it Not?
Go, and When It's Okay to Learn New Things
Grouping Records by Month with Ruby
Add Timestamps to Existing Tables in Rails
The Busy and (Somewhat) Fit Developer
TuxedoCSS and the Rails Asset Pipeline
Gem You Should Know About: auto_html
Querying for Today's Date with ActiveRecord