1

I need to convert this array:

[:a, :b, :c, :d, :e]

Into this hash:

{:a=>{:b=>{:c=>{:d=>:e}}}}

Unfortunately, I'm missing the Recursion Lobe of the brain. I found BottomlessHash

# http://firedev.com/posts/2015/bottomless-ruby-hash/
class BottomlessHash < Hash
  def initialize
    super &-> h, k { h[k] = self.class.new }
  end
end

While I'm struggling to understand the "pretzel stab", it does the trick if you write it out explicitly,

bhash = BottomlessHash.new
bhash[:a][:b][:c][:d] = :e 
bhash # => {:a=>{:b=>{:c=>{:d=>:e}}}} 

However I can't figure out a way to pass arbitrary values programmatically.

store doesn't work, nor does send("[:a][:b][:c][:d]=", :e)

1
  • 1
    What should be the result if the array has 0 or 1 elements? Commented Mar 6, 2019 at 16:36

4 Answers 4

3

What send does is call a method, just one, with zero or more arguments. It can't call multiple methods at once. Your example here:

send("[:a][:b][:c][:d]=", :e)

This is trying to call a method named, literally, [:a][:b][:b][:d]= which doesn't exist, so the send fails.

Now this bit of actual Ruby code:

x[:a][:b][:c][:d] = :e

Becomes interpreted by Ruby as:

x.send(:[], :a).send(:[], :b).send(:[], :c).send(:[]=, :d, :e)

Which is just a really long, ugly way of doing what the original code did. The key here is that each [...] part represents a method call that returns something and then the next part is evaluated against that, or chained on.

Now for the original part, this pretzel-stab:

super &-> h, k { h[k] = self.class.new }

The & means "pass through this Proc as a block argument", as in for a method with the signature:

initialize(&block)

Where &block represents a block, if given, to that method, as in:

Hash.new { |h,k| h[k] = { } }

In a more basic implementation.

The -> h, k { ... } part is traditionally written as:

lambda { |h, k| ... }

Where that's probably more recognizable.

Sign up to request clarification or add additional context in comments.

Comments

2
[:a, :b, :c, :d, :e].reverse_each.inject{|h, k| {k => h}}
# => {:a=>{:b=>{:c=>{:d=>:e}}}}

Comments

1
array = [*:a..:e]
array[0..-2].reverse.reduce(array[-1]) { |b, a| { a => b } }
#  {:a=>{:b=>{:c=>{:d=>:e}}}}

Comments

1
items = [:a, :b, :c, :d, :e]

# Iterative version
def hashify(items)
  items = items.dup

  result = items.pop
  result = {items.pop => result} until items.empty?
  result
end

hashify(items) # => {:a=>{:b=>{:c=>{:d=>:e}}}}

# Recursive version
def hashify(items)
  return items.last if items.size < 2

  *head, penultimate, last = items
  hashify(head + [{penultimate => last}])
end

hashify(items) # => {:a=>{:b=>{:c=>{:d=>:e}}}}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.