Is there a way to quickly check if the hash as symbols or strings as keys?
CodePudding user response:
You can use #all? to check whether the hash's keys are all symbols, like so:
hash = { foo: 1, bar: 2, baz: 3 }
hash.all? { |key, _value| key.is_a?(Symbol) }
#=> true
CodePudding user response:
TL;DR
A flat Hash with a single level of keys can easily be interrogated, but nested data structures can't. Additionally, a Hash key isn't limited to just String or Symbol keys. You can use almost any Ruby object as a Hash key, with a few caveats about what methods the objects must #respond_to? in order to function properly as a key.
Using #keys.all?
If your Hash isn't a nested data structure, then you could simply ask the Hash if all its top-level keys are Symbol objects:
p {foo: 1, bar: 2, baz: 3}.keys.all? Symbol
#=> true
Just replace Symbol
with String
if you want to check the opposite.
However, if you have nested data structures, then you have to iterate over each key and value to symbolize or stringify each key. You can do this with a gem like Hashie, or natively round-trip the Hash to and from JSON to coerce symbols into strings (the default) or perform other object conversions during the serialization or parse. For example, with Ruby 3.1.2:
require "json"
hash = {foo: {bar: 1, baz: 2 }}
hash = JSON.parse hash.to_json
hash
#=> {"foo"=>{"bar"=>1, "baz"=>2}}
will ensure that all your keys are converted to String objects, or raise an exception if they can't be sensibly converted to a String. You can also pass other options to force different behavior, including support for various built-in or custom JSON additions if you want. Round-tripping with YAML or Marshal are also possible, although these are generally less safe than standard JSON if you have tainted or user-provided objects in your Hash.
CodePudding user response:
You may write the following.
def key_type(h)
case h.each_key.map(&:class).uniq
when [String] then String
when [Symbol] then Symbol
else nil
end
end
key_type('cats'=>9, 'dogs'=>2) #=> String
key_type(:cats=>9, :dogs=>2) #=> Symbol
key_type('cats'=>9, :dogs=>2) #=> nil
key_type(:cats=>9, 'dogs'=>2) #=> nil
key_type(9=>'cats') #=> nil
key_type(9=>'cats', 2=>'dogs') #=> nil
key_type({}) #=> nil
For example, if
h = { 'cats'=>9, 'dogs'=>2 }
then
a = h.each_key.map(&:class)
#=> [String, String]
b = a.uniq
#=> [String]
so String
is returned. By contrast, if
h = { 'cats'=>9, :dogs=>2 }
then
a = h.each_key.map(&:class)
#=> [String, Symbol]
b = a.uniq
#=> [String, Symbol]
so nil
is returned.