Reputation: 1936
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
Reputation: 369478
Is it possible to create a
hash
without usinghash class
in Ruby? Or probably if we don't have a hash concept, we may be calling itassociative 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
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
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