DEV Community

Andrew Bohush
Andrew Bohush

Posted on

Convert a Hash to a Struct-like object

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
Enter fullscreen mode Exit fullscreen mode

Check out the full API description here.

Top comments (0)