Collections
Prefer literal array and hash creation notation (unless you need to pass parameters to their constructors, that is). [link]
# bad arr = Array.new hash = Hash.new # good arr = [ ] hash = { }Prefer
%wto the literal array syntax when you need an array of words (non-empty strings without spaces and special characters in them). [link]# bad STATES = ["draft", "open", "closed"] # good STATES = %w[draft open closed]Prefer
%ito the literal array syntax when you need an array of symbols (and you don't need to maintain Ruby 1.9 compatibility). [link]# bad STATES = [:draft, :open, :closed] # good STATES = %i[draft open closed]Avoid comma after the last item of an
ArrayorHashwhen the items are not on separate lines. [link]# good: easier to move/add/remove items VALUES = [ 1001, 2020, 3333, ] # bad VALUES = [1001, 2020, 3333, ] # good VALUES = [1001, 2020, 3333]Avoid the creation of huge gaps in arrays. [link]
arr = [] arr[100] = 1 # now you have an array with lots of nilsWhen accessing the first or last element from an array, prefer
firstorlastover[0]or[-1]. [link]Use
Setinstead ofArraywhen dealing with unique elements.Setimplements a collection of unordered values with no duplicates. This is a hybrid ofArray's intuitive inter-operation facilities andHash's fast lookup. [link]Prefer symbols instead of strings as hash keys. [link]
# bad hash = {"one" => 1, "two" => 2, "three" => 3} # good hash = {one: 1, two: 2, three: 3}Avoid the use of mutable objects as hash keys. [link]
Use the Ruby 1.9 hash literal syntax when your hash keys are symbols. [link]
# bad hash = {:one => 1, :two => 2, :three => 3} # good hash = {one: 1, two: 2, three: 3}Don't mix the Ruby 1.9 hash syntax with hash rockets in the same hash literal. When you've got keys that are not symbols stick to the hash rockets syntax. [link]
# bad {a: 1, "b" => 2} # good {:a => 1, "b" => 2}Use
Hash#key?instead ofHash#has_key?andHash#value?instead ofHash#has_value?. As noted here by Matz, the longer forms are considered deprecated. [link]# bad hash.has_key?(:test) hash.has_value?(value) # good hash.key?(:test) # or hash.include?(:test) hash.value?(value)Use
Hash#fetchwhen dealing with hash keys that should be present. [link]heroes = {batman: "Bruce Wayne", superman: "Clark Kent"} # bad: if we make a mistake we might not spot it right away heroes[:batman] # => "Bruce Wayne" heroes[:supermann] # => nil # good: fetch raises a KeyError making the problem obvious heroes.fetch(:supermann)Introduce default values for hash keys via
Hash#fetchas opposed to using custom logic. [link]batman = {name: "Bruce Wayne", is_evil: false} # bad: if we just use || operator with falsy value we won't get the expected result batman[:is_evil] || true # => true # good: fetch work correctly with falsy values batman.fetch(:is_evil) { true } # => falsePrefer the use of the block instead of the default value in
Hash#fetch. [link]batman = {name: "Bruce Wayne"} # bad: if we use the default value, we eager evaluate it # so it can slow the program down if done multiple times batman.fetch(:powers, obtain_batman_powers) # obtain_batman_powers is an expensive call # good: blocks are lazy evaluated, so only triggered in case of KeyError exception batman.fetch(:powers) { obtain_batman_powers }Use
Hash#values_atwhen you need to retrieve several values consecutively from a hash. [link]# bad email = data["email"] username = data["nickname"] # good email, username = data.values_at("email", "nickname")Rely on the fact that as of Ruby 1.9 hashes are ordered. [link]
Do not modify a collection while traversing it. [link]
When accessing elements of a collection, avoid direct access via
[n]by using an alternate form of the reader method if it is supplied. This guards you from calling[]onnil. [link]# bad Regexp.last_match[1] # good Regexp.last_match(1)When providing an accessor for a collection, provide an alternate form to save users from checking for
nilbefore accessing an element in the collection. [link]# bad def awesome_things @awesome_things end # good def awesome_things(index = nil) if index && @awesome_things @awesome_things[index] else @awesome_things end end