pmichna
pmichna

Reputation: 4888

How to insert values in dynamically nested hash?

I have an array of strings of unknown length (but let's say up to 5). I have also an empty hash h = {} and a value.

I want to transform the array and the value to hash like this:

val = 1
h = {}
a = ['a', 'b', 'c', 'd']
# result I want:
{
  'a' => {
    'b' => {
      'c' => {
        'd' => 1
      }
    }
  }
}

What's important is that some of the keys might already exist (created in a loop iteration before). So I might have:

val = 2
h = {'a' => {'b' => {'c' => {'d' => 1}}}}
a = ['a', 'b', 'c', 'e']
# result I want:
{
  'a' => {
    'b' => {
      'c' => {
        'd' => 1,
        'e' => 2
      }
    }
  }
}

Any ideas on how to do that?

Upvotes: 0

Views: 256

Answers (1)

tadman
tadman

Reputation: 211590

Once again, inject to the rescue:

def dredge(hash, list, value = nil)
  # Snip off the last element
  *list, tail = list

  # Iterate through the elements in the path...
  final = list.inject(hash) do |h, k|
    # ...and populate with a hash if necessary.
    h[k] ||= { }
  end

  # Add on the final value
  final[tail] = value

  hash
end

This could be improved a little depending on how resilient you need it to be on things like zero-length lists and so on.

Here it is applied:

h = {}
a = ['a', 'b', 'c', 'd']
dredge(h, a, 1)
# => {"a"=>{"b"=>{"c"=>{"d"=>1}}}}

h = {'a' => {'b' => {'c' => {'d' => 1}}}}
a = ['a', 'b', 'c', 'e']

dredge(h, a, 2)
# => {"a"=>{"b"=>{"c"=>{"d"=>1, "e"=>2}}}}

Upvotes: 4

Related Questions