Tuesday, June 16, 2015

RubyKoan: test_creating_hashes

This Koan is to get you to understand a Hash.

  def test_creating_hashes
    empty_hash = Hash.new
    assert_equal Hash, empty_hash.class
    assert_equal({}, empty_hash)
    assert_equal 0, empty_hash.size
  end

Hash.new and {} creates an empty hash.
Curly brackets {} encapsulate a hash.
When you create a hash this way, by default is has no size


  def test_hash_literals
    hash = { :one => "uno", :two => "dos" }
    assert_equal 2, hash.size
  end

Creates and add values into the hash.


  def test_accessing_hashes
    hash = { :one => "uno", :two => "dos" }
    assert_equal "uno", hash[:one]
    assert_equal "dos", hash[:two]
    assert_equal nil, hash[:doesnt_exist]
  end

Creates a hash, adds two key, value pairs.
Calls asserts that verify the data is inserted in the correct place.


  def test_accessing_hashes_with_fetch
    hash = { :one => "uno" }
    assert_equal "uno", hash.fetch(:one)
    assert_raise (IndexError) do
      hash.fetch(:doesnt_exist)
  end

This one is the same as above except is uses fetch to call the value of :one
It repeats this for an IndexError to be raised for a fetch that doesn't exist.

    # THINK ABOUT IT:
    #
    # Why might you want to use #fetch instead of #[ ] when accessing hash keys?
    # Using fetch can raise an error while using #[ ] would return nil if there was no key found.

  def test_changing_hashes
    hash = { :one => "uno", :two => "dos" }
    hash[:one] = "eins"

    expected = { :one => "eins", :two => "dos" }
    assert_equal expected, hash

    # Bonus Question: Why was "expected" broken out into a variable
    # rather than used as a literal?
    # Good question! I don't know, someone please comment and explain.
  end

  def test_hash_is_unordered
    hash1 = { :one => "uno", :two => "dos" }
    hash2 = { :two => "dos", :one => "uno" }

    assert_equal true, hash1 == hash2
  end

This is to test that hashes do not keep an order.  If they have the same key, value pairs then they are equal.  This means you can move things around inside a hash, they don't have to be neatly organized in any order as long as they remain in their key, value pairs.


  def test_hash_keys
    hash = { :one => "uno", :two => "dos" }
    assert_equal 2, hash.keys.size
    assert_equal true, hash.keys.include?(:one)
    assert_equal true, hash.keys.include?(:two)
    assert_equal Array, hash.keys.class
  end

  def test_hash_values
    hash = { :one => "uno", :two => "dos" }
    assert_equal 2, hash.values.size
    assert_equal true, hash.values.include?("uno")
    assert_equal true, hash.values.include?("dos")
    assert_equal Array, hash.values.class
  end

The first test, tests for the keys being where they should be and the second test is for the values.

  def test_combining_hashes
    hash = { "jim" => 53, "amy" => 20, "dan" => 23 }
    new_hash = hash.merge({ "jim" => 54, "jenny" => 26 })

    assert_equal true, hash != new_hash

    expected = { "jim" => 54, "amy" => 20, "dan" => 23, "jenny" => 26 }
    assert_equal true, expected == new_hash
  end

This test merge.  When you call merge, any new hashes are added.
Any hash that matches a key of the previous hash will have its value over written by the merge.


  def test_default_value
    hash1 = Hash.new
    hash1[:one] = 1

    assert_equal 1, hash1[:one]
    assert_equal nil, hash1[:two]

    hash2 = Hash.new("dos")
    hash2[:one] = 1

    assert_equal 1, hash2[:one]
    assert_equal "dos", hash2[:two]
  end

The default value for a new hash is nil.  When you create a hash and pass a value, then all keys will have that default value.  You can over write it.  Useful if you have many hashes with the same value.


  def test_default_value_is_the_same_object
    hash = Hash.new([])

    hash[:one] << "uno"
    hash[:two] << "dos"

    assert_equal ["uno", "dos"], hash[:one]
    assert_equal ["uno", "dos"], hash[:two]
    assert_equal ["uno", "dos"], hash[:three]

    assert_equal true, hash[:one].object_id == hash[:two].object_id

  end

When you create a hash using hash = Hash.new([ ]) you create a Hash with a default value that is the same for all key instances. So when you pass a key that doesn't exist, you get the same default value, in this case an array. In this case, you get the array created inside the method ["uno", "dos"]. If you push(<<) "tres", then each hash([:one], [:two], [:three] will have the value of the entire array ["uno", "dos", "tres"].


  def test_default_value_with_block
    hash = Hash.new { |hash, key| hash[key] = [] }

    hash[:one] << "uno"
    hash[:two] << "dos"

    assert_equal ["uno"], hash[:one]
    assert_equal ["dos"], hash[:two]
    assert_equal [], hash[:three]
  end

In the last test, each key is being assigned the default value, which is its own array. They are no longer referencing to the same object/array, which is why the array doesn't appear to grow like in the previous example.


Brought to you by KENYACODE. Can you code?