Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby hash equivalent to Python dict setdefault

In Python it is possible to read a dictionary/hash key while at the same time setting the key to a default value if one does not already exist.

For example:

>>> d={'key': 'value'}
>>> d.setdefault('key', 'default')
'value'                                          # returns the existing value
>>> d.setdefault('key-doesnt-exist', 'default')
'default'                                        # sets and returns default value
>>> d
{'key-doesnt-exist': 'default', 'key': 'value'}

Is there an equivalent with Ruby hashes? If not, what is the idiomatic approach in Ruby?

like image 663
gabrtv Avatar asked Apr 03 '14 16:04

gabrtv


People also ask

What is Setdefault () method in dictionary?

Python Dictionary setdefault() Method The setdefault() method returns the value of the item with the specified key. If the key does not exist, insert the key, with the specified value, see example below.

Are there dictionaries in Ruby?

The Ruby Hash is an implementation of the dictionary concept, where the keys (in the key, value pair) are unique numbers. A dictionary is a general concept that maps unique keys to non-unique values.

What is a hash in ruby?

In Ruby, Hash is a collection of unique keys and their values. Hash is like an Array, except the indexing is done with the help of arbitrary keys of any object type. In Hash, the order of returning keys and their value by various iterators is arbitrary and will generally not be in the insertion order.


4 Answers

Not to beat a dead horse here, but setDefault acts more like fetch does on a hash. It does not act the same way default does on a hash. Once you set default on a hash, any missing key will use that default value. That is not the case with setDefault. It stores the value for only the one missing key and only if it fails to find that key. That whole stores the new key value pair piece is where it differs from fetch.

At the same time, we currently just do what setDefault does like this:

h = {}
h['key'] ||= 'value'

Ruby continued to drive point home:

h.default = "Hey now"
h.fetch('key', 'default')                       # => 'value'
h.fetch('key-doesnt-exist', 'default')          # => 'default'
# h => {'key' => 'value'}
h['not your key']                               # => 'Hey now'

Python:

h = {'key':'value'}
h.setdefault('key','default')                   # => 'value'
h.setdefault('key-doesnt-exist','default')      # => 'default'
# h {'key': 'value', 'key-doesnt-exist': 'default'}
h['not your key']                               # => KeyError: 'not your key'
like image 56
Bassel Samman Avatar answered Oct 23 '22 04:10

Bassel Samman


There is no equivalent to this function in Python. You can always use monkey patching to get this functionality:

class Hash

  def setdefault(key, value)
    if self[key].nil?
      self[key] = value
    else
      self[key]
    end
  end

end

h = Hash.new
h = { 'key' => 'value' }
h.setdefault('key', 'default')
# => 'value'
h.setdefault('key-doesnt-exist', 'default')
# => 'default'

But keep in mind that monkey patching is often seen as a taboo, at least in certain code environments.

The golden rule of monkey patching applies: just because you could, doesn’t mean you should.

The more idiomatic way is to define default values through the Hash constructor by passing an additional block or value.

like image 20
Konrad Reiche Avatar answered Oct 23 '22 03:10

Konrad Reiche


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.

like image 9
steenslag Avatar answered Oct 23 '22 05:10

steenslag


You can simply pass a block to the Hash constructor:

hash = Hash.new do |hash, key|
  hash[key] = :default
end

The block will be invoked when an attempt to access a non-existent key is made. It will be passed the hash object and the key. You can do anything you want with them; set the key to a default value, derive a new value from the key, etc.

If you already have a Hash object, you can use the default_proc= method:

hash = { key: 'value' }

# ...

hash.default_proc = proc do |hash, key|
  hash[key] = :default
end
like image 1
Matheus Moreira Avatar answered Oct 23 '22 05:10

Matheus Moreira