Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deeply compact nested hash in ruby?

Given the following hash structure...

{
  a: nil,
  b: [],
  c: {c1: {c2: nil}},
  d: [{d1: "Value!"}],
  e: "Value!",
  f: {f1: {f2: nil, f3: "Value!"}}
}

I'd like to be able to return...

{
  d: [{d1: "Value!"}],
  e: "Value!",
  f: {f1: {f3: "Value!"}}
}

So the rules would be
1) Remove any key that points to a nil, {}, or [] value
2) Remove any key that leads to value which points to an empty value (example c: from the original hash)
3) Preserve the outer key if one or more inner keys point to a non empty value, but remove inner keys that point to an empty value. (see f: and notice that f2: is removed)

Any help would be appreciated!

like image 487
cheeseandpepper Avatar asked Oct 22 '25 16:10

cheeseandpepper


2 Answers

You could have some fun with monkey-patching the core classes involved:

class Object
  def crush
    self
  end
end

class Array
  def crush
    r = map(&:crush).compact

    r.empty? ? nil : r
  end
end

class Hash
  def crush
    r = each_with_object({ }) do |(k, v), h|
      if (_v = v.crush)
        h[k] = _v
      end
    end

    r.empty? ? nil : r
  end
end

It's an unusual thing to want to do, but if you do need it done writing a method like crush might help.

like image 64
tadman Avatar answered Oct 25 '25 09:10

tadman


This should be a one pass operation that works with nested arrays and hashes:

CRUSHABLE = [nil, [], {}].freeze
def crush(thing)
  if thing.is_a?(Array)
    thing.each_with_object([]) do |v, a|
      v = crush(v)
      a << v unless CRUSHABLE.include?(v)
    end
  elsif thing.is_a?(Hash)
    thing.each_with_object({}) do |(k,v), h|
      v = crush(v)
      h[k] = v unless CRUSHABLE.include?(v)
    end
  else
    thing
  end
end

like image 28
user160917 Avatar answered Oct 25 '25 11:10

user160917