TL;DR
Stop thinking about symbols vs strings when accessing values in your hash. Simply call a method. Convert your hash to a struct-like object with hash_to_struct gem. It is built on top of standard Struct
and OpenStruct
, with the ability to handle nested hashes/arrays and a few more convenient features.
Motivation
Working with a ruby Hash
can sometimes be tricky. Hash's ability to hold almost any object as a key may be a source of frustration when using strings and symbols as hash keys.
Often, when we use a hash to represent some data, we also have to pick String
or Symbol
type for the hash keys while in fact we do not really care about the key type. From a human perspective keys 'foo'
and :foo
in a hash should point to the same value.
And while it may not matter to you, it certainly does for a ruby interpreter. So it is common to get a nil
in the place where you are expecting hash[:foo]
to return a value but the hash happen to be {'foo' => value}
for some reason.
This typically happens around hash serialization actions. For example, at one point you had a hash with symbol keys and saved it to Redis, then later got the hash back from Redis, but keys are strings now.
There is an Active Support Core Extensions class called ActiveSupport::HashWithIndifferentAccess
, solving precisely this problem. It extends a ruby Hash
, making it handle keys like 'foo'
and :foo
as if they were the same.
However, I would not consider this as a go-to approach whenever you don't want to care about a key type. I think the best case for the ActiveSupport::HashWithIndifferentAccess
is when you work with an API that explicitly expects a hash, but you don't know or don't care what key type the API relies on.
When working with regular serializable data I prefer to use structs (e.g., Struct
, OpenStruct
). Unlike hashes, structs represent data in the form of value objects, which is more convenient to me, because I do not have to rely on hash specifics like brackets ([]
) as accessor method with the whole string vs symbols dilemma. I expect my data to be an object with values accessible through regular method calls, so I can rely on duck typing if needed.
Ruby's standard struct classes are a good fit for this case, but you will need some extra "glue" to make this all work together. There is no easy way to convert Hash
to Struct
or OpenStruct
. While OpenStrcut
does accept a hash as a constructor argument, Struct
expects a list of keys first to be mapped to values later. And both Struct
and OpenStruct
are not able to perform recursive conversion of a nested hash.
To facilitate this, I've built the hash_to_struct gem, which defines a unified interface for creating objects based on Struct
or OpenStruct
out of Hash
objects, with an ability to use nested hashes and a few other convenient features. It is a simple gem under 100 lines of code, with no external dependencies.
With this library you can, for example, create a Struct-based value object like this:
struct = HashToStruct.struct({q: 1, w: { e: 2 }})
struct.q.w.e # => 2
Check out the full API description here.
Top comments (0)