Ruby - elegantly convert variable to an array if not an array already
Given an array, a single element, or nil, obtain an array - the latter two being a single element array and an empty array respectively.
I mistakenly figured Ruby would work this way:
[1,2,3].to_a #= [1,2,3] # Already an array, so no change
1.to_a #= [1] # Creates an array and adds element
nil.to_a #= [] # Creates empty array
But what you really get is:
[1,2,3].to_a #= [1,2,3] # Hooray
1.to_a #= NoMethodError # Do not want
nil.to_a #= [] # Hooray
So to solve this, I either need to use another method, or I could meta program by modifying the to_a method of all classes I intend to use - which is not an option for me.
So a Method it is:
result = nums.class == "Array".constantize ? nums : (nums.class == "NilClass".constantize ? [] : ([]<<nums))
The problem is that it is a bit of a mess. Is there an elegant way of doing this? (I would be amazed if this is the Ruby-ish way to solve this problem)
What applications does this have? Why even convert to an array?
In Rails' ActiveRecord, calling say, user.posts
will either return an array of posts, a single post, or nil. When writing methods which work on the results of this, it is easiest to assume that the method will take an array, which may have zero, one, or many elements. Example method:
current_user.posts.inject(true) {|result, element| result and (element.some_boolean_condition)}
[*foo]
or Array(foo)
will work most of the time, but for some cases like a hash, it messes it up.
Array([1, 2, 3]) # => [1, 2, 3]
Array(1) # => [1]
Array(nil) # => []
Array({a: 1, b: 2}) # => [[:a, 1], [:b, 2]]
[*[1, 2, 3]] # => [1, 2, 3]
[*1] # => [1]
[*nil] # => []
[*{a: 1, b: 2}] # => [[:a, 1], [:b, 2]]
The only way I can think of that works even for a hash is to define a method.
class Object; def ensure_array; [self] end end
class Array; def ensure_array; to_a end end
class NilClass; def ensure_array; to_a end end
[1, 2, 3].ensure_array # => [1, 2, 3]
1.ensure_array # => [1]
nil.ensure_array # => []
{a: 1, b: 2}.ensure_array # => [{a: 1, b: 2}]
With ActiveSupport (Rails): Array.wrap
Array.wrap([1, 2, 3]) # => [1, 2, 3]
Array.wrap(1) # => [1]
Array.wrap(nil) # => []
Array.wrap({a: 1, b: 2}) # => [{:a=>1, :b=>2}]
If you are not using Rails, you can define your own method similar to the rails source.
class Array
def self.wrap(object)
if object.nil?
[]
elsif object.respond_to?(:to_ary)
object.to_ary || [object]
else
[object]
end
end
end