Project

General

Profile

Feature #11747

"bury" feature, similar to 'dig' but opposite

Added by dam13n (damien sutevski) over 2 years ago. Updated about 1 month ago.

Status:
Rejected
Priority:
Normal
Target version:
-
[ruby-core:71709]

Description

In Matz's recent Rubyconf talk, he used this example for the new 'dig' feature coming in Ruby 2.3:

# we want this
data[:users][0][:name]

# we can do this w/o nil errors
data.dig(:users, 0, :name)

What I'm proposing is a 'bury' feature that is the opposite of 'dig' in a sense. It inserts a value at an arbitrary depth, for example:

data.bury(:users, 0, :name, 'Matz')

This will create a nested hash or an array automatically at each step if it doesn't already exist, and that can be inferred from the what the user is passing (such as a symbol or string for a hash or an integer for an array). It's similar to autovivification but more powerful!

This behavior is very common, at least in my experience, so a dry method built into Ruby would be awesome!

bury_examples.rb (1 KB) bury_examples.rb briankung (Brian Kung), 05/17/2018 04:36 PM

Related issues

Has duplicate Ruby trunk - Feature #13179: Deep Hash Update MethodRejected

History

#1 [ruby-core:71714] Updated by nobu (Nobuyoshi Nakada) over 2 years ago

  • Description updated (diff)
  • Status changed from Open to Feedback

How can it know what should be created, hash, array, or struct?

#2 [ruby-core:71720] Updated by sawa (Tsuyoshi Sawada) over 2 years ago

inferred from the what the user is passing (such as a symbol or string for a hash or an integer for an array)

I don't think this is a good idea. I think it should rather depend on the class of the receiver.

{}.bury(:users, 0, :name, 'Matz') # => {:users => {0 => {:name => "Matz"}}}
[].bury(:users, 0, :name, 'Matz') # => error
{}.bury(0, 1, 2, :foo) # => {0 => {1 => {2 => :foo}}}
[].bury(0, 1, 2, :foo) # => [[nil, [nil, nil, :foo]]]

and similar for struct.

#3 [ruby-core:71805] Updated by dam13n (damien sutevski) over 2 years ago

Tsuyoshi Sawada wrote:

inferred from the what the user is passing (such as a symbol or string for a hash or an integer for an array)

I don't think this is a good idea. I think it should rather depend on the class of the receiver.

{}.bury(:users, 0, :name, 'Matz') # => {:users => {0 => {:name => "Matz"}}}
[].bury(:users, 0, :name, 'Matz') # => error
{}.bury(0, 1, 2, :foo) # => {0 => {1 => {2 => :foo}}}
[].bury(0, 1, 2, :foo) # => [[nil, [nil, nil, :foo]]]

and similar for struct.

I agree. I should clarify that I was assuming the class of the receiver (data) was known in my example. The inference I was talking about was that a buried 0 would imply an array position by default instead of a hash key. But if it was strictly determined by the receiver class, that'd still be useful.

#4 [ruby-core:71885] Updated by matz (Yukihiro Matsumoto) over 2 years ago

  • Status changed from Feedback to Rejected

It's not clear to generate either Hash, Array, or Struct (or whatever) to bury a value.
So it's better to reject now.

Matz.

#5 Updated by nobu (Nobuyoshi Nakada) over 1 year ago

#6 [ruby-core:87143] Updated by briankung (Brian Kung) about 1 month ago

matz (Yukihiro Matsumoto) wrote:

It's not clear to generate either Hash, Array, or Struct (or whatever) to bury a value.

Would it be desirable to specify the new object in a block? That would make it somewhat symmetrical to how Hash.new takes a block as a default value. For example:

[{users: ['skipped']].bury(:users, 1, :name, 'Matz') { Hash.new }
# [{:users => ['skipped', {:name => 'Matz'}]}]

{users: {0 => nil}}.bury(:users, 0, :name, 'Matz') { |next_arg| Struct.new(next_arg).new }
# {:users => {0 => #<struct name="Matz">}}
#
# If the one of the retrieved values is nil, in this case {0 => nil},
# should #bury overwrite it?

If this is okay, then it might even be nice if #dig took a block as well as a fallback value:

[].dig(1) { 'default' }
#=> "default"

Additional #bury examples have been attached as bury_examples.rb.

Also available in: Atom PDF