Ruby array hash keys - ruby

Basically I'm working with a 2D matrix. I can access elements of the matrix by specifying an (x,y) pair to get the corresponding value at that position.
Now I also want to be able to keep track of certain pairs that are arbitrarily determined at run-time. For example, maybe I need to keep track of the values at (1,2), (3,4), and (5,6), and maybe I need to retrieve the value at that position frequently.
So I was thinking how about just make a hash .
liked_elements = {[1,2] => M[1,2], [3,4] =>M[3,4], [5,6]=>M[5,6]}
Or something like that.
Then I can quickly iterate over the hash and get the elements that I like.
Are there any issues with using arrays as hash keys?

Just don't modify the array afterward (or remember to rehash the hash if you do).

If it's truly a matrix (an array of arrays), then you can just pass in coordinates like this
matrix = [[:a, :b, :c],[:d, :e, :f], [:g, :h, :i]]
matrix[0][1] # returns :b
matrix[1][2] # returns :f
matrix[2][3] # returns nil, since 3 is out of bounds
Yes, you can create an array as a hash key.
h = Hash[[0,1], matrix[0][1]]
h[[0,1]] # returns :b

Related

How to get the index of a key in a hash?

I'm trying to get the index of a key in a hash.
I know how to do this in an array:
arr = ['Done', 13, 0.4, true]
a = arr.index('Done')
puts a
Is there a method or some sort of way to do this something like this with a key in a hash? Thanks!
Hashes aren't usually treated as ordered structures, they simply have a list of keys and values corresponding to those keys.
It's true that in Ruby hashes are technically ordered, but there's very rarely an actual use case for treating them as such.
If what you want to do is find the key corresponding to a value in a hash, you can simply use the Hash#key method:
hash = { a: 1, b: 2 }
hash.key(1) # => :a
I suppose you could use hash.keys.index(hash.key(1)) to get 0 since it's the first value, but again, I wouldn't advise doing this because it's not typical use of the data structure
There are at least a couple ways you can get this information, the 2 that come to mind are Enumerable's find_index method to pass each element to a block and check for your key:
hash.find_index { |key, _| key == 'Done' }
or you could get all the keys from your hash as an array and then look up the index as you've been doing:
hash.keys.index('Done')

Behavior of altered array keys in hashes

Ruby allows for a mutable object to be used as a hash key, and I was curious how this worked when the object is updated. It seems like the referenced object is irretrievable from key requests if it's updated.
key = [1,2]
test = {key => 12}
test # => {[1, 2] => 12}
test[key] # => 12
test[[1,2]] # => 12
test[[1,2,3]] # => nil
key << 3
test # => {[1, 2, 3] => 12}
test[key] # => nil
test[[1,2]] # => nil
test[[1,2,3]] # => nil
Why does this work this way? Why can't I provide a key to the hash which will return the value associated with the list I original used as a key?
According to the documentation:
Two objects refer to the same hash key when their hash value is identical and the two objects are eql? to each other.
Mutating a key doesn't change the hash it's stored under. After you mutate the key, trying to index with [1,2] matches the hash but not eql?, while [1,2,3] matches the eql? but isn't found by hash.
See this article for a more elaborate explanation.
You can rehash test, however, to recalculate the hashes based on current key values:
test.rehash
test[[1,2,3]] # => 12
class D
end
p D.new.methods.include?(:hash) #=> true
# so the D instance has a hash method. What does it do?
p D.new.hash #=> -332308361 # just some number
(Almost) every object in Ruby has a hash method. The Hash calls this method when the object is used as a key, and uses the resulting number to store and retrieve the key. (There are smart procedures to handle duplicate numbers (hash collisions)). Retrieving goes like this:
a_hash[[1,2,3]]
# the a_hash calls the hash method to the [1,2,3] object
# and checks if it has stored a value for the resulting number.
This number is only created once: when the key is added to the hash instance.
Problems arise when you start messing with the key after including it in a hash: the hashmethod of the object will differ from the one stored in the hash.
Don't do that, or
consider not using mutable objects as keys, or
remember to do a timely:
a_hash.rehash
which will recalculate all hash numbers.
Note: For strings keys, a copy is used for calculating the hash number, so modifying the original key won't matter.
It would be inconvenient if the identity of an array matters as the hash key. If you have a hash with a key [1, 2], you want to be able to access that with a different array object [1, 2] that has the same content. You want access by the content, not the identity. That would mean that what particular object (with the particular object id) is stored as a key does not matter for a hash. All that matters is the content of the key at the time it was assigned to the hash.
Therefore, after doing key << 3, it makes sense that test[key] or test[[1, 2, 3]] does not return the stored value anymore because key at the time of assignment to test was [1, 2].
The tricky thing is that test[[1, 2]] also returns nil. That is the limitation of Ruby.
If you want the hash to reflect the change made in the key objects, there is a method Hash#rehash.
test.rehash
test[key] # => 12
test[[1,2]] # => nil
test[[1,2,3]] # => 12

removing array elements by indexes from an array ruby

I have an array of words like so
["jo","jibber","hail","noobs","smirk","awkland"]
and a seperate array with indexes
[0,3,5]
I want to remove all the elements from the previous array at those indexes.
I was thinking i could use reject but im not sure exactly how i would do it. Also once i remove one element wont all the other indexes would have to change.
Is there an easy way of doing this??
You can use reject and with_index
arr = ["jo", "jibber", "hail", "noobs", "smirk", "awkland"]
indexes = [0, 3, 5]
arr.reject.with_index {|_, idx| indexes.include?(idx)} # => ["jibber", "hail", "smirk"]

Ruby: hash that doesn't remember key values

Is there a hash implementation around that doens't remember key values? I have to make a giant hash but I don't care what the keys are.
Edit:
Ruby's hash implementation stores the key's value. I would like hash that doesn't remember the key's value. It just uses the hash function to store your value and forgets the key. The reason for this is that I need to make a hash for about 5 gb of data and I don't care what the key values are after creating it. I only want to be able to look up the values based on other keys.
Edit Edit:
The language is kind of confusing. By key's value I mean this:
hsh['value'] = data
I don't care what 'value' is after the hash function stores data in the hash.
Edit^3:
Okay so here's what I am doing: I am generating every 35-letter (nucleotide) kmer for a set of multiple genes. Each gene has an ID. The hash looks like this:
kmers = { 'A...G' => [1, 5, 3], 'G...T' => [4, 9, 9, 3] }
So the hash key is the kmer, and the value is an array containing IDs for the gene(s)/string(s) that have that kmer.
I am querying the hash for kmers in another dataset to quickly find matching genes. I don't care what the hash keys are, I just need to get the array of numbers from a kmer.
>> kmers['A...G']
=> [1, 5, 3]
>> kmers.keys.first
=> "Sorry Dave, I can't do that"
I guess you want a set, allthough it stores unique keys and no values. It has the fast lookup time from a hash.
Set is included in the standard libtrary.
require 'set'
s = Set.new
s << 'aaa'
p s.merge(['ccc', 'ddd']) #=> #<Set: {"aaa", "ccc", "ddd"}>
Even if there was an oddball hash that just recorded existence (which is how I understand the question) you probably wouldn't want to use it, as the built-in Hash would be simpler, faster, not require a gem, etc. So just set...
h[k] = k
...and call it a day...
I assume the 5 gb string is a genome, and the kmers are 35 base pair nucleotide sequences.
What I'd probably do (slightly simplified) is:
human_genome = File.read("human_genome.txt")
human_kmers = Set.new
human_genome.each_cons(35) do |potential_kmer|
human_kmers << potential_kmer unless human_kmers.include?(potential_kmer)
end
unknown_gene = File.read("unknown_gene.txt")
related_to_humans = unknown_gene.each_cons(35).any? do |unknown_gene_kmer|
human_kmers.include?(unknown_gene_kmer)
end
I have to make a giant hash but I don't care what the keys are.
That is called an array. Just use an array. A hash without keys is not a hash at all and loses its value. If you don't need key-value lookup then you don't need a hash.
Use an Array. An Array indexes by integers instead of keys. http://www.ruby-doc.org/core/classes/Array.html
a = []
a << "hello"
puts a #=> ["hello"]

Ruby: Range is empty, but slicing with it produces elements

I'm learning Ruby and have just gotten into some stuff about arrays and ranges. I've run into something about slices that, while it makes sense at first glance, confuses me a bit when I look deeper into it.
IRB says that (2..-1).to_a is an empty array, meaning no values in the range, right?
But if I use that same range in [:a, :b, :c, :d, :e][2..-1], I get back [:c, :d, :e] rather than an empty array.
Now, I'm aware that -1 represents the last element of the array, so it kind of makes sense that what got picked, did. But if the range itself would be empty, how is it selecting anything?
This is a fascinating question. The answer is that it's not the individual elements of the range that are inspected when slicing the array, but the first and last elements. Specifically:
>> (2..-1).to_a
=> []
>> (2..-1).first
=> 2
>> (2..-1).last
=> -1
Thus the example works, since it slices the array from the [2] element to the [-1] element.
If you want a consistent way to think about this, consider that (2..-1).to_a outputs the integers found between 2 and -1 (of which there are none), but that [2..-1] means from the 2 index to the -1 index.
(Source: array.c and range.c in the Ruby source.)
And, the complicated bonus part: to get the meaning you were thinking about, you could use
>> [:a, :b, :c, :d, :e].values_at *(2..-1).to_a
=> []
In the slicing operation it's not seeing the range as a Range per se, it's just looking at the values of the endpoints, in this case 2 and -1. Since -1 has a special meaning (i.e last item in the list) it just returns everything from item at index 2 to the end of the list. Don't think of it as a Range here, just think of it as a convenient way of passing in two numbers (expressing two end points).
The Array#[] method does not use the range as a range (that is, it does not call include? on it or anything like that); it just takes the two numbers out of it and checks whether it's exclusive.
You can imagine it to look somewhat like this (though the real [] is implemented in C, not in Ruby, and of course also handles arguments other than ranges):
def [](range)
start = range.begin
length = range.end - start
length -= 1 if range.exclude_end?
slice(start, length)
end

Resources