Stefan
Stefan

Reputation: 114138

Creating an array from variables without nil values in Ruby

What's the most idiomatic way to create an array from several variables without nil values?

Given these variables:

a = 1
b = nil
c = 3

I would like to create an array ary:

ary #=> [1, 3]

I could use Array#compact:

ary = [a, b, c].compact
ary #=> [1, 3]

But putting everything in an array just to remove certain elements afterwards doesn't feel right.

Using if statements on the other hand produces more code:

ary = []
ary << a if a
ary << b if b
ary << c if c
ary #=> [1, 3]

Is there another or a preferred way or are there any advantages or drawbacks using either of the above?


PS: false doesn't necessarily have to be considered. The variables are either truthy (numbers / strings / arrays / hashes) or nil.

Upvotes: 4

Views: 896

Answers (3)

Mac
Mac

Reputation: 1

Here is a solution that uses a hash.

With these values put in an array:

a = 1; b = nil; c = 3; d = nil; e = 10;
ary = [a, b, c, d, e]

There are two nil items in the result which would require a compact to remove both "nil" items.

However the same variables added to a hash:

a = 1; b = nil; c = 3; d = nil; e = 10;
hash = {a => nil, b => nil, c => nil, d => nil, e => nil}

There is just one "nil" item in the result which can easily be removed by hash.delete(nil).

Upvotes: 0

Stefan
Stefan

Reputation: 114138

I was hoping for a way to somehow "skip" the nil values during array creation. But after thinking about this for a while, I realized that this can't be achieved because of Ruby's way to handle multiple values. There's no concept of a "list" of values, multiple values are always represented as an array.

If you assign multiple values, Ruby creates an array:

ary = 1, nil, 3
#=> [1, nil, 3]

Same for a method taking a variable number of arguments:

def foo(*args)
  args
end

foo(1, nil, 3)
#=> [1, nil, 3]

So even if I would patch Array with a class method new_without_nil, I would end up with:

def Array.new_without_nil(*values)
  values.compact!
  values
end

This just moves the code elsewhere.

Everything is an object

From an OO point of view, there's nothing special about nil - it's an object like any other. Therefore, removing nil's is not different from removing 1's.

Using a bunch of if statements on the other hand is something I'm trying to avoid when writing object oriented code. I prefer sending messages to objects.

Regarding "advantages or drawbacks":

[...] with compact / compact!

  • creates full array and shrinks it as needed
  • short code, often fits in one line
  • is easily recognized
  • evaluates each item once
  • faster (compiled C code)

[...] with << and if statements

  • creates empty array and grows it as needed
  • long code, one line per item
  • purpose might not be as obvious
  • items can easily be commented / uncommented
  • evaluates each item twice
  • slower (interpreted Ruby code)

Verdict:

I'll use compact, might have been obvious.

Upvotes: 2

Borsunho
Borsunho

Reputation: 1157

If you are concerned about performance, best way would be probably to use destructive #compact! to avoid allocating memory for second array.

Upvotes: 4

Related Questions