Ruby Hash Equivalent to Python Dict Setdefault

Ruby hash equivalent to Python dict setdefault

A Hash can have a default value or a default Proc (which is called when a key is absent).

h = Hash.new("hi")
puts h[123] #=> hi
# change the default:
h.default = "ho"

In above case the hash stays empty.

h = Hash.new{|h,k| h[k] = []}
h[123] << "a"
p h # =>{123=>["a"]}

Hash.new([]) would not have worked because the same array (same as identical object) would be used for each key.

What's Ruby's equivalent to Python's setDefault()?

Ruby has the hash.fetch method, you can use it for the same way to gracefully handle missing keys, the difference is it doesn't then store those values:

h = {}
value = h.fetch(:some_key, [])
# value is now []

Another common way we accomplish what you are doing in Ruby is this:

h[:some_key] ||= []

or I guess you can even do this if you wanted to assign that to a value:

value = h[:some_key] ||= []

Setting ruby hash .default to a list

Hash.default is used to set the default value returned when you query a key that doesn't exist. An entry in the collection is not created for you, just because queried it.

Also, the value you set default to is an instance of an object (an Array in your case), so when this is returned, it can be manipulated.

a = {}
a.default = [] # set default to a new empty Array
a[8] << 9 # a[8] doesn't exist, so the Array instance is returned, and 9 appended to it
a.default # => [9]
a[9] # a[9] doesn't exist, so default is returned

Why dict.get(key) instead of dict[key]?

It allows you to provide a default value if the key is missing:

dictionary.get("bogus", default_value)

returns default_value (whatever you choose it to be), whereas

dictionary["bogus"]

would raise a KeyError.

If omitted, default_value is None, such that

dictionary.get("bogus")  # <-- No default specified -- defaults to None

returns None just like

dictionary.get("bogus", None)

would.

Set a value in a dict only if the value is not already set

This is a bit of a non-answer, but I would say the most pythonic is the if statement as you have it. You resisted the urge to one-liner it with __setitem__ or other methods. You've avoided possible bugs in the logic due to existing-but-falsey values which might happen when trying to be clever with short-circuiting and/or hacks. It's immediately obvious that the compute function isn't used when it wasn't necessary.

It's clear, concise, and readable - pythonic.

Set a value in a dict only if the value is not already set

This is a bit of a non-answer, but I would say the most pythonic is the if statement as you have it. You resisted the urge to one-liner it with __setitem__ or other methods. You've avoided possible bugs in the logic due to existing-but-falsey values which might happen when trying to be clever with short-circuiting and/or hacks. It's immediately obvious that the compute function isn't used when it wasn't necessary.

It's clear, concise, and readable - pythonic.

Does Python have an or equals function like ||= in Ruby?

Jon-Eric's answer's is good for dicts, but the title seeks a general equivalent to ruby's ||= operator.

A common way to do something like ||= in Python is

x = x or new_value

Ruby Hash Interaction With Pushing Onto Array

Read the Ruby Hash.new documentation carefully - "if this hash is subsequently accessed by a key that doesn’t correspond to a hash entry, the value returned depends on the style of new used to create the hash".

new(obj) → new_hash


...If obj is specified, this single object will be used for all default values.

In your example you attempt to push something onto the value associated with a key which does not exist, so you end up mutating the same anonymous array you used to construct the hash initially.

the_array = []
h = Hash.new(the_array)
h['foo'] << 1 # => [1]
# Since the key 'foo' was not found
# ... the default value (the_array) is returned
# ... and 1 is pushed onto it (hence [1]).
the_array # => [1]
h # {} since the key 'foo' still has no value.

You probably want to use the block form:

new { |hash, key| block } → new_hash


...If a block is specified, it will be called with the hash object and the key, and should return the default value. It is the block’s responsibility to store the value in the hash if required.

For example:

h = Hash.new { |hash, key| hash[key] = [] } # Assign a new array as default for missing keys.
h['foo'] << 1 # => [1]
h['foo'] << 2 # => [1, 2]
h['bar'] << 3 # => [3]
h # => { 'foo' => [1, 2], 'bar' => [3] }


Related Topics



Leave a reply



Submit