Convert array of hashes to single hash with values as keys - ruby

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}

Related

How would I remove multiple nested values from a hash

I've got a really confusing question that I can't seem to figure out.
Say I have this hash
hash = {
"lock_version"=>1,
"exhibition_quality"=>false,
"within"=>["FID6", "S5"],
"representation_file"=>{
"lock_version"=>0,
"created_by"=>"admin",
"within"=>["FID6", "S5"]
}
}
How can I delete from "within"=>["FID6", "S5"] a value with the pattern FID<Number> (in this example FID6)?
I've thought about it a bit and used the .delete, .reject! the within but I realised this was deleting the whole key value pair which is not what I want. Thanks a lot for any help that can put me on the right path.
You could use a method or proc/lambda to achieve the result. This solutions splits up the logic in two parts. Removing the actual FID<Number> string from the array and recursively calling the former on the correct key.
remove_fid = ->(array) { array.grep_v(/\AFID\d+\z/) }
remove_nested_fid = lambda do |hash|
hash.merge(
hash.slice('within').transform_values(&remove_fid),
hash.slice('representation_file').transform_values(&remove_nested_fid)
)
end
pp hash.then(&remove_nested_fid) # or remove_nested_fid.call(hash)
# {"lock_version"=>1,
# "exhibition_quality"=>false,
# "within"=>["S5"],
# "representation_file"=>
# {"lock_version"=>0, "created_by"=>"admin", "within"=>["S5"]}}
grep_v removes all strings from the array that do not match the given regex.
slice creates a new hash only containing the given keys. If a key is missing it will not be present in the resulting hash.
transform_values transforms the values of a hash into a new value (similar to map for Array), returning a hash.
merge creates a new hash, merging the hashes together.
This solution does not mutate the original hash structure.
You're going to need to recurse over the Hash and, in cases where the value is a Hash, process it using the same function, repeatedly. This can be problematic in Ruby -- which doesn't handle recursion very well -- if the depth of the tree is too deep, but it's the most natural way to express this type of issue.
def filter_fids(h)
h.each_pair do |k, v|
if v.is_a? Hash
filter_fids v
elsif k == "within"
v.reject! { |x| x.start_with? "FID" }
end
end
end
This will mutate the original data structure in place.

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

How can I build a hash of hash from Hash of Hash of array in Ruby

I have following hash of hash of array with me:
Hash["signin"]["elementname"]["ids"]` -- here ids are "macid" and "winid"
Its structure is like elementname has two kind of ids macid and winid.
On runtime when I pass a parameter say mac then I am trying to build a hash from a existing hash which would have only macid.
So that, I am trying to convert a Hash["signin"]["elementname"]["ids"].
This should work
Hash[common_ids.map { |a|
[a[0], Hash[a[1].map { |b|
[b[0], b[1]['mac_id']]
}]]
}]
You need to do the Hash[...] part because the map method on a hash turns it into an array of key-value pairs.
Hopefully that code works for you. It worked for me in irb. You may want to rename the a and b variables to something that makes more sense.
Here's some explanation:
When you call .map on a hash, it provides each value as a [k, v] array, it expects the contents of the block to evaluate to a [k, v] array, and the result returned by .map is an array of [k, v] arrays. Hash[...] is used to turn that style of array back into a hash (see http://ruby-doc.org/core-2.1.5/Hash.html#method-c-5B-5D).
For
common_ids = {"signin"=> { "company_txt"=>{"mac_id"=> "mac_id_1", "win_id"=> "win_id_1"}, "username_txtbx"=> {"mac_id"=>"mac_id_2", "win_id"=>"win_id_2"} } }
here is what happens:
The first level block (with parameter a) gets
a = ["signin", { "company_txt"=>{"mac_id"=> "mac_id_1", "win_id"=> "win_id_1"}, "username_txtbx"=> {"mac_id"=>"mac_id_2", "win_id"=>"win_id_2"} }]
It calls map on the second entry of that array (the value part of the key-value pair) using the second block (with parameter b), which first gets
b = ["company_txt", {"mac_id"=> "mac_id_1", "win_id"=> "win_id_1"}]
It returns
["company_txt", "mac_id_1"]
Then it gets
b = ["username_txtbx", {"mac_id"=>"mac_id_2", "win_id"=>"win_id_2"}]
It returns
["username_txtbx", "mac_id_2"]
The result of this inner map is
[["company_txt", "mac_id_1"], ["username_txtbx", "mac_id_2"]]
Calling Hash[...] on this gives
{"company_txt" => "mac_id_1", "username_txtbx" => "mac_id_2"}
This is then given as the second element of the array for the outer map, resulting in
["signin", {"company_txt" => "mac_id_1", "username_txtbx" => "mac_id_2"}]
If you had a second top-level element of common_ids, it would result in the same processing. When the outer map call completes, you have
[["signin", {"company_txt" => "mac_id_1", "username_txtbx" => "mac_id_2"}], ...]
where the ... represents where additional top-level elements of common_ids would go.
Calling Hash[...] on this gives
{"signin" => {"company_txt" => "mac_id_1", "username_txtbx" => "mac_id_2"}, ...}
where the ... represents any additional top-level key-value pairs in the form k => v.
Hopefully that explanation helps.

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...

Resources