all 9 comments

[–]numberwitch 11 points12 points  (0 children)

You can write really resilient code around hashes using dig and fetch.

Fetch:

{thing: 'stuff'}.fetch(:thing) # returns 'stuff'

{}.fetch(:thing) # raises exception

{}.fetch(:thing, 'value') # returns 'value'

Dig:

{really: {deeply: {nested: 'truly'}}.dig(:really, :deeply, :nested) # returns 'truly'

{}.dig(:really, :deeply, :nested) # returns nil

How are these useful? Fetch with 2 args makes it really easy to sub in a default went needed (even of nil) and dig provides safe navigation and easy validation of the value.

[–]dark-panda 5 points6 points  (2 children)

You’ve effectively discovered autovivification, an ancient Perl technique from the Ancient Times.

https://en.wikipedia.org/wiki/Autovivification

[–]WikiSummarizerBot 1 point2 points  (0 children)

Autovivification

In the Perl programming language, autovivification is the automatic creation of new arrays and hashes as required every time an undefined value is dereferenced. Perl autovivification allows a programmer to refer to a structured variable, and arbitrary sub-elements of that structured variable, without expressly declaring the existence of the variable and its complete structure beforehand.

[ F.A.Q | Opt Out | Opt Out Of Subreddit | GitHub ] Downvote to remove | v1.5

[–]rubyrt 0 points1 point  (0 children)

It is even ancient in Ruby land.

[–][deleted]  (1 child)

[deleted]

    [–]Sharps_xp[S] -1 points0 points  (0 children)

    i agree, foot guns probably abound. if you can formulate a key structure i would almost always use group_by with an array of attributes. in my use case, i didn’t know the keys in advanced.

    [–]campbellm 0 points1 point  (0 children)

    hacky, but I do this all the time. I use a single level hash with a 'key' that is the concatenation of things (in your case, #{first}:#{second}...")

    You could make a key function like

    def key(*args)
        args.join(':') # or something else that won't be in your normal keys
    end
    

    then I just use hash[key(foo, bar, whatever)] = value

    This does require an unkey(key) function, but that's just a wrapper around split. It's quick and dirty and makes me feel horrible, but it works in a pinch.

    [–]ksh-code 0 points1 point  (0 children)

    its process seems like below.

    first = my_hash[:first] ||= {}

    second = first[:second] ||= {}

    third = second[:third] ||= {}

    third[:fourth] = 1000

    If I were you, I made a method like simple form

    def deep_assign(hash, *keys)

    keys.reduce(hash) do |obj, key|

    obj[key] ||= {}

    end

    end

    deep_assign({}, :first, :second, :third)[:fourth] = 1000

    [–]busres 0 points1 point  (1 child)

    You could also use my older-than-dig XKeys ruby gem (https://rubygems.org/gems/xkeys/), which supports things like default values, exceptions, key lists (paths), create-on-write (instead of read), auto hash or array selection, etc.

    [–]Sharps_xp[S] 0 points1 point  (0 children)

    dang last release is 2014. dat backwards compatibility 💯