Change Hash to array of arrays in ruby - ruby-on-rails

Let's say i have a hash
{:facebook=>0.0, :twitter=>10.0, :linkedin=>6.0, :youtube=>8.0}
Now i want it to change to an array like
[[Facebook,0.0],[Twitter,10.0],[Linkedin,6.0],[Youtube,8.0]]
I can use a logic to extract and change it in to array, but i was just wondering if there could be any defined methods in ruby which i can use to implement the above.

You can use to_a.
{:facebook=>0.0, :twitter=>10.0, :linkedin=>6.0, :youtube=>8.0}.to_a
returns
[[:facebook, 0.0], [:twitter, 10.0], [:linkedin, 6.0], [:youtube, 8.0]]
This won't automatically convert your symbols to constants though, you will have to use map (and const_get) for that.
{:facebook=>0.0, :twitter=>10.0, :linkedin=>6.0, :youtube=>8.0}.map{|k,v| [Kernel.const_get(k.to_s.capitalize), v]}
Outputs
[[Facebook,0.0],[Twitter,10.0],[Linkedin,6.0],[Youtube,8.0]]

your_hash.to_a
is the answer. http://www.ruby-doc.org/core-1.9.2/Enumerable.html#method-i-to_a

Just wrap your hash in [] and add asterisks before hash.
[*{:facebook=>0.0, :twitter=>10.0, :linkedin=>6.0, :youtube=>8.0}]

sites = {:facebook => 0.0, :twitter => 10.0, :linkedin => 6.0, :youtube => 8.0}
sites.map { |key, value| [Object.const_get(key.to_s.capitalize), value] }

Related

Convert an array of hash with symbols

I have an array of hashes:
[{'object' => 'ob1', 'quantity' => '2'}, {'object' => 'ob2', 'quantity' => '3'}, .....]
I want to convert it to symbolized form:
[{:object => 'ob1', :quantity => '2'}, {:object => 'ob2', :quantity => '3'}, .....]
tried with:
symbolized_array = array.each => { |c| c.to_options }
but i didn't obtained any conversion, the symbolized_array is same as array
why?
Since ruby 2.5 there's Hash#transform_keys:
array.map{|hash| hash.transform_keys(&:to_sym) }
Before that it was available in activesupport (part of rails) along with shortcut symbolize_keys
You tagged rails so you can use symbolize_keys
array.map(&:symbolize_keys)
Use below code. Then you will get expected output
array.map! {|my_hash| my_hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}}
Or simply you can use array.map(&:symbolize_keys). This code will be work on rails environment
i didn't obtained any conversion […] why?
to_options does return a new hash with the keys symbolized, but you didn't use that new hash – each merely traverses the array and at the end returns the array.
If you want to pick up the blocks results as the new array element, you have to use map:
array.map { |c| c.to_options } # or array.map(&:to_options)
Alternatively there's to_options! (with a !) which would work along with each:
array.each { |c| c.to_options! } # or array.each(&:to_options!)
That's because to_options! modifies the hashes in-place.
Note that to_options is an alias for symbolize_keys which might be a little clearer.

How do you use map to get a hash instead of an array?

I am currently using the following to get an array of a certain field in a table:
Classrooms.all.map(&:teacher_name)
This returns the following:
["James", "Josh", "Peter"]
What I want is a hash instead so something like the following where I can include the teacher_id:
{"James" => "1", "Josh" => "2", "Peter" => "3"}
I tried using Classrooms.all.map(&:teacher_name, &:teacher_id) but it gives me a syntax error.
Thanks!
Do it the old-fashioned way:
pairs = Classrooms.all.map {|t|
[t.teacher_name, t.teacher_id] # [key, value]
}
hash = Hash[pairs] # in /old/ ruby: Hash[*pairs.flatten]
.. or whatnot.
See In Ruby, how do I make a hash from an array?
Ruby 2.6.0 enables a shorter syntax:
Classrooms.all.to_h { |t| [t.teacher_name, t.teacher_id] }
Another alternative is not to use each at all. Use each_with_object instead. It is designed for what you are trying to do.
Classrooms.all.each_with_object({}) { |c, hash| hash[c.teacher_name] = c.teacher_id }

How to check if specific value is present in a hash?

I'm using Rails and I have a hash object. I want to search the hash for a specific value. I don't know the keys associated with that value.
How do I check if a specific value is present in a hash? Also, how do I find the key associated with that specific value?
Hash includes Enumerable, so you can use the many methods on that module to traverse the hash. It also has this handy method:
hash.has_value?(value_you_seek)
To find the key associated with that value:
hash.key(value_you_seek)
This API documentation for Ruby (1.9.2) should be helpful.
The simplest way to check multiple values are present in a hash is:
h = { a: :b, c: :d }
h.values_at(:a, :c).all? #=> true
h.values_at(:a, :x).all? #=> false
In case you need to check also on blank values in Rails with ActiveSupport:
h.values_at(:a, :c).all?(&:present?)
or
h.values_at(:a, :c).none?(&:blank?)
The same in Ruby without ActiveSupport could be done by passing a block:
h.values_at(:a, :c).all? { |i| i && !i.empty? }
Hash.has_value? and Hash.key.
Imagine you have the following Array of hashes
available_sports = [{name:'baseball', label:'MLB Baseball'},{name:'tackle_football', label:'NFL Football'}]
Doing something like this will do the trick
available_sports.any? {|h| h['name'] == 'basketball'}
=> false
available_sports.any? {|h| h['name'] == 'tackle_football'}
=> true
While Hash#has_key? works but, as Matz wrote here, it has been deprecated in favour of Hash#key?.
Hash's key? method tells you whether a given key is present or not.
hash.key?(:some_key)
The class Hash has the select method which will return a new hash of entries for which the block is true;
h = { "a" => 100, "b" => 200, "c" => 300 }
h.select {|k,v| v == 200} #=> {"b" => 200}
This way you'll search by value, and get your key!
If you do hash.values, you now have an array.
On arrays you can utilize the Enumerable search method include?
hash.values.include?(value_you_seek)
An even shorter version that you could use would be hash.values

Ruby JSON parse changes Hash keys

Lets say I have this Hash:
{
:info => [
{
:from => "Ryan Bates",
:message => "sup bra",
:time => "04:35 AM"
}
]
}
I can call the info array by doing hash[:info].
Now when I turn this into JSON (JSON.generate), and then parse it (JSON.parse), I get this hash:
{
"info" => [
{
"from" => "Ryan Bates",
"message" => "sup bra",
"time" => "04:35 AM"
}
]
}
Now if I use hash[:info] it returns nil, but not if I use hash["info"].
Why is this? And is there anyway to fix this incompatibility (besides using string keys from the start)?
The JSON generator converts symbols to strings because JSON does not support symbols. Since JSON keys are all strings, parsing a JSON document will produce a Ruby hash with string keys by default.
You can tell the parser to use symbols instead of strings by using the symbolize_names option.
Example:
original_hash = {:info => [{:from => "Ryan Bates", :message => "sup bra", :time => "04:35 AM"}]}
serialized = JSON.generate(original_hash)
new_hash = JSON.parse(serialized, {:symbolize_names => true})
new_hash[:info]
#=> [{:from=>"Ryan Bates", :message=>"sup bra", :time=>"04:35 AM"}]
Reference: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/json/rdoc/JSON.html#method-i-parse
In short, no. Think about it this way, storing symbols in JSON is the same as storing strings in JSON. So you cannot possibly distinguish between the two when it comes to parsing the JSON string. You can of course convert the string keys back into symbols, or in fact even build a class to interact with JSON which does this automagically, but I would recommend just using strings.
But, just for the sake of it, here are the answers to this question the previous times it's been asked:
what is the best way to convert a json formatted key value pair to ruby hash with symbol as key?
ActiveSupport::JSON decode hash losing symbols
Or perhaps a HashWithIndifferentAccess
I solved my similar issue with calling the with_indifferent_access method on it
Here I have a json string and we can assign it to variable s
s = "{\"foo\":{\"bar\":\"cool\"}}"
So now I can parse the data with the JSON class and assign it to h
h = JSON.parse(s).with_indifferent_access
This will produce a hash that can accept a string or a symbol as the key
h[:foo]["bar"]
#=> "cool"
Use ActiveSupport::JSON.decode, it will allow you to swap json parsers easier
Use ActiveSupport::JSON.decode(my_json, symbolize_names: true)
This will recursively symbolize all keys in the hash.
(confirmed on ruby 2.0)
It's possible to modify all the keys in a hash to convert them from a string to a symbol:
symbol_hash = Hash[obj.map{ |k,v| [k.to_sym, v] }]
puts symbol_hash[:info]
# => {"from"=>"Ryan Bates", "message"=>"sup bra", "time"=>"04:35 AM"}
Unfortunately that doesn't work for the hash nested inside the array. You can, however, write a little recursive method that converts all hash keys:
def symbolize_keys(obj)
#puts obj.class # Useful for debugging
return obj.collect { |a| symbolize_keys(a) } if obj.is_a?(Array)
return obj unless obj.is_a?(Hash)
return Hash[obj.map{ |k,v| [k.to_sym, symbolize_keys(v)] }]
end
symbol_hash = symbolize_keys(hash)
puts symbol_hash[:info]
# => {:from=>"Ryan Bates", :message=>"sup bra", :time=>"04:35 AM"}
You can't use that option like this
ActiveSupport::JSON.decode(str_json, symbolize_names: true)
In Rails 4.1 or later, ActiveSupport::JSON.decode no longer accepts
an options hash for MultiJSON. MultiJSON reached its end of life and
has been removed.
You can use symbolize_keys to handle it.
Warning: It works only for JSON strings parsed to hash.
ActiveSupport::JSON.decode(str_json).symbolize_keys

convert a list of hashes to a argument for a method

I have hash based arguments.
method1(:test=>[:arg1, :arg2 => :something])
I need to pass :test as argument to another method in the following format
from A:
[:arg1, {:arg2=>:something}]
to B:
method2 :arg1, :arg2=>:something
How can I get from A to B?
How about?
args = {:test => [:arg1, :arg2 => :something]}
method1(args)
method2(*args[:test])
If ary = [:art1, {:arg2 => :something}] then method2 *ary should do the trick.
if you don't have a lot of things in your hash, you could just loop on the keys and dereference them. What has to happen for this to work is:
you have to add the key/values in the order you want them.
be using 1.9 (I believe it was in 1.9 that they made it so that key ordering is retained in hashes)

Resources