Reputation: 1368
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)
Upvotes: 1
Views: 299
Reputation: 168091
[:a, :b, :c, :d, :e].reverse_each.inject{|h, k| {k => h}}
# => {:a=>{:b=>{:c=>{:d=>:e}}}}
Upvotes: 2
Reputation: 36101
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}}}}
Upvotes: 1
Reputation: 211560
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.
Upvotes: 3
Reputation: 33420
array = [*:a..:e]
array[0..-2].reverse.reduce(array[-1]) { |b, a| { a => b } }
# {:a=>{:b=>{:c=>{:d=>:e}}}}
Upvotes: 1