Remove nil values from hash - ruby

I am looking to remove keys from hash that have nil value. article is a class storing each article, and attributes method stores the article as hash.
Expected Result:
{"articles":[{"results":[{"author":null,"title":"Former bar manager jailed for preying on homeless 14-year-old girl","summary":"<p><img src=\"http://images.theage.com.au/2015/08/24/6790912/Thumbnail999662740gisd08image.related.thumbnail.320x214.gj68pg.png1440386418031.jpg-90x60.jpg\" width=\"90\" height=\"60\" style=\"float:left;margin:4px;border:0px\"/></p>A man who preyed on a 14-year-old girl he came across living on the streets of Wodonga has been jailed for nine months.","images":null,"source":null,"date":"Mon, 24 Aug 2015 03:20:21 +0000","guid":"<guid isPermaLink=\"false\">gj68pg</guid>","link":"http://www.theage.com.au/victoria/former-bar-manager-jailed-for-preying-on-homeless-14yearold-girl-20150824-gj68pg.html","section":null,"item_type":null,"updated_date":null,"created_date":null,"material_type_facet":null,"abstract":null,"byline":null,"kicker":null}]}]}
Looking to remove null values from the above output.
def attributes
hash = {
"author" => #author,
"title" => #title,
"summary" => #summary,
"images" => #images,
"source" => #source,
"date" => #date
}
hash = {}
count = 0
article.attributes.each do |key,value|
if value == nil
hash[count] = article.attributes.delete(key)
count += 1
end
end
hash.to_json
The result is as below:
{"0":null,"1":null,"2":null,"3":null,"4":null,"5":null,"6":null,"7":null,"8":null,"9":null,"10":null}

If you're on Ruby >= 2.4.6, you can use compact.
https://apidock.com/ruby/v2_4_6/Hash/compact
Example:
hash = {:foo => nil, :bar => "bar"}
=> {:foo=>nil, :bar=>"bar"}
hash.compact
=> {:bar=>"bar"}
hash
=> {:foo=>nil, :bar=>"bar"}
There's also compact! which removes nil values, or nil if no values in the hash were nil (from version >= 2.5.5).
https://apidock.com/ruby/v2_4_6/Hash/compact%21
Example:
hash
=> {:foo=>nil, :bar=>"bar"}
hash.compact!
=> {:bar=>"bar"}
hash
=> {:bar=>"bar"}
hash.compact!
=> nil

How about trying:
hash = article.attributes.select {|k, v| v }
If the value is false or nil, the attribute will be ignored.
If you want to keep the false value and only eliminate nil, you could run:
hash = article.attributes.select {|k, v| !v.nil? }

You can remove all the nil values from a hash namely "h":
h.delete_if { |k, v| v.nil? }
and you can use this one to remove empty values, too:
h.delete_if { |k, v| v.nil? || v.empty? }

In my case I use refinements to Hash class
module HashUtil
refine Hash do
# simple but can not adapt to nested hash
def squeeze
select{|_, v| !v.nil? }
end
# complex but can adapt to nested hash
def squeeze_deep
each_with_object({}) do |(k, v), squeezed_hash|
if v.is_a?(Hash)
squeezed_hash[k] = v.squeeze
else
squeezed_hash[k] = v unless v.nil?
end
end
end
end
end
class Article
using HashUtil
def attributes
hash = {
"author" => #author,
"title" => #title,
"summary" => #summary,
"images" => #images,
"source" => #source,
"date" => #date
}
hash.squeeze
end
end

The given input is valid JSON, and the question has the JSON tag, so I'd like to offer a generic solution to the problem of recursively eliminating keys in JSON objects, wherever they occur, and no matter what the input JSON may be.
The solution is written in a new JSON-oriented programming language, jq, but even if you cannot use jq, the solution is so brief and elegant that it may suggest an implementation to the general problem in your language of choice.
Here it is - a one-liner:
walk( if type == "object" then with_entries( select(.value != null) ) else . end)
This presupposes jq version 1.5 (see https://stedolan.github.io/jq/).
If you have an older version of jq, the definition of walk/1 can easily be added. (See e.g. Transforming the name of key deeper in the JSON structure with jq)

Related

Using ruby hash as constraints for selecting from a larger hash

I have a hash of conditions:
{"age" => "52", "First_Name" => "Bat"}
And I have an array of person hashes:
[{"id"=>"1", "First_Name"=>"John", "Last_Name"=>"Doe", "Age"=>"27"}, {"id"=>"2", "First_Name"=>"Jane", "Last_Name"=>"Doe", "Age"=>"26"}, {"id"=>"3", "First_Name"=>"Bat", "Last_Name"=>"Man", "Age"=>"52"}, {"id"=>"4", "First_Name"=>"Bat", "Last_Name"=>"Woman", "Age"=>"51"}]
How do I get all the person hashes who match conditions? (In this case, it should only return the "Bat Man" hash.)
If you want to get all the people who match conditions:
people.select do |person|
conditions.all? { |k,v| person[k] == v }
end
Or if you are sure there will only ever be one (thanks to #tokland):
people.detect do |person|
conditions.all? { |k,v| person[k] == v }
end

How do I convert a Ruby hash so that all of its keys are symbols?

I have a Ruby hash which looks like:
{ "id" => "123", "name" => "test" }
I would like to convert it to:
{ :id => "123", :name => "test" }
hash = {"apple" => "banana", "coconut" => "domino"}
Hash[hash.map{ |k, v| [k.to_sym, v] }]
#=> {:apple=>"banana", :coconut=>"domino"}
#mu is too short: Didn't see word "recursive", but if you insist (along with protection against non-existent to_sym, just want to remind that in Ruby 1.8 1.to_sym == nil, so playing with some key types can be misleading):
hash = {"a" => {"b" => "c"}, "d" => "e", Object.new => "g"}
s2s =
lambda do |h|
Hash === h ?
Hash[
h.map do |k, v|
[k.respond_to?(:to_sym) ? k.to_sym : k, s2s[v]]
end
] : h
end
s2s[hash] #=> {:d=>"e", #<Object:0x100396ee8>=>"g", :a=>{:b=>"c"}}
If you happen to be in Rails then you'll have symbolize_keys:
Return a new hash with all keys converted to symbols, as long as they respond to to_sym.
and symbolize_keys! which does the same but operates in-place. So, if you're in Rails, you could:
hash.symbolize_keys!
If you want to recursively symbolize inner hashes then I think you'd have to do it yourself but with something like this:
def symbolize_keys_deep!(h)
h.keys.each do |k|
ks = k.to_sym
h[ks] = h.delete k
symbolize_keys_deep! h[ks] if h[ks].kind_of? Hash
end
end
You might want to play with the kind_of? Hash to match your specific circumstances; using respond_to? :keys might make more sense. And if you want to allow for keys that don't understand to_sym, then:
def symbolize_keys_deep!(h)
h.keys.each do |k|
ks = k.respond_to?(:to_sym) ? k.to_sym : k
h[ks] = h.delete k # Preserve order even when k == ks
symbolize_keys_deep! h[ks] if h[ks].kind_of? Hash
end
end
Note that h[ks] = h.delete k doesn't change the content of the Hash when k == ks but it will preserve the order when you're using Ruby 1.9+. You could also use the [(key.to_sym rescue key) || key] approach that Rails uses in their symbolize_keys! but I think that's an abuse of the exception handling system.
The second symbolize_keys_deep! turns this:
{ 'a' => 'b', 'c' => { 'd' => { 'e' => 'f' }, 'g' => 'h' }, ['i'] => 'j' }
into this:
{ :a => 'b', :c => { :d => { :e => 'f' }, :g => 'h' }, ['i'] => 'j' }
You could monkey patch either version of symbolize_keys_deep! into Hash if you really wanted to but I generally stay away from monkey patching unless I have very good reasons to do it.
If you are using Rails >= 4 you can use:
hash.deep_symbolize_keys
hash.deep_symbolize_keys!
or
hash.deep_stringify_keys
hash.deep_stringify_keys!
see http://apidock.com/rails/v4.2.1/Hash/deep_symbolize_keys
Just in case you are parsing JSON, from the JSON docs you can add the option to symbolize the keys upon parsing:
hash = JSON.parse(json_data, symbolize_names: true)
Victor Moroz provided a lovely answer for the simple recursive case, but it won't process hashes that are nested within nested arrays:
hash = { "a" => [{ "b" => "c" }] }
s2s[hash] #=> {:a=>[{"b"=>"c"}]}
If you need to support hashes within arrays within hashes, you'll want something more like this:
def recursive_symbolize_keys(h)
case h
when Hash
Hash[
h.map do |k, v|
[ k.respond_to?(:to_sym) ? k.to_sym : k, recursive_symbolize_keys(v) ]
end
]
when Enumerable
h.map { |v| recursive_symbolize_keys(v) }
else
h
end
end
Try this:
hash = {"apple" => "banana", "coconut" => "domino"}
# => {"apple"=>"banana", "coconut"=>"domino"}
hash.tap do |h|
h.keys.each { |k| h[k.to_sym] = h.delete(k) }
end
# => {:apple=>"banana", :coconut=>"domino"}
This iterates over the keys, and for each one, it deletes the stringified key and assigns its value to the symbolized key.
If you're using Rails (or just Active Support):
{ "id" => "123", "name" => "test" }.symbolize_keys
Starting with Ruby 2.5 you can use the transform_key method.
So in your case it would be:
h = { "id" => "123", "name" => "test" }
h.transform_keys!(&:to_sym) #=> {:id=>"123", :name=>"test"}
Note: the same methods are also available on Ruby on Rails.
Here's a Ruby one-liner that is faster than the chosen answer:
hash = {"apple" => "banana", "coconut" => "domino"}
#=> {"apple"=>"banana", "coconut"=>"domino"}
hash.inject({}){|h,(k,v)| h[k.intern] = v; h}
#=> {:apple=>"banana", :coconut=>"domino"}
Benchmark results:
n = 100000
Benchmark.bm do |bm|
bm.report { n.times { hash.inject({}){|h,(k,v)| h[k.intern] = v; h} } }
bm.report { n.times { Hash[hash.map{ |k, v| [k.to_sym, v] }] } }
end
# => user system total real
# => 0.100000 0.000000 0.100000 ( 0.107940)
# => 0.120000 0.010000 0.130000 ( 0.137966)
I'm partial to:
irb
ruby-1.9.2-p290 :001 > hash = {"apple" => "banana", "coconut" => "domino"}
{
"apple" => "banana",
"coconut" => "domino"
}
ruby-1.9.2-p290 :002 > hash.inject({}){ |h, (n,v)| h[n.to_sym] = v; h }
{
:apple => "banana",
:coconut => "domino"
}
This works because we're iterating over the hash and building a new one on the fly. It isn't recursive, but you could figure that out from looking at some of the other answers.
hash.inject({}){ |h, (n,v)| h[n.to_sym] = v; h }
You can also extend core Hash ruby class placing a /lib/hash.rb file :
class Hash
def symbolize_keys_deep!
new_hash = {}
keys.each do |k|
ks = k.respond_to?(:to_sym) ? k.to_sym : k
if values_at(k).first.kind_of? Hash or values_at(k).first.kind_of? Array
new_hash[ks] = values_at(k).first.send(:symbolize_keys_deep!)
else
new_hash[ks] = values_at(k).first
end
end
new_hash
end
end
If you want to make sure keys of any hash wrapped into arrays inside your parent hash are symbolized, you need to extend also array class creating a "array.rb" file with that code :
class Array
def symbolize_keys_deep!
new_ar = []
self.each do |value|
new_value = value
if value.is_a? Hash or value.is_a? Array
new_value = value.symbolize_keys_deep!
end
new_ar << new_value
end
new_ar
end
end
This allows to call "symbolize_keys_deep!" on any hash variable like this :
myhash.symbolize_keys_deep!
def symbolize_keys(hash)
new={}
hash.map do |key,value|
if value.is_a?(Hash)
value = symbolize_keys(value)
end
new[key.to_sym]=value
end
return new
end
puts symbolize_keys("c"=>{"a"=>2,"k"=>{"e"=>9}})
#{:c=>{:a=>2, :k=>{:e=>9}}}
Here's my two cents,
my version of symbolize_keys_deep! uses the original symbolize_keys! provided by rails and just makes a simple recursive call to Symbolize sub hashes.
def symbolize_keys_deep!(h)
h.symbolize_keys!
h.each do |k, v|
symbolize_keys_deep!(v) if v.is_a? Hash
end
end
Facets' Hash#rekey is also a worth mentioning.
Sample:
require 'facets/hash/rekey'
{ "id" => "123", "name" => "test" }.deep_rekey
=> {:id=>"123", :name=>"test"}
There is also a recursive version:
require 'facets/hash/deep_rekey'
{ "id" => "123", "name" => {"first" => "John", "last" => "Doe" } }.deep_rekey
=> {:id=>"123", :name=>{:first=>"John", :last=>"Doe"}}
Here's a little recursive function to do a deep symbolization of the keys:
def symbolize_keys(hash)
Hash[hash.map{|k,v| v.is_a?(Hash) ? [k.to_sym, symbolize_keys(v)] : [k.to_sym, v] }]
end

Bidirectional Hash table in Ruby

I need a bidirectional Hash table in Ruby. For example:
h = {:abc => 123, :xyz => 789, :qaz => 789, :wsx => [888, 999]}
h.fetch(:xyz) # => 789
h.rfetch(123) # => abc
h.rfetch(789) # => [:xyz, :qaz]
h.rfetch(888) # => :wsx
Method rfetch means reversed fetch and is only my proposal.
Note three things:
If multiple keys map at the same value then rfetch returns all of them, packed in array.
If value is an array then rfetch looks for its param among elements of the array.
Bidirectional Hash means that both fetch and rfetch should execute in constant time.
Does such structure exists in Ruby (including external libraries)?
I thought about implementing it using two one-directional Hashes synchronized when one of them is modified (and packing it into class to avoid synchronization problems) but maybe I could use an already existing solution?
You could build something yourself pretty easily, just use a simple object that wraps two hashes (one for the forward direction, one for the reverse). For example:
class BiHash
def initialize
#forward = Hash.new { |h, k| h[k] = [ ] }
#reverse = Hash.new { |h, k| h[k] = [ ] }
end
def insert(k, v)
#forward[k].push(v)
#reverse[v].push(k)
v
end
def fetch(k)
fetch_from(#forward, k)
end
def rfetch(v)
fetch_from(#reverse, v)
end
protected
def fetch_from(h, k)
return nil if(!h.has_key?(k))
v = h[k]
v.length == 1 ? v.first : v.dup
end
end
Look ups will behave just like normal hash lookups (because they are normal hash lookups). Add some operators and maybe decent to_s and inspect implementations and you're good.
Such a thing works like this:
b = BiHash.new
b.insert(:a, 'a')
b.insert(:a, 'b')
b.insert(:a, 'c')
b.insert(:b, 'a')
b.insert(:c, 'x')
puts b.fetch(:a).inspect # ["a", "b", "c"]
puts b.fetch(:b).inspect # "a"
puts b.rfetch('a').inspect # [:a, :b]
puts b.rfetch('x').inspect # :c
puts b.fetch(:not_there).inspect # nil
puts b.rfetch('not there').inspect # nil
There's nothing wrong with building your tools when you need them.
There is no such structure built-in in Ruby.
Note that Hash#rassoc does something similar, but it returns only the first match and is linear-time:
h = {:abc => 123, :xyz => 789, :qaz => 789, :wsx => [888, 999]}
h.rassoc(123) # => [:abc, 123]
Also, it isn't possible to fullfill your requirements in Ruby in a perfectly safe manner, as you won't be able to detect changes in values that are arrays. E.g.:
h = MyBidirectionalArray.new(:foo => 42, :bar => [:hello, :world])
h.rfetch(:world) # => :bar
h[:bar].shift
h[:bar] # => [:world]
h.rfetch(:world) # => should be nil, but how to detect this??
Computing a hash everytime to detect a change will make your lookup linear-time. You could duplicate the array-values and freeze them, though (like Ruby does for Hash keys that are strings!)
What you seem to need is a Graph class, which could have a different API than a Hash, no? You can check out rgl or similar, but I don't know how they're implemented.
Good luck.
There is a Hash#invert method (http://www.ruby-doc.org/core-2.1.0/Hash.html#method-i-invert) to achieve this. It won't map multiple values to an array though.
Try this:
class Hash
def rfetch val
select { |k,v| v.is_a?(Array) ? v.include?(val) : v == val }.map { |x| x[0] }
end
end
If you're not doing lots of updates to this hash, you might be able to use inverthash.

Ruby Style: How to check whether a nested hash element exists

Consider a "person" stored in a hash. Two examples are:
fred = {:person => {:name => "Fred", :spouse => "Wilma", :children => {:child => {:name => "Pebbles"}}}}
slate = {:person => {:name => "Mr. Slate", :spouse => "Mrs. Slate"}}
If the "person" doesn't have any children, the "children" element is not present. So, for Mr. Slate, we can check whether he has parents:
slate_has_children = !slate[:person][:children].nil?
So, what if we don't know that "slate" is a "person" hash? Consider:
dino = {:pet => {:name => "Dino"}}
We can't easily check for children any longer:
dino_has_children = !dino[:person][:children].nil?
NoMethodError: undefined method `[]' for nil:NilClass
So, how would you check the structure of a hash, especially if it is nested deeply (even deeper than the examples provided here)? Maybe a better question is: What's the "Ruby way" to do this?
The most obvious way to do this is to simply check each step of the way:
has_children = slate[:person] && slate[:person][:children]
Use of .nil? is really only required when you use false as a placeholder value, and in practice this is rare. Generally you can simply test it exists.
Update: If you're using Ruby 2.3 or later there's a built-in dig method that does what's described in this answer.
If not, you can also define your own Hash "dig" method which can simplify this substantially:
class Hash
def dig(*path)
path.inject(self) do |location, key|
location.respond_to?(:keys) ? location[key] : nil
end
end
end
This method will check each step of the way and avoid tripping up on calls to nil. For shallow structures the utility is somewhat limited, but for deeply nested structures I find it's invaluable:
has_children = slate.dig(:person, :children)
You might also make this more robust, for example, testing if the :children entry is actually populated:
children = slate.dig(:person, :children)
has_children = children && !children.empty?
With Ruby 2.3 we'll have support for the safe navigation operator:
https://www.ruby-lang.org/en/news/2015/11/11/ruby-2-3-0-preview1-released/
has_children now could be written as:
has_children = slate[:person]&.[](:children)
dig is being added as well:
has_children = slate.dig(:person, :children)
Another alternative:
dino.fetch(:person, {})[:children]
You can use the andand gem:
require 'andand'
fred[:person].andand[:children].nil? #=> false
dino[:person].andand[:children].nil? #=> true
You can find further explanations at http://andand.rubyforge.org/.
One could use hash with default value of {} - empty hash. For example,
dino = Hash.new({})
dino[:pet] = {:name => "Dino"}
dino_has_children = !dino[:person][:children].nil? #=> false
That works with already created Hash as well:
dino = {:pet=>{:name=>"Dino"}}
dino.default = {}
dino_has_children = !dino[:person][:children].nil? #=> false
Or you can define [] method for nil class
class NilClass
def [](* args)
nil
end
end
nil[:a] #=> nil
Traditionally, you really had to do something like this:
structure[:a] && structure[:a][:b]
However, Ruby 2.3 added a feature that makes this way more graceful:
structure.dig :a, :b # nil if it misses anywhere along the way
There is a gem called ruby_dig that will back-patch this for you.
def flatten_hash(hash)
hash.each_with_object({}) do |(k, v), h|
if v.is_a? Hash
flatten_hash(v).map do |h_k, h_v|
h["#{k}_#{h_k}"] = h_v
end
else
h[k] = v
end
end
end
irb(main):012:0> fred = {:person => {:name => "Fred", :spouse => "Wilma", :children => {:child => {:name => "Pebbles"}}}}
=> {:person=>{:name=>"Fred", :spouse=>"Wilma", :children=>{:child=>{:name=>"Pebbles"}}}}
irb(main):013:0> slate = {:person => {:name => "Mr. Slate", :spouse => "Mrs. Slate"}}
=> {:person=>{:name=>"Mr. Slate", :spouse=>"Mrs. Slate"}}
irb(main):014:0> flatten_hash(fred).keys.any? { |k| k.include?("children") }
=> true
irb(main):015:0> flatten_hash(slate).keys.any? { |k| k.include?("children") }
=> false
This will flatten all the hashes into one and then any? returns true if any key matching the substring "children" exist.
This might also help.
dino_has_children = !dino.fetch(person, {})[:children].nil?
Note that in rails you can also do:
dino_has_children = !dino[person].try(:[], :children).nil? #
Here is a way you can do a deep check for any falsy values in the hash and any nested hashes without monkey patching the Ruby Hash class (PLEASE don't monkey patch on the Ruby classes, such is something you should not do, EVER).
(Assuming Rails, although you could easily modify this to work outside of Rails)
def deep_all_present?(hash)
fail ArgumentError, 'deep_all_present? only accepts Hashes' unless hash.is_a? Hash
hash.each do |key, value|
return false if key.blank? || value.blank?
return deep_all_present?(value) if value.is_a? Hash
end
true
end
Simplifying the above answers here:
Create a Recursive Hash method whose value cannot be nil, like as follows.
def recursive_hash
Hash.new {|key, value| key[value] = recursive_hash}
end
> slate = recursive_hash
> slate[:person][:name] = "Mr. Slate"
> slate[:person][:spouse] = "Mrs. Slate"
> slate
=> {:person=>{:name=>"Mr. Slate", :spouse=>"Mrs. Slate"}}
slate[:person][:state][:city]
=> {}
If you don't mind creating empty hashes if the value does not exists for the key :)
You can try to play with
dino.default = {}
Or for example:
empty_hash = {}
empty_hash.default = empty_hash
dino.default = empty_hash
That way you can call
empty_hash[:a][:b][:c][:d][:e] # and so on...
dino[:person][:children] # at worst it returns {}
Given
x = {:a => {:b => 'c'}}
y = {}
you could check x and y like this:
(x[:a] || {})[:b] # 'c'
(y[:a] || {})[:b] # nil
Thks #tadman for the answer.
For those who want perfs (and are stuck with ruby < 2.3), this method is 2.5x faster:
unless Hash.method_defined? :dig
class Hash
def dig(*path)
val, index, len = self, 0, path.length
index += 1 while(index < len && val = val[path[index]])
val
end
end
end
and if you use RubyInline, this method is 16x faster:
unless Hash.method_defined? :dig
require 'inline'
class Hash
inline do |builder|
builder.c_raw '
VALUE dig(int argc, VALUE *argv, VALUE self) {
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
self = rb_hash_aref(self, *argv);
if (NIL_P(self) || !--argc) return self;
++argv;
return dig(argc, argv, self);
}'
end
end
end
You can also define a module to alias the brackets methods and use the Ruby syntax to read/write nested elements.
UPDATE: Instead of overriding the bracket accessors, request Hash instance to extend the module.
module Nesty
def []=(*keys,value)
key = keys.pop
if keys.empty?
super(key, value)
else
if self[*keys].is_a? Hash
self[*keys][key] = value
else
self[*keys] = { key => value}
end
end
end
def [](*keys)
self.dig(*keys)
end
end
class Hash
def nesty
self.extend Nesty
self
end
end
Then you can do:
irb> a = {}.nesty
=> {}
irb> a[:a, :b, :c] = "value"
=> "value"
irb> a
=> {:a=>{:b=>{:c=>"value"}}}
irb> a[:a,:b,:c]
=> "value"
irb> a[:a,:b]
=> {:c=>"value"}
irb> a[:a,:d] = "another value"
=> "another value"
irb> a
=> {:a=>{:b=>{:c=>"value"}, :d=>"another value"}}
I don't know how "Ruby" it is(!), but the KeyDial gem which I wrote lets you do this basically without changing your original syntax:
has_kids = !dino[:person][:children].nil?
becomes:
has_kids = !dino.dial[:person][:children].call.nil?
This uses some trickery to intermediate the key access calls. At call, it will try to dig the previous keys on dino, and if it hits an error (as it will), returns nil. nil? then of course returns true.
You can use a combination of & and key? it is O(1) compared to dig which is O(n) and this will make sure person is accessed without NoMethodError: undefined method `[]' for nil:NilClass
fred[:person]&.key?(:children) //=>true
slate[:person]&.key?(:children)

Best way to convert strings to symbols in hash

What's the (fastest/cleanest/straightforward) way to convert all keys in a hash from strings to symbols in Ruby?
This would be handy when parsing YAML.
my_hash = YAML.load_file('yml')
I'd like to be able to use:
my_hash[:key]
Rather than:
my_hash['key']
In Ruby >= 2.5 (docs) you can use:
my_hash.transform_keys(&:to_sym)
Using older Ruby version? Here is a one-liner that will copy the hash into a new one with the keys symbolized:
my_hash = my_hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
With Rails you can use:
my_hash.symbolize_keys
my_hash.deep_symbolize_keys
Here's a better method, if you're using Rails:
params.symbolize_keys
The end.
If you're not, just rip off their code (it's also in the link):
myhash.keys.each do |key|
myhash[(key.to_sym rescue key) || key] = myhash.delete(key)
end
For the specific case of YAML in Ruby, if the keys begin with ':', they will be automatically interned as symbols.
require 'yaml'
require 'pp'
yaml_str = "
connections:
- host: host1.example.com
port: 10000
- host: host2.example.com
port: 20000
"
yaml_sym = "
:connections:
- :host: host1.example.com
:port: 10000
- :host: host2.example.com
:port: 20000
"
pp yaml_str = YAML.load(yaml_str)
puts yaml_str.keys.first.class
pp yaml_sym = YAML.load(yaml_sym)
puts yaml_sym.keys.first.class
Output:
# /opt/ruby-1.8.6-p287/bin/ruby ~/test.rb
{"connections"=>
[{"port"=>10000, "host"=>"host1.example.com"},
{"port"=>20000, "host"=>"host2.example.com"}]}
String
{:connections=>
[{:port=>10000, :host=>"host1.example.com"},
{:port=>20000, :host=>"host2.example.com"}]}
Symbol
if you're using Rails, it is much simpler - you can use a HashWithIndifferentAccess and access the keys both as String and as Symbols:
my_hash.with_indifferent_access
see also:
http://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html
Or you can use the awesome "Facets of Ruby" Gem, which contains a lot of extensions to Ruby Core and Standard Library classes.
require 'facets'
> {'some' => 'thing', 'foo' => 'bar'}.symbolize_keys
=> {:some=>"thing", :foo=>"bar}
see also:
http://rubyworks.github.io/rubyfaux/?doc=http://rubyworks.github.io/facets/docs/facets-2.9.3/core.json#api-class-Hash
Even more terse:
Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]
Since Ruby 2.5.0 you can use Hash#transform_keys or Hash#transform_keys!.
{'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2}
http://api.rubyonrails.org/classes/Hash.html#method-i-symbolize_keys
hash = { 'name' => 'Rob', 'age' => '28' }
hash.symbolize_keys
# => { name: "Rob", age: "28" }
If you are using json, and want to use it as a hash, in core Ruby you can do it:
json_obj = JSON.parse(json_str, symbolize_names: true)
symbolize_names: If set to true, returns symbols for the names (keys) in a JSON object. Otherwise strings are returned. Strings are the default.
Doc: Json#parse symbolize_names
Here's a way to deep symbolize an object
def symbolize(obj)
return obj.inject({}){|memo,(k,v)| memo[k.to_sym] = symbolize(v); memo} if obj.is_a? Hash
return obj.inject([]){|memo,v | memo << symbolize(v); memo} if obj.is_a? Array
return obj
end
I really like the Mash gem.
you can do mash['key'], or mash[:key], or mash.key
A modification to #igorsales answer
class Object
def deep_symbolize_keys
return self.inject({}){|memo,(k,v)| memo[k.to_sym] = v.deep_symbolize_keys; memo} if self.is_a? Hash
return self.inject([]){|memo,v | memo << v.deep_symbolize_keys; memo} if self.is_a? Array
return self
end
end
params.symbolize_keys will also work. This method turns hash keys into symbols and returns a new hash.
In Rails you can use:
{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!
Converts to:
{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}
So many answers here, but the one method rails function is hash.symbolize_keys
This is my one liner for nested hashes
def symbolize_keys(hash)
hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v }
end
In case the reason you need to do this is because your data originally came from JSON, you could skip any of this parsing by just passing in the :symbolize_names option upon ingesting JSON.
No Rails required and works with Ruby >1.9
JSON.parse(my_json, :symbolize_names => true)
You could be lazy, and wrap it in a lambda:
my_hash = YAML.load_file('yml')
my_lamb = lambda { |key| my_hash[key.to_s] }
my_lamb[:a] == my_hash['a'] #=> true
But this would only work for reading from the hash - not writing.
To do that, you could use Hash#merge
my_hash = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(YAML.load_file('yml'))
The init block will convert the keys one time on demand, though if you update the value for the string version of the key after accessing the symbol version, the symbol version won't be updated.
irb> x = { 'a' => 1, 'b' => 2 }
#=> {"a"=>1, "b"=>2}
irb> y = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(x)
#=> {"a"=>1, "b"=>2}
irb> y[:a] # the key :a doesn't exist for y, so the init block is called
#=> 1
irb> y
#=> {"a"=>1, :a=>1, "b"=>2}
irb> y[:a] # the key :a now exists for y, so the init block is isn't called
#=> 1
irb> y['a'] = 3
#=> 3
irb> y
#=> {"a"=>3, :a=>1, "b"=>2}
You could also have the init block not update the hash, which would protect you from that kind of error, but you'd still be vulnerable to the opposite - updating the symbol version wouldn't update the string version:
irb> q = { 'c' => 4, 'd' => 5 }
#=> {"c"=>4, "d"=>5}
irb> r = Hash.new { |h,k| h[k.to_s] }.merge(q)
#=> {"c"=>4, "d"=>5}
irb> r[:c] # init block is called
#=> 4
irb> r
#=> {"c"=>4, "d"=>5}
irb> r[:c] # init block is called again, since this key still isn't in r
#=> 4
irb> r[:c] = 7
#=> 7
irb> r
#=> {:c=>7, "c"=>4, "d"=>5}
So the thing to be careful of with these is switching between the two key forms. Stick with one.
Would something like the following work?
new_hash = Hash.new
my_hash.each { |k, v| new_hash[k.to_sym] = v }
It'll copy the hash, but you won't care about that most of the time. There's probably a way to do it without copying all the data.
a shorter one-liner fwiw:
my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }
How about this:
my_hash = HashWithIndifferentAccess.new(YAML.load_file('yml'))
# my_hash['key'] => "val"
# my_hash[:key] => "val"
This is for people who uses mruby and do not have any symbolize_keys method defined:
class Hash
def symbolize_keys!
self.keys.each do |k|
if self[k].is_a? Hash
self[k].symbolize_keys!
end
if k.is_a? String
raise RuntimeError, "Symbolizing key '#{k}' means overwrite some data (key :#{k} exists)" if self[k.to_sym]
self[k.to_sym] = self[k]
self.delete(k)
end
end
return self
end
end
The method:
symbolizes only keys that are String
if symbolize a string means to lose some informations (overwrite part of hash) raise a RuntimeError
symbolize also recursively contained hashes
return the symbolized hash
works in place!
The array we want to change.
strings = ["HTML", "CSS", "JavaScript", "Python", "Ruby"]
Make a new variable as an empty array so we can ".push" the symbols in.
symbols = [ ]
Here's where we define a method with a block.
strings.each {|x| symbols.push(x.intern)}
End of code.
So this is probably the most straightforward way to convert strings to symbols in your array(s) in Ruby. Make an array of strings then make a new variable and set the variable to an empty array. Then select each element in the first array you created with the ".each" method. Then use a block code to ".push" all of the elements in your new array and use ".intern or .to_sym" to convert all the elements to symbols.
Symbols are faster because they save more memory within your code and you can only use them once. Symbols are most commonly used for keys in hash which is great. I'm the not the best ruby programmer but this form of code helped me a lot.If anyone knows a better way please share and you can use this method for hash too!
If you would like vanilla ruby solution and as me do not have access to ActiveSupport here is deep symbolize solution (very similar to previous ones)
def deep_convert(element)
return element.collect { |e| deep_convert(e) } if element.is_a?(Array)
return element.inject({}) { |sh,(k,v)| sh[k.to_sym] = deep_convert(v); sh } if element.is_a?(Hash)
element
end
Starting on Psych 3.0 you can add the symbolize_names: option
Psych.load("---\n foo: bar")
# => {"foo"=>"bar"}
Psych.load("---\n foo: bar", symbolize_names: true)
# => {:foo=>"bar"}
Note: if you have a lower Psych version than 3.0 symbolize_names: will be silently ignored.
My Ubuntu 18.04 includes it out of the box with ruby 2.5.1p57
ruby-1.9.2-p180 :001 > h = {'aaa' => 1, 'bbb' => 2}
=> {"aaa"=>1, "bbb"=>2}
ruby-1.9.2-p180 :002 > Hash[h.map{|a| [a.first.to_sym, a.last]}]
=> {:aaa=>1, :bbb=>2}
This is not exactly a one-liner, but it turns all string keys into symbols, also the nested ones:
def recursive_symbolize_keys(my_hash)
case my_hash
when Hash
Hash[
my_hash.map do |key, value|
[ key.respond_to?(:to_sym) ? key.to_sym : key, recursive_symbolize_keys(value) ]
end
]
when Enumerable
my_hash.map { |value| recursive_symbolize_keys(value) }
else
my_hash
end
end
I like this one-liner, when I'm not using Rails, because then I don't have to make a second hash and hold two sets of data while I'm processing it:
my_hash = { "a" => 1, "b" => "string", "c" => true }
my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key) }
my_hash
=> {:a=>1, :b=>"string", :c=>true}
Hash#delete returns the value of the deleted key
Facets' Hash#deep_rekey is also a good option, especially:
if you find use for other sugar from facets in your project,
if you prefer code readability over cryptical one-liners.
Sample:
require 'facets/hash/deep_rekey'
my_hash = YAML.load_file('yml').deep_rekey
In ruby I find this to be the most simple and easy to understand way to turn string keys in hashes to symbols :
my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key)}
For each key in the hash we call delete on it which removes it from the hash (also delete returns the value associated with the key that was deleted) and we immediately set this equal to the symbolized key.
Similar to previous solutions but written a bit differently.
This allows for a hash that is nested and/or has arrays.
Get conversion of keys to a string as a bonus.
Code does not mutate the hash been passed in.
module HashUtils
def symbolize_keys(hash)
transformer_function = ->(key) { key.to_sym }
transform_keys(hash, transformer_function)
end
def stringify_keys(hash)
transformer_function = ->(key) { key.to_s }
transform_keys(hash, transformer_function)
end
def transform_keys(obj, transformer_function)
case obj
when Array
obj.map{|value| transform_keys(value, transformer_function)}
when Hash
obj.each_with_object({}) do |(key, value), hash|
hash[transformer_function.call(key)] = transform_keys(value, transformer_function)
end
else
obj
end
end
end

Resources