Ahmad hamza
Ahmad hamza

Reputation: 1936

Can we write a hash without hash class?

For the sake of this question lets assume that we don't have a hash class in Ruby.

Is it possible to create a hash without using hash class in Ruby? Or probably if we don't have a hash concept, we may be calling it associative array.

Upvotes: 2

Views: 160

Answers (3)

Jörg W Mittag
Jörg W Mittag

Reputation: 369478

Is it possible to create a hash without using hash class in Ruby? Or probably if we don't have a hash concept, we may be calling it associative array.

Of course. Why wouldn't it be? The concept of a hash table is not tied to any particular language.

Also, Ruby is a Turing-complete language, which means that you can compute everything in Ruby which you can also compute in any other language.

Rubinius's Hash class is written in pure Ruby, and it obviously cannot use the Hash class, so clearly it is possible to write Hash in pure Ruby. And why wouldn't it be? YARV's Hash class is written in C, JRuby's Hash class is written in Java, IronRuby's in C♯, Topaz's in RPython, and if you want to write one in Ruby, you "just" have to write the same thing as those all did, except in Ruby instead of C, Java, C♯, or RPython.

(To be fair, Rubinius's Hash implementation uses Tuple, which is partially implemented in C++, but again, you just have to re-write that code in Ruby, et voilà, you have a Hash class in Ruby.)

Upvotes: 2

the Tin Man
the Tin Man

Reputation: 160551

While it's possible to use an Array to simulate the behavior of a Hash, performance is going to suffer:

require 'fruity'

CHARS = ('a' .. 'z').to_a
ARY = CHARS.zip(CHARS)
HASH = ARY.to_h

compare do 
  _assoc_a { ARY.assoc('a') }
  _hash_a  { HASH['a']      }
  _hash_m  { HASH['m']      }
  _assoc_m { ARY.assoc('m') }
  _assoc_z { ARY.assoc('z') }
  _hash_z  { HASH['z']      }
end
# >> Running each test 32768 times. Test will take about 3 seconds.
# >> _hash_m is similar to _hash_z (results differ: m vs z)
# >> _hash_z is similar to _hash_a (results differ: z vs a)
# >> _hash_a is faster than _assoc_a by 2x ± 1.0 (results differ: a vs ["a", "a"])
# >> _assoc_a is faster than _assoc_m by 5x ± 1.0 (results differ: ["a", "a"] vs ["m", "m"])
# >> _assoc_m is faster than _assoc_z by 2x ± 0.1 (results differ: ["m", "m"] vs ["z", "z"])

Doing reverse lookups, from the second element to the first, or from the value to the key:

compare do
  _rassoc_a      { ARY.rassoc('a')  }
  _hash_rassoc_a { HASH.rassoc('a') }
  _rassoc_m      { ARY.rassoc('m')  }
  _hash_rassoc_m { HASH.rassoc('m') }
  _rassoc_z      { ARY.rassoc('z')  }
  _hash_rassoc_z { HASH.rassoc('z') }
end
# >> Running each test 32768 times. Test will take about 4 seconds.
# >> _rassoc_a is faster than _hash_rassoc_a by 2x ± 1.0
# >> _hash_rassoc_a is faster than _rassoc_m by 3x ± 1.0 (results differ: ["a", "a"] vs ["m", "m"])
# >> _rassoc_m is similar to _hash_rassoc_m
# >> _hash_rassoc_m is similar to _rassoc_z (results differ: ["m", "m"] vs ["z", "z"])
# >> _rassoc_z is similar to _hash_rassoc_z

It makes sense to me that a reverse lookup of the hash to the key would be slower, since a hash isn't optimized for such things. If I need to do that I try to create a separate Hash reversing the keys and values then use that to do reverse lookups:

hash = {'a' => 1, 'b' => 2}
reversed_hash = hash.invert # => {1=>"a", 2=>"b"}

This breaks if the value is an array or the values are not unique, and sometimes the resulting key isn't very convenient to use.

Upvotes: 2

Radmation
Radmation

Reputation: 1534

Sure you can.

If you don't want to go through the trouble of recreating your own hash class you can use the Array class's assoc and rassoc methods. See "Associative arrays in Ruby...what?".

Examples:

picks = [
 ["AAPL", "buy"],
 ["GOOG", "sell"],
 ["MSFT", "sell"]
]

print picks.assoc("AAPL")
puts #new line 
print picks.rassoc("sell") #gets the first row that contains

Output:

["AAPL", "buy"]
["GOOG", "sell"]

Upvotes: 1

Related Questions