Ruby hash equivalente a Python dict setdefault

En Python es posible leer una clave de hash / diccionario al mismo tiempo que se configura la clave en un valor predeterminado si no existe una.

Por ejemplo:

>>> 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'} 

¿Hay un equivalente con hashes de Ruby? Si no, ¿cuál es el enfoque idiomático en Ruby?

Un Hash puede tener un valor predeterminado o un Proc predeterminado (que se llama cuando una clave está ausente).

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

En el caso anterior el hash permanece vacío.

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

Hash.new([]) no habría funcionado porque se usaría la misma matriz (igual que el objeto idéntico) para cada clave.

No para vencer a un caballo muerto aquí, pero setDefault actúa más como fetch en un hash. No actúa de la misma manera que lo hace por defecto en un hash. Una vez que establezca el valor predeterminado en un hash, cualquier clave faltante usará ese valor predeterminado. Ese no es el caso con setDefault. Almacena el valor solo para la clave faltante y solo si no puede encontrar esa clave. Todo eso almacena la nueva pieza de par de valor clave donde difiere de la búsqueda.

Al mismo tiempo, actualmente solo hacemos lo que setDefault hace de esta manera:

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

Ruby continuó conduciendo el punto a casa:

 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' 

Pitón:

 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' 

No hay equivalente a esta función en Python. Siempre puedes usar el parche de mono para obtener esta funcionalidad:

 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' 

Pero tenga en cuenta que los parches de mono a menudo se ven como un tabú, al menos en ciertos entornos de código.

La regla de oro del parche de monos se aplica: solo porque puedas, no significa que debas.

La forma más idiomática es definir los valores predeterminados a través del constructor de hash pasando un bloque o valor adicional.

Simplemente puede pasar un bloque al constructor Hash :

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

El bloque se invocará cuando se realice un bash de acceso a una clave que no existe. Se pasará el objeto hash y la clave. Puedes hacer lo que quieras con ellos; establecer la clave en un valor predeterminado, derivar un nuevo valor de la clave, etc.

Si ya tiene un objeto Hash , puede usar el método default_proc= :

 hash = { key: 'value' } # ... hash.default_proc = proc do |hash, key| hash[key] = :default end 

Si solo desea modificar el valor devuelto por setdefault , puede expresslo a través de Hash#merge! :

Pitón:

 >>> d = {} >>> d.setdefault("k", []).append("v") >>> d {'k': ['v']} 

Rubí:

 [28] pry(main)> h = {} => {} [29] pry(main)> h.merge!(k: [:v]) { |_key, old, new| old.concat(new) } => {:k=>[:v]} [30] pry(main)> h => {:k=>[:v]}