tim_xyz
tim_xyz

Reputation: 13491

Construct a Ruby array with conditional logic inside

How can you construct an array in Ruby which only uses a variable if it exists, and otherwise uses nil in its place. With the conditional logic inside the array constructor.

Simplified Example:

a = 1
c = 3

arr = [a, b || nil, c]

I've tried a number of different way but they are not working so I feel like I'm missing something fundamental here.

  1. (b || nil)
  2. b ? b : nil
  3. b.nil? ? nil : b

Is this possible?

Context: This array constructor is used inside a loop used by multiple different models. Some models have the b attribute and some do not, making it difficult extrapolate the logic outside.

Upvotes: 2

Views: 792

Answers (5)

mu is too short
mu is too short

Reputation: 434665

Given that your b is actually a model attribute which may or may not be supported by the current self, then b is not a variable at all, b is a method call and you're not interested in whether or not the "variable" exists, you're interested in whether or not self responds to b. Hence you want to use respond_to?:

arr = [a, respond_to?(:b) ? b : nil, c]

or perhaps:

arr = [a, respond_to?(:b, true) ? b : nil, c]

if you want to allow b to be a non-public method.

defined?(b) should also work in this case (unless b's accessor method is automatically created via method_missing) but it will be rather puzzling to anyone looking at your code. Using respond_to? to see if an object has a method/attribute would be more idiomatic.

Using respond_to? when faced with method_missing of course assumes that both method_missing and respond_to? have been overridden but that should be a relatively safe assumption.

Upvotes: 2

the Tin Man
the Tin Man

Reputation: 160551

Ugh. Don't write code like that. Instead break it into more understandable pieces:

a = 1
c = 3

b ||= nil

arr = [a, b, c]

Or, use vertical space to help the brain see what's going on:

arr = [
  a,
  b ||= 3,
  c
]

Upvotes: 0

Bartłomiej Gładys
Bartłomiej Gładys

Reputation: 4615

Yea, you can use defined? method. It return "local-variable" if variable exist, if not it will return nil.

arr = [a, defined?(b) ? b : nil, c]

Upvotes: 3

CDub
CDub

Reputation: 13354

You can do:

arr = [a, (b ||= nil), c]

Example:

a = 1
b = nil
c = 3

> arr = [a, (b ||= 33), c]
=> [1, 33, 3]

b = 2

> arr = [a, (b ||= 33), c]
=> [1, 2, 3]

If b may not be defined, you'll need to use defined?:

> arr = [a, (defined?(b) ? b : nil), c]
=> [1, nil, 3]

Upvotes: 0

Sagar Pandya
Sagar Pandya

Reputation: 9497

This will do the trick:

arr = [a, b = b || nil, c]

Example 1:

a = 1
c = 3

arr = [a, b = b || nil, c]
p arr #=> [1, nil, 3]

Example 2:

a = 1
b = 2
c = 3

arr = [a, b = b || nil, c]
p arr #=> [1, 2, 3]

Upvotes: 0

Related Questions