cozyconemotel
cozyconemotel

Reputation: 1161

Initialize an Array with optional elements depending on conditions

What is the common way when you want to initialize an Array with optional elements in ruby/rails?

Right now I do

array = []
array << "1st element"
array << "2nd element" if flag?
array << "3rd element"

but It feels very verbose and I am guessing it could be slower than when initialized altogether

I'd like to know if there is something like,

array = [
  "1st element",
  "2nd element" if flag?,
  "3rd element",
]
#=> ["1st element", "3rd element"]

Upvotes: 2

Views: 1136

Answers (5)

Stefan
Stefan

Reputation: 114138

You can achieve this by using the splat operator *:

array = [
  "1st element",
  *("2nd element" if flag?),
  "3rd element"
]

This works because *"2nd element" returns the string and *nil effectively removes itself:

["1st element", *"2nd element", "3rd element"]
#=> ["1st element", "2nd element", "3rd element"]

["1st element", *nil, "3rd element"]
#=> ["1st element", "3rd element"]

Note that * attempts to call to_a so this might have undesired effects if your objects implements that method: (this is Time#to_a)

["1st element", *Time.now, "3rd element"]
#=> ["1st element", 56, 43, 11, 15, 10, 2020, 4, 289, true, "CEST", "3rd element"]

Upvotes: 0

Cary Swoveland
Cary Swoveland

Reputation: 110675

I would emphasize clarity and generality.

a = [1,2,3]
b = [4,5,6]
c = [7,8,9]
def concat_some(*arr)
  arr.reduce([]) { |ar,(a,flag)| flag ? ar.concat(a) : ar }
end
concat_some([a,true], [b,false], [c,true])
  #=> [1, 2, 3, 7, 8, 9]
concat_some([a,false], [b,true], [c,false])
  #=> [4, 5, 6]

or

def concat_for_indices(*arr, indices)
  indices.reduce([]) { |a,i| a.concat(arr[i]) }
end
concat_for_indices(a,b,c,[1,2])
  #=> [4, 5, 6, 7, 8, 9]
concat_for_indices(a,b,c,[2])
  #=> [7, 8, 9] 
concat_for_indices(a,b,c,[])
  #=> [] 
concat_for_indices(a,b,c,[0,1,2])
  #=> [1, 2, 3, 4, 5, 6, 7, 8, 9]

or

def concat_except_for_indices(*arr, indices)
  ((0..arr.size-1).to_a - indices).reduce([]) { |a,i| a.concat(arr[i]) }
end
concat_except_for_indices(a,b,c, [0])
  #=> [4, 5, 6, 7, 8, 9] 
concat_except_for_indices(a,b,c, [1,2])
  #=> [1, 2, 3] 
concat_except_for_indices(a,b,c, [])
  #=> [1, 2, 3, 4, 5, 6, 7, 8, 9] 
concat_except_for_indices(a,b,c, [0,1,2])
  #=> []

Upvotes: 1

max
max

Reputation: 101811

While this doesn't quite do it at initialization you can always insert the elements afterwards if you know the index:

array = ["1st element", "3rd element"]
array.insert(1, "2nd element") if flag?

Or a bit more elegantly:

array = ["1st element", "3rd element"].tap { |a| a.insert(1, "2nd element") if flag? }

The advantage here being that it does not compact the array in case you actually intend to have nil values.

Upvotes: 1

Emu
Emu

Reputation: 5895

Though it's not a good idea, but you can do something like this in ruby/rails:

flag = true
["1st element", eval("'2nd element' if flag"), "3rd element"].compact 
# output: ["1st element", "2nd element", "3rd element"]

Upvotes: 0

Roc Khalil
Roc Khalil

Reputation: 1385

I'm not sure if this is a good way of doing it, but here's my suggestion:

array = [
  "1st element",
  flag? ? "2nd element" : nil,
  "3rd element"
].compact

.compact will remove all the nil values from your array. This will be problematic if you might have nil values.

Upvotes: 3

Related Questions