Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elegantly handling blank values in a nested hash [duplicate]

I'm sure I've seen an elegant solution to this before, but I can't quite find it:

I have a rails controller which may-or-may-not have the following hash element:

myhash[:parent_field]

Inside that parent field, a child element could also be blank. I'm currently checking that via the (very ugly) method:

if (!myhash[:parent_field] || !myhash[:parent_field][:child_field] || myhash[:parent_field][:child_field].blank?)

Which works, but I figure - surely - there has to be a more elegant way. Just to reiterate:

  • myhash[:parent_field] may or may not exist
  • If it does exist, myhash[:parent_field][:child_field] may or may not exist
  • If that exists, it may or may not be blank.
like image 219
PlankTon Avatar asked Apr 27 '12 22:04

PlankTon


2 Answers

#fetch is your friend:

my_hash.fetch(:parent, {})[:child].blank?
like image 167
d11wtq Avatar answered Oct 28 '22 18:10

d11wtq


What I would do is just use local variables to ease your burden:

unless (p=foo[:parent]) && (c=p[:child]) && !c.blank?
  # Stuff is borked!
end

But let's explore alternatives, for fun…


If you can't change your data structure (that's a Hash, by the way, not an Array) then you can use the Ruby andand gem in conjunction with the Rails' try as lazy ways of calling methods on things that might be nil objects.


You could alternatively change your data structure to Hashes that return empty auto-vivifying hashes when you ask for a key that does not exist:

mine = Hash.new{ |h,k| Hash.new(&h.default_proc) }
mine[:root] = { dive:42 }
p mine[:root][:dive]        #=> 42
p mine[:ohno][:blah][:whee] #=> {}
p mine[:root][:blah][:whee] #=> undefined method `[]' for nil:NilClass (NoMethodError)

However, you'd have to ensure that every object in your hierarchy was one of these hashes (which I explicitly failed to do for the contents of :dive, resulting in the error).


For alternative fun, you could add your own magic lookup method:

class Hash
  def divedive(*keys)
    obj = self
    keys.each do |key|
      return obj unless obj && obj.respond_to?(:[])
      obj = obj[key]
    end
    obj
  end
end

if myarray.divedive(:parent,:child).blank?
  # ...
like image 42
Phrogz Avatar answered Oct 28 '22 19:10

Phrogz