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 %w to 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 %i to 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 Array or Hash when 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 nils
    
  • When accessing the first or last element from an array, prefer first or last over [0] or [-1]. [link]

  • Use Set instead of Array when dealing with unique elements. Set implements a collection of unordered values with no duplicates. This is a hybrid of Array's intuitive inter-operation facilities and Hash'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 of Hash#has_key? and Hash#value? instead of Hash#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#fetch when 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#fetch as 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 } # => false
    
  • Prefer 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_at when 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 [] on nil. [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 nil before 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