Assign variable only if not nil
Asked Answered
S

9

16

I have @obj.items_per_page, which is 20 at the beginning, and I want the method below to assign value to it only if many_items is not nil:

def fetch_it_baby (many_items = nil)
    @obj.items_per_page = many_items

With the code above, even if many_items is nil, @obj.items_per_page remains at 20. Why? And is that "good" coding? Shouldn't I use something like

@obj.items_per_page = many_items || @obj.items_per_page

Or is there a third way? I don't feel completely comfortable with either way.

Steatite answered 1/10, 2013 at 10:47 Comment(0)
V
23

The style I generally see looks like this:

@obj.items_per_page = many_items if many_items

This uses the inline conditional, while avoiding negative or double-negative conditions.

Veliger answered 1/10, 2013 at 10:53 Comment(1)
Is there a more elegant way to say the same? Like some crazy attribution signs? It would me nice to have something like @obj.items_per_page if= many_itemsBrookins
A
16

I suggest the following as it makes it clear that you have a default value for the assignment in case the caller did not specify many_items in the call:

def function(argument = nil)
  variable = argument || 20
  ...
end

However, since you specified that the assignment takes places only if the value is not nil then you'll need to check for the nil value otherwise you will miss the assignment if the value was false. If you really need that case then the solution is more long-winded:

def function(argument = nil)
  variable = argument.nil? ? 20 : argument
  ...
end
Aviate answered 1/10, 2013 at 10:59 Comment(0)
E
12

You can use &&= (in the same way as ||= is used to assign only if nil or false)

> a = 20    # => 20 
> a &&= 30  # => 30
> a         # => 30
> a = nil   # => nil
> a &&= 30  # => nil
> a = false # => false
> a &&= 30  # => false
> a = {}    # => {}
> a &&= 30  # => 30

remember though

> a = 30      # => 30
> a &&= nil   # => nil
> a &&= false # => nil
> b &&= 3     # => nil
Eloquent answered 24/7, 2015 at 21:34 Comment(2)
foobar &&= 3 returns nil here. might this have changed since 2015?Creole
Nope, it behaves as expected, as foobar = foobar && 3. The problem is that I was answering to "Assign variable only if variable is not nil", whereas the question implied "Assign variable only if value is not nil".Eloquent
M
10

Even if many_items is nil @obj.items_per_page remains at 20

That sounds like whatever class @obj is has a custom modifier method items_per_page= that only updates the value if the new value is not nil. This is not standard Ruby. For example, given this definition:

class Demo
  attr_accessor :items_per_page
end

I get this behavior:

irb(main):005:0>     demo = Demo.new           #=> #<Demo:0x007fb7b2060240>
irb(main):006:0>     demo.items_per_page = 20  #=> 20
irb(main):007:0>     demo.items_per_page       #=> 20
irb(main):008:0>     demo.items_per_page = nil #=> nil
irb(main):009:0>     demo.items_per_page       #=> nil

As for your example, I would probably write it this way:

@obj.items_per_page = many_items unless many_items.nil?
Murcia answered 1/10, 2013 at 10:59 Comment(0)
A
4

For Rails you can also use presence as described here

region = params[:state].presence || params[:country].presence || 'US'
Aplomb answered 12/12, 2019 at 12:6 Comment(0)
C
0

new-alexandria's answer is my go-to, but another "third way" would be to use a ternary:

class Demo
  attr_accessor :items_per_page
end

many_items = 100

@obj = Demo.new
@obj.items_per_page = 20  #=> 20
@obj.items_per_page = !many_items.nil? ? 30 : nil   #=> 30
Claudicant answered 9/2, 2017 at 23:50 Comment(0)
D
0

I am using Rails and I have a similar need.

You can define a method on your model:

class Gift < ApplicationRecord
  def safe_set(attribute, value)
    return if value.nil?
    send("#{attribute}=", value)
  end
end

So you can do

g = Gift.new
g.colour = 'red'
g.safe_set(:colour, nil)
g.colour  -> 'red'
g.safe_set(:colour, 'green')
g.colour  -> 'green'
Dieback answered 4/7, 2019 at 14:53 Comment(0)
L
0

We have one more method in rails that can help to remove values that are nil.

compact

This method can be used with an array, hash. Returns the same data removing all values that are nil

Eg :

array.compact

Further reference:

https://apidock.com/ruby/v1_9_3_392/Array/compact

Lorimer answered 19/5, 2021 at 5:54 Comment(0)
G
0

If many items is a variable the if/unless versions above are the best. But if many_items is actually a method you don't want to evaluate multiple times I find the following useful.

 @obj.items_per_page = many_items.presence || @obj.items_per_page 

This isn't quite what you want since it won't assign empty strings or other non-nil but non-present values but most of the time I want to do this kind of thing this works for me (it does create a self-assignment when the value is nil/non-present so only use it if your setters are idempotent).

Glynda answered 4/4, 2022 at 17:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.