Feature #14564


`dig` opposite method

Added by nilcolor (Aleksey Blinov) almost 4 years ago. Updated almost 4 years ago.

Target version:


We have nice dig method that helps a lot.
Though we didn't have an opposite method that allows setting a value.
I know we already have these:
Both were closed because of name or lack of use cases. Let me promote the new name for this:

class Hash
  def expand(*where, value)
    where[0..-2].reduce(self) { |h, key|
      h[key] = h[key] || {}
    }[where[-1]] = value

{}.expand(:a, :b, :c, 42)                 # => {:a=>{:b=>{:c=>42}}}
{}.expand(:a, 0, :c, 42)                  # => {:a=>{0=>{:c=>42}}}
{a: {}}.expand(:a, :b, :c, 42)            # => {:a=>{:b=>{:c=>42}}}
{a: {b: nil}}.expand(:a, :b, :c, 42)      # => {:a=>{:b=>{:c=>42}}}
{a: {foo: "bar"}}.expand(:a, :b, :c, 42)  # => {:a=>{:foo=>"bar", :b=>{:c=>42}}}
{a: {b: "wat"}}.expand(:a, :b, :c, 42)    # => TypeError: no implicit conversion of Symbol into Integer

class Array
  def expand(*where, value)
    where[0..-2].reduce(self) { |a, idx|
      a[idx] = a[idx] || []
    }[where[-1]] = value

[].expand(2, 1, 3, "?")              # => [nil, nil, [nil, [nil, nil, nil, "?"]]]
[1, [0, 2], []].expand(1, 1, "BAM")  # => [1, [0, "BAM"], []]
[1, [0, 2], []].expand(2, 0, "BAM")  # => [1, [0, 2], ["BAM"]]

Use cases: working with deeply nested structures, used as parameters (params[:a][:nested][:some_id] = 42).
In general, I think it's mostly useful for Hashes. Though having this on Array may be useful as well.

Updated by shevegen (Robert A. Heiler) almost 4 years ago

I have nothing against the functionality, but I think the name .expand()
is not a good one. When I read .expand, I think of the opposite of
flatten; or it reminds me of .extend.

I don't have a better name suggestion myself, though.

Updated by zverok (Victor Shepelev) almost 4 years ago

Was already proposed as a "bury" (direct antonym to dig) and rejected: and

Matz's response:

  • "It's not clear to generate either Hash, Array, or Struct (or whatever) to bury a value. So it's better to reject now." to first and
  • "You have to come up with a better name candidate and concrete use-case." to second.

BTW, you may be interested to take a look at my experimental hm gem, which defines some declarative hash processing helpers, including bury. It, BTW, decides to generate Array on numeric bury key, and Hash on any other, but I understand that it could be too vague for some cases.

Hm({a: {foo: "bar"}}).bury(:a, :b, :c, 42).to_h
# => {:a=>{:foo=>"bar", :b=>{:c=>42}}} 

Hm({a: {b: "wat"}}).bury(:a, :b, :c, 42).to_h
# TypeError: String is not diggable

Hm([1, [0, 2], []]).bury(2, 0, "BAM").to_h # well, to_h is weird here, but works
# => [1, [0, 2], ["BAM"]]

Hm([]).bury(2, 1, 3, "?").to_h
# => [nil, nil, [nil, [nil, nil, nil, "?"]]] 

Updated by nilcolor (Aleksey Blinov) almost 4 years ago

I know about those 2 proposals. I references them in the description )

Name is a hard topic. As for types - I'd say that having same type as a receiver is enough.
So, {}.whatever_name(*) will generate nested Hashes. [].whatever_name(*) - Arrays. This will cover the majority of cases.

Names... Personally, I find bury nice ) Though a bit of horrory. expand - yeah, maybe not that good.

What about "ruin", "embed", or "melt"?

Updated by phluid61 (Matthew Kerwin) almost 4 years ago

Clearly the only accurate name for this method is store_recursive_with_autovivification

A lot of people ask for it, but I still can't see how this is a good thing to add to the language. In your personal code, sure -- maybe even in a gem -- but not the core.


Also available in: Atom PDF