From: Colin Bartlett Date: 2011-03-05T02:56:10+09:00 Subject: Re: How to assign an element to a hash only if its value is not nil? On Fri, Mar 4, 2011 at 3:20 PM, Mike Moore wrote: > On Fri, Mar 4, 2011 at 3:05 AM, Robert Klemme > wrote: >> If you want to clearly communicate the intent you can add a comment or >> an assert statement to that effect.  First inserting something that is >> removed later is totally backwards. > > I'm sure there are cases where removing values from a hash is appropriate. Yes, but I think one of Robert Klemme's points was why put values into a hash only to remove them immediately. >> Plus, you might remove more than intended, >> e.g. if there are nil values inserted previously and you >> only want to avoid inserting nils during _this_ method call. > > In that case where you don't want to remove all nil values from a hash > you shouldn't call `delete_if { |k, v| v.nil? }` Which rather proves Robert Klemme's point? >> So, you proposed solution is less efficient and error prone. >> Now how is that beautiful? > Ouch. So its ugly *AND* buggy... I'll avoid the beautiful/ugly debate. (I have some sympathy with Dirac's views on the necessity of "beauty" in theories, but at the end of the day I'll usually go with whatever overall works best, even if it looks "ugly".) >  # Check for nil values on hash assignment >  # Assuming false is a valid value >  h         = {} >  h[:one]   = one if !one.nil? # local method >  h[:two]   = some_obj.two_method if !some_obj.two_method.nil? >  tmp       = expensive_format_three(some_obj, some_other_obj) >  h[:three] = tmp if !tmp.nil? >  h > >  # Delete nil values after hash assignment >  h = { >        :one   => one, # local method >        :two   => some_obj.two_method, >        :three => expensive_format_three(some_obj, some_other_obj) >      }.delete_if { |k, v| v.nil? } I'm not sure what that code does, but taking what the OP wanted to do as the starting point. > hash = {} > hash[:key] = myvalue > How to assign hash[:key] only if myvalue is not nil? > Current solution is: > hash[:key] = myvalue unless myvalue.nil? > Is there something more beautiful? Again avoiding the "beauty question" (beauty is in the eye of the beholder), that seems to me to do what the OP wants, and concisely says what it's doing. I can see four problems with first assigning to the hash even if the value is nil, and then deleting nil values from the hash. Robert Klemme pointed out that it was inefficient, and that it might remove previous wanted nil values in the hash. Also, the code is longer, and you might override (and then delete) an existing value if it has the same key as a new key & value pair in which the value is nil: see what happens to :two in the following: #ruby 1.9.1p430 (2010-08-16 revision 28998) [i386-mingw32] hhh = { :zero => 0, :one => 1, :two => 2, :three => nil, :four => nil } kv = [ [:one, 11], [:two, nil], [:three, 33], [:four, nil], [:five, 5], [:six, nil] ] h = hhh.dup puts; puts h.inspect kv.each { |k, v| h[k] = v unless v.nil? } puts "#=> #{h.inspect}" # {:zero=>0, :one=>1, :two=>2, :three=>nil, :four=>nil} #=> {:zero=>0, :one=>11, :two=>2, :three=>33, :four=>nil, :five=>5} h = hhh.dup puts; puts h.inspect kv.each { |k, v| h[k] = v } h.delete_if { |k, v| v.nil? } puts "#=> #{h.inspect}" # {:zero=>0, :one=>1, :two=>2, :three=>nil, :four=>nil} #=> {:zero=>0, :one=>11, :three=>33, :five=>5}