# How to assign an element to a hash only if its value is not nil?

Discussion in 'Ruby' started by Thomas W., Mar 3, 2011.

1. ### Thomas W.Guest

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?

Thomas W., Mar 3, 2011

2. ### David J.HamiltonGuest

Excerpts from Thomas W.'s message of Thu Mar 03 09:10:26 -0800 2011:
There is an alternate way. Whether it is more beautiful or not is for you to
decide.

myvalue and hash[:hey] = myvalue

ruby-1.9.2-p136 :001 > h, m, n = {}, 3, nil
=> [{}, 3, nil]
ruby-1.9.2-p136 :003 > m and h[:m] = m
=> 3
ruby-1.9.2-p136 :004 > n and h[:n] = n
=> nil
ruby-1.9.2-p136 :005 > h
=> {:m=>3}

David J.Hamilton, Mar 3, 2011

3. ### Michael EdgarGuest

If you don't mind also overwriting the value if the value is false as =
well as if the value is nil,
you can use the common ||=3D approach:

hash[:key] ||=3D myvalue

Michael Edgar

http://carboni.ca/

Michael Edgar, Mar 3, 2011
4. ### Mike MooreGuest

You could assign the all values and then remove the just the nil
values afterward.

=A0 >> h =3D { ne =3D> 1, :two =3D> 2, :three =3D> nil, :four =3D> 4, :fi=
ve =3D> nil }
=A0 =3D> {:three=3D>nil, :four=3D>4, :five=3D>nil, ne=3D>1, :two=3D>2}
=A0 >> h[:six] =3D 6
=A0 =3D> 6
=A0 >> h[:seven] =3D nil
=A0 =3D> nil
=A0 >> h.delete_if { |k, v| v.nil? }
=A0 =3D> {:four=3D>4, :six=3D>6, ne=3D>1, :two=3D>2}

Mike Moore, Mar 3, 2011
5. ### Gary WrightGuest

I tend to use #nil? and 'unless' sparingly and using them together like =
this makes my brain work to hard.

Do you care specifically about distinguishing between nil and false? If =
not:

hash[:key] =3D myvalue if myvalue

would work. So would good old:

if myvalue
hash[:key] =3D myvalue
end

or maybe

hash.store(key, myvalue) if myvalue

if you don't like the look of the assignment.

What is your default value for the hash?
Do you need to avoid creating a key if myvalue is nil?

If nil is your default value and you don't care about extra keys, then =
just do the assignment regardless of the value of myvalue. I would even =
suggest that you might want to arrange the rest of your code so that =
this is true. The idea is to work with the grain of Hash's normal =
behavior rather than fight against it.

Gary Wright=

Gary Wright, Mar 3, 2011
6. ### Gary WrightGuest

That is different than what the OP described as it depends on the =
existing value of hash[:key] but the OP wanted the assignment =
conditional on the new value ('myvalue').

Gary Wright=

Gary Wright, Mar 3, 2011
7. ### Michael EdgarGuest

Whoa, whoops, completely misread that. Early morning e-mail, disregard.

Michael Edgar

http://carboni.ca/

existing value of hash[:key] but the OP wanted the assignment =
conditional on the new value ('myvalue').

Michael Edgar, Mar 3, 2011
8. ### Robert KlemmeGuest

Oh, this is so ugly. And also potentially inefficient since the hash
table might grow larger than needed.

Cheers

robert

Robert Klemme, Mar 3, 2011
9. ### Mike MooreGuest

Mike Moore, Mar 3, 2011
10. ### Robert KlemmeGuest

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. 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. So, your
proposed solution is less efficient and error prone. Now how is that
beautiful?

Cheers

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Robert Klemme, Mar 4, 2011
11. ### Mike MooreGuest

I'm sure there are cases where removing values from a hash is appropriate.
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? }`
Ouch. So its ugly *AND* buggy...

# Check for nil values on hash assignment
# Assuming false is a valid value
h =3D {}
h[ne] =3D one if !one.nil? # local method
h[:two] =3D some_obj.two_method if !some_obj.two_method.nil?
tmp =3D expensive_format_three(some_obj, some_other_obj)
h[:three] =3D tmp if !tmp.nil?
h

# Delete nil values after hash assignment
h =3D {
ne =3D> one, # local method
:two =3D> some_obj.two_method,
:three =3D> expensive_format_three(some_obj, some_other_obj)
}.delete_if { |k, v| v.nil? }

Mike Moore, Mar 4, 2011
12. ### Colin BartlettGuest

Colin Bartlett, Mar 4, 2011
13. ### Mike MooreGuest

My assumption from the OP's question was that he wanted to populate a
new hash so that the keys's contain non-nil values. I offered a
slightly different approach to achieve the same result. (Please note I
said you "could", not "should".) My apologies for inflicting ugly or
dangerous code on you, I assumed the circumstances and tradeoffs
necessary for using (or not using) such an approach would be obvious.