0

I have an array eg. arr = [:a, :b, :c] and i would like to make a hash from it in the form of

[{:a => [{:b => [:c] } ] } ]
1
  • "Making a deep hash" in the title is problematic in two ways. Firstly you're making an array, not a hash, and "nested" is probably more what you're looking for than "deep". Perhaps, "Making an array of nested hashes and arrays from an array."? (All arrays contain elements (:- and "Ruby" is taken care of by the tag.) Commented Sep 8, 2016 at 18:32

3 Answers 3

3

Quick and dirty:

[{ arr.shift => [{ arr.shift => [arr.shift] }] }]

To Cary's point, this is a little less dirty:

[{ arr[0] => [{ arr[1] => [arr[2]] }] }]

Or:

enum = arr.each
[{ enum.next => [{ enum.next => [enum.next] }] }]

If you're looking for something a little more flexible, here's a recursive method that does it:

def nest(arr)
  head, *tail = arr
  return [head] if tail.empty?
  [{ head => nest(tail) }]
end

nest([:a, :b, :c]) # => [{:a=>[{:b=>[:c]}]}]
Sign up to request clarification or add additional context in comments.

8 Comments

You could also redefine the method signature: nest(head, *tail) and call it like nest(*tail).
Whoa, that first example is quick and dirty. What makes you think that your left-most arr.shift always gets evaluated first, followed by the one in the middle, before the one on the right? If a different Ruby version evaluates things in a different order, your results will change.
@tadman That's a neat idea, but the starting input is an array, so I wanted it to be compatible with that (i.e. without requiring the caller to splat it).
Aside from @David's point, isn't your quick and dirty an obtuse version of [{ arr[0] => [{ arr[1] => [arr[2] }] }] (which, incidentally, doesn't mutate arr)?
mwp: The code in a Ruby interpreter for evaluating a hash might look something like: key = evaluate_key_expr(); value = evaluate_value_expr(); add_to_hash(key, value); Some programmer might reorder the first two lines some day (so a hash value gets evaluated before the corresponding key) and it would not break most Ruby programs, but it would break your quick and dirty one.
|
1

If you don’t hesitate to mutate an input:

loop.inject([]) do |memo|
  break memo unless e = arr.pop
  [memo.empty? ? e : {e => memo}]
end

To not mutate the initial array arr, call dup on it in advance, or use iterator:

iter = arr.reverse.each
loop.inject([]) do |memo|
  break memo unless e = iter.next
  [memo.empty? ? e : {e => memo}]
end

Comments

1

You can get something close with inject:

arr.reverse.inject([]) { |memo, item| [{item => memo }] }
# => [{:a=>[{:b=>[{:c=>[]}]}]}]

and thanks to the comment by Cary, spot on:

arr.reverse.inject([]) { |memo, item| memo.empty? ? [item] : [{item => memo }] }
# => [{:a=>[{:b=>[:c]}]}]

3 Comments

arr.reverse.inject([]) { |memo, item| memo.empty? ? [item] : [{item => memo }] } #=> [{:a=>[{:b=>[:c]}]}].
@Kris, that did not display the desired result.

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.