Jonathan Chow Jonathan Chow - 1 year ago 57
Ruby Question

Ruby pipe operator on nil and array

So I have been using | to merge two arrays and get unique values.

but I found out that

nil | ["foo", "bar"]
instead of
["foo", "bar"]

Can someone explain this behaviour? Ruby version is 2.2.1 if that matters.

mwp mwp
Answer Source

nil is the receiver in your example, and NilClass#| coerces the right-hand side of the expression (the argument) to a boolean value. The expression is the equivalent of nil | !!(["foo", "bar"]), and because nil is always falsey and arrays are never falsey, it reduces to false | true, which will always return true. The same is true for other non-falsey values on the right-hand side:

irb(main):001:0> nil|0
=> true
irb(main):002:0> nil|1
=> true
irb(main):003:0> nil|"foo"
=> true
irb(main):004:0> nil|true
=> true
irb(main):005:0> nil|false
=> false
irb(main):006:0> nil|nil
=> false

You can keep doing what you're doing, but if the receiver might be nil (or really anything other than an array), you should coerce it into an array to get the Array#| behavior you desire. Fortunately, there's a really neat and easy way to do that in Ruby:

[*nil] | ["foo", "bar"]

This "does the right thing" in a variety of cases:

irb(main):001:0> [*nil] | ["foo", "bar"]
=> ["foo", "bar"]
irb(main):002:0> var = "foo"
=> "foo"
irb(main):003:0> [*var] | ["foo", "bar"]
=> ["foo", "bar"]
irb(main):004:0> var = ["bar"]
=> ["bar"]
irb(main):005:0> [*var] | ["foo", "bar"]
=> ["bar", "foo"]
irb(main):006:0> var = []
=> []
irb(main):007:0> [*var] | ["foo", "bar"]
=> ["foo", "bar"]
irb(main):008:0> var = nil
=> nil
irb(main):009:0> [*var] | ["foo", "bar"]
=> ["foo", "bar"]