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

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')

Related

Convert array of hashes to single hash with values as keys

Given a source array of hashes:
[{:country=>'england', :cost=>12.34}, {:country=>'scotland', :cost=>56.78}]
Is there a neat Ruby one-liner for converting it to a single hash, where the values for the :country key in the original hash (guaranteed to be unique) become keys in the new hash?
{:england=>12.34, :scotland=>56.78}
This should do what you want
countries.each_with_object({}) { |country, h| h[country[:country].to_sym] = country[:cost] }
=> {:england=>12.34, :scotland=>56.78}
You can do that using Enumerable#inject:
countries.inject({}) { |hsh, element| hsh.merge!(element[:country].to_sym => element[:cost]) }
=> {:england=>12.34, :scotland=>56.78}
We initialise the accumulator as {}, and then we iterate over each of the elements of the initial array and add the new formatted element to the accumulator.
One point to add is that using hsh.merge or hsh.merge! would have the same effect for the output, given that inject will set the accumulator hsh as the return value from the block. However, using merge! is better when it comes to memory usage, given that merge will always generate a new Hash, whereas merge! will apply the merge over the same existing Hash.
One more possible solution is:
countries.map(&:values).to_h
=> {"england"=>12.34, "scotland"=>56.78}

How symbols equal each others?

When I do :symbol == :symbol I find that its true. They are the same.
If this is the case, how can we create arrays like this:
a = [{:name=>"Michael"},{:name=>"John"}]
Look the below code :
a = [{:name=>"Michael"},{:name=>"John"}]
a.map(&:object_id) # => [70992070, 70992050]
This is because a is an array of Hash, but they are 2 different hash objects. In Ruby, Hash must have uniq key. But 2 different hash can have same named symbols as keys.
You seem to be confused about hash keys. One hash cannot contain the same key twice, but two different hashes can have the same object as a key. For example:
a_key = "hello"
spanish = { a_key => "hola" }
french = { a_key => "bonjour" }
some_array = [spanish, french]
On top of that, it is possible for arrays to contain duplicate objects (e.g. [1, 2, 1] is valid) -- but these aren't even duplicates. Two hashes that contain the same key are still different objects.
There's nothing at all unusual about an array like that. In fact, it's normal for hashes in an array to have keys in common, because usually if you want to put things in an array, it means they have something in common that you can use to deal with them in the same way.

How to save an array of information coming from a hash in Ruby

I am new to ruby and don't have much experience with hashes, I have a variable named tweets and it is a hash as such:
{"statuses"=>[{"metadata"=>{"result_type"=>"recent", "iso_language_code"=>"tl"}, "lang"=>"tl"}]}
I would like to save the array of information as a separate variable in an array. How would I go about this?
Hash's have 2 very nice methods,
hash.values
hash.keys
in your case -
h = {"statuses"=>[{"metadata"=>{"result_type"=>"recent", "iso_language_code"=>"tl"}, "lang"=>"tl"}]}
p h.values
p.keys
These output arrays of each type. This might be what you want.
Also, this question will very well be closed. 1 Google search reported several Hash to Array SO questions.
Ruby Hash to array of values
Converting Ruby hashes to arrays
If you have a Hash like so:
hash = {:numbers => [1,2,3,4]}
And you need to capture the array into a new variable. You can just access the key and assign it to a new variable like so:
one_to_five = hash[:numbers]
However, note that the new variable actually holds the array that is in the hash. So altering the hash's array alters the new variable's array.
hash[:numbers] << 6
puts one_to_five #=> [1,2,3,4,5,6]
If you use dup, it will create a copy of the array so it will be two separate arrays.
one_to_five = hash[:numbers].dup
hash[:numbers] << 6
puts one_to_five #=> [1,2,3,4,5]
So, in your case:
hash = {'statuses' => [{"metadata"=>{"result_type"=>"recent", "iso_language_code"=>"tl"}, "lang"=>"tl"}]}
new_array = hash['statuses'].dup
However, it would be interesting to see what it is you are wishing to accomplish with your code, or at least get a little more context, because this may not be the best approach for your final goal. There are a great many things you can do with Arrays and Hashes (and Enumerable) and I would encourage you to read through the documentation on them.

How to sort hash by numerical values in Ruby without converting to array?

I have a hash: {"spider" => 213, "frog" => 128, "apple" => 812}.
How can I reorder that so that it orders them by the value in descending order without converting it to an array? So, changing that to {"apple" => 812, "spider" => 213, "frog" => 128} without making it into an array.
I've tried .sort_by, but that converts it to an array.
In Ruby 1.9, "Hashes enumerate their values in the order that the corresponding keys were inserted", so just make sure you insert them in the right way:
Hash[h.sort_by { |_, v| -v }]
#=> > {"apple"=>812, "spider"=>213, "frog"=>128}
Do you need to sort the hash itself, or just operate on its contents in sort order? If the latter, then this snippet would work:
myhash.keys.sort.each { |k| .... do something with myhash[k] .... }
The only approach I can see, apart from simply populating the hash in sorted order to start with, is to use an intermediate array:
Hash[myhash.sort]
But you, as the programmer, don't 'see' the intermediate state. Using the sort method on a hash converts the hash to a sorted array of arrays, and the #[] method coerces the array into a hash. On Ruby-1.9.3, at least.
So the question is not irrelevant. But, why? What do you mean by reorder? In place reorder? No matter what you do, you will have to somehow extract the values from the hash. Why are you afraid of arrays? Do you know that small hashes are now implemented as arrays in Ruby? (Or will be in 2.0?) Matz refrained from adding sort method to hashes on purpose, I read his post on this topic...

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"]

Resources