Ruby hash эквивалент Python dict setdefault

В Python можно прочитать словарь/хеш-ключ, в то же время устанавливая ключ по умолчанию, если он еще не существует.

Например:

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

Есть ли эквивалент с хэшами Ruby? Если нет, то каков идиоматический подход в Ruby?

Ответ 1

A Hash может иметь значение по умолчанию или значение по умолчанию Proc (которое вызывается, когда ключ отсутствует).

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

В приведенном выше случае хэш остается пустым.

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

Hash.new([]) не работал бы, потому что для каждого ключа будет использоваться тот же массив (тот же, что и у идентичного объекта).

Ответ 2

Не бить мертвую лошадь здесь, но setDefault больше похож на fetch на хеш. Он не действует так же, как по умолчанию делает хэш. После того, как вы установите значение по умолчанию для хеша, любой недостающий ключ будет использовать это значение по умолчанию. Это не относится к setDefault. Он сохраняет значение только для одного отсутствующего ключа и только если ему не удается найти этот ключ. В этом магазине новая часть пары ключей ценности находится там, где она отличается от выборки.

В то же время мы в настоящее время просто делаем то, что setDefault делает это:

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

Руби продолжал двигаться домой:

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'

Ответ 3

В Python нет эквивалента этой функции. Вы можете всегда использовать патч обезьяны, чтобы получить эту функциональность:

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'

Но имейте в виду, что исправление обезьян часто рассматривается как табу, по крайней мере, в определенных средах кода.

Золотое правило патчей обезьян применяется: только потому, что вы могли, не означает, что вы должны.

Более идиоматичным способом является определение значений по умолчанию с помощью конструктора Hash путем передачи дополнительного блока или значения.

Ответ 4

Вы можете просто передать блок конструктору Hash:

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

Блок будет вызываться, когда будет предпринята попытка получить доступ к несуществующему ключу. Он будет передан хэш-объект и ключ. Вы можете делать с ними все, что хотите; установите ключ в значение по умолчанию, выведите новое значение из ключа и т.д.

Если у вас уже есть объект Hash, вы можете использовать метод default_proc=:

hash = { key: 'value' }

# ...

hash.default_proc = proc do |hash, key|
  hash[key] = :default
end