Не присваивать нулевые значения хешу

Есть ли короткая рука или лучшая практика для назначения вещей хешу, когда они равны нулю в рубине? Например, моя проблема в том, что я использую другой хеш для создания этого, и если что-то в нем равно нулю, он присваивает этому ключу ноль, а не просто оставляет его в покое. Я понимаю, почему это происходит, поэтому мое решение было:

hash1[:key] = hash2[:key] unless hash2[:key].nil?

Потому что у меня не может быть значения в has, где ключ фактически указывает на ноль. (Я предпочел бы пустой хеш, чем тот, у которого есть {:key => nil}, это не может произойти)

Мой вопрос будет в том, есть ли лучший способ сделать это? Я не хочу делать delete_if в конце заданий.


person Red    schedule 29.11.2011    source источник
comment
Ваше решение выглядит хорошо для меня. Если бы вы привели весь цикл в качестве примера, я уверен, что было бы несколько хороших способов сделать его одной строкой.   -  person Sean Vikoren    schedule 30.11.2011


Ответы (7)


немного короче, если вы отрицаете утверждение «если»

hash1[:key] = hash2[:key] if hash2[:key]   # same as   if ! hash2[:key].nil?

вы также можете выполнить сравнение в операторе &&, как это было предложено в других ответах Майкла или Марка-Андре.

Это действительно зависит от вас, что вы считаете наиболее читаемым для вас. По замыслу в Ruby всегда есть несколько способов решить проблему.

Вы также можете изменить hash2 :

hash1 = hash2.reject{|k,v| v.nil?}

hash2.reject!{|k,v| v.nil?}   # even shorter, if in-place editing of hash2

это удалит пары ключ/значение :key => nil из hash2 (на месте, если вы используете reject!)

person Tilo    schedule 29.11.2011
comment
› false.nil? =› ложь - person nroose; 30.09.2015
comment
Осторожно, reject! возвращает nil, если не было внесено никаких изменений. - person Pak; 16.09.2019

Мне нравится это лучше всего, цикл и условное переопределение в одной строке!

h1 = {:foo => 'foo', :bar => 'bar'}
h2 = {:foo => 'oof', :bar => nil}

h1.merge!(h2) { |key, old_val, new_val| new_val.nil? ? old_val : new_val }

#=> {:foo => 'oof', :bar => 'bar'}

Это заменит каждое значение в h1 значением h2, где ключи одинаковы, а значение h2 не равно нулю.

person bkempner    schedule 29.11.2011
comment
да и нет: / Если у вас есть нулевое значение в h2, а h1 пуст, он установит значение в h1 равным нулю. - person Tilo; 19.05.2015

Я не уверен, что это действительно лучше, но

hash2[:key] && hash[:key] = hash2[:key]

может работать. Обратите внимание, что это будет вести себя одинаково для false и nil, если это не то, что вам нужно.

!hash2[:key].nil? && hash[:key] = hash2[:key]

было бы лучше. Все это при условии, что :key будет произвольным значением, которое вы не можете контролировать.

person Michael Kohl    schedule 29.11.2011
comment
+1 это правильно. Но в основном делает то же самое, что и решение Red. - person Tilo; 30.11.2011
comment
Конечно, он делает то же самое. Насколько я понял вопрос, ему не понравился стиль кода, а не функциональность. - person Michael Kohl; 30.11.2011

Как насчет чего-то подобного?

hash2.each_pair do |key, value|
  next if value.nil?
  hash1[key] = value
end

Если вы выполняете только одно задание, это может сократить количество символов:

hash2[:key] && hash1[:key] = hash2[:key]

Мой первый пример также можно было бы немного побрить:

hash2.each_pair{ |k,v| v && hash1[k] = v }

Я думаю, что первое легче всего читать/понимать. Кроме того, в примерах 2 и 3 будет пропущено все, что оценивается как false (nil или false). Этот последний пример состоит из одной строки и не будет пропускать false значений:

hash2.each_pair{ |k,v| v.nil? || hash1[k] = v }
person Carl Zulauf    schedule 29.11.2011

Я считаю, что лучше всего скопировать значение nil в хэш. Если кто-то передает опцию :foo => nil, это может что-то означать и должно переопределять, например, :foo по умолчанию из 42. Это также упрощает использование параметров, которые по умолчанию должны иметь значение true, хотя в таких случаях следует использовать fetch:

opt = hash.fetch(:do_cool_treatment, true) # => will be true if key is not present

Есть много способов скопировать значения, включая nil или false.

Для одного ключа вы можете использовать has_key? вместо поиска:

hash1[:key] = hash2[:key] if hash2.has_key? :key

Для всех (или многих) ключей используйте merge!:

hash1.merge!(hash2)

Если вы хотите сделать это только для пары ключей hash2, вы можете нарезать его:

hash1.merge!(hash2.slice(:key, ...))
person Marc-André Lafortune    schedule 29.11.2011
comment
Если hash2 имеет ключи со значениями nil, merge! не будет работать, поскольку hash1 будет иметь те же самые ключи и их значения nil... именно то, чего хотел избежать OP. - person Carl Zulauf; 30.11.2011
comment
Недостатком слияния является то, что оно копирует пару ключ/значение :key =› nil в хеш1, если она существует в хеше2. Я думаю, что ОП этого не хотел. - person Tilo; 30.11.2011
comment
@Tilo: отредактировано, чтобы отразить мое мнение о том, что лучше всего копировать значения nil. - person Marc-André Lafortune; 30.11.2011

Итак, если слияние не работает, потому что вы хотите больше контроля:

hash1[:key] = hash2.fetch(:key, hash1[:key])

Это установит :key hash1 как hash2, если только он не существует. В этом случае будет использоваться значение по умолчанию (2-й аргумент для выборки), которое является ключом hash1.

person Jesse Wolgamott    schedule 29.11.2011
comment
это не сработает, если :key еще не находится в hash1 (правая сторона) - person ; 30.11.2011
comment
@RickR Ты прав... дох! ... также нужно получить с правой стороны: hash1[:key] = hash2.fetch(:key, hash1.fetch(:key, nil)) ... и это выглядит немного некрасиво - person Jesse Wolgamott; 30.11.2011

Добавьте это в свои инициализаторы hash.rb

class Hash
  def set_safe(key,val)
    if val && key
      self[key] = val
    end
  end
end

использовать

hash = {}
hash.set_safe 'key', value_or_nil
person Tim Kozak    schedule 28.05.2015