Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby dig set - Assign values using Hash#dig

Tags:

ruby

Basically I want to assign an array using #dig.

My has has to be like this:

hash = {
   :first => {
      :second => [1,2,3,4]
  }
}

and I would use Hash#dig

hash.dig(:first, :second) = [1,2,3,4]

How can I assign this value?

like image 514
sparkle Avatar asked Jun 17 '19 15:06

sparkle


People also ask

How do you add a value to a Hash in Ruby?

Hash literals use the curly braces instead of square brackets and the key value pairs are joined by =>. For example, a hash with a single key/value pair of Bob/84 would look like this: { "Bob" => 84 }. Additional key/value pairs can be added to the hash literal by separating them with commas.

What is .DIG in Ruby?

About Dig. Dig acts similarly to the [ ] and fetch methods. It's a method on Ruby hashes that allows for the traversing of a hash to access nested values. Like [ ], it returns nil when it encounters a missing key.


2 Answers

You can create a hash that behaves like what you want. Hash.new takes a block which is invoked whenever a key lookup fails. We can create an empty hash when that happens:

hash = Hash.new { |hash, key| hash[key] = Hash.new(&hash.default_proc) }

hash[:first][:second] = [1, 2, 3, 4]

hash # => {:first=>{:second=>[1, 2, 3, 4]}}

Note though that merely accessing an inexistent key will result in the creation of a new hash:

hash.dig(:a, :b, :c) # => {}

hash # => {:first=>{:second=>[1, 2, 3, 4]}, :a=>{:b=>{:c=>{}}}}

hash[:foo].nil? # => false
like image 97
fphilipe Avatar answered Sep 18 '22 11:09

fphilipe


I've assumed that the answer to the question I raised in a comment on the question is "yes".

One could use Enumerable#reduce (a.k.a. inject):

def undig(*keys, value)
  keys[0..-2].reverse_each.reduce (keys.last=>value) { |h,key| { key=>h } }   
end

undig(:first, :second, [1,2,3,4])
  #=> {:first=>{:second=>[1, 2, 3, 4]}} 

or recursion:

def undig(*keys, value)
  keys.empty? ? value : { keys.first=>undig(*keys.drop(1), value) }
end

undig(:first, :second, [1,2,3,4])
  #=> {:first=>{:second=>[1, 2, 3, 4]}} 
like image 36
Cary Swoveland Avatar answered Sep 20 '22 11:09

Cary Swoveland