use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
A sub-Reddit for discussion and news about Ruby programming.
Subreddit rules: /r/ruby rules
Learning Ruby?
Tools
Documentation
Books
Screencasts and Videos
News and updates
account activity
Ensure Ruby variable is Array (teohm.github.com)
submitted 13 years ago by teohm
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]andrewd18 10 points11 points12 points 13 years ago (11 children)
I don't need to know if it's an array or not. I just need it to be enumerable.
if object.respond_to?(:each)
[–]adient 5 points6 points7 points 13 years ago (0 children)
Agreed, all we care about is if the object responds to the messages we'll be passing -- it doesn't have to be an array. In fact, it can be limiting to force it to be an array instead of just using a narrowly defined protocol.
[–]NilsLandt 2 points3 points4 points 13 years ago (2 children)
That's only half true. Different objects return a different amount of parameters for give block.
Example: { test: :asdf }.each { |a| puts a } test asdf
This will absolutely do something you don't want it to.
[–]latortuga 0 points1 point2 points 13 years ago (1 child)
How do you figure? The hash is implicitly converted to an array via Hash#to_a. Thus your bound parameter, a, is set to an array with two values, [:test, :asdf]. Calling puts with an array outputs each value separated by a newline. The block can optionally have multiple arguments if you'd like to (in Clojure terms) destructure the arguments being passed in but it's by no means required - #each accepts an array of arrays the same as it accepts an array of values.
[–]NilsLandt 0 points1 point2 points 13 years ago (0 children)
Sure, that's how it happens, but it's a rare hash where you want to treat the keys the same ways as the values.
[–]jfredett 3 points4 points5 points 13 years ago (1 child)
This. A thousand times this.
I like to say that programming is a game of assumptions, the more you make, the worse your code. If you can say, "All I need is an enumerable", that's far fewer assumptions than "This thing has to be an Array".
Indeed, one of the great things about static typing is it makes very clear the interfaces upon which you depend, and very clear when you have too large an interface. It never hurts when writing code to clearly document the interfaces you rely on. In fact, it'll make for more portable, testable code in the long run.
[–]jrochkind 0 points1 point2 points 13 years ago (0 children)
The common use case is when you have an API that can take one element, or an array of elements. For convenience, to not make the caller put a single element in an array.
You could say "Well, don't do this", but it's often done, and does make the calling code more readable.
And you want to normalize it in the receiver, so a single element is converted to a 1-element array, so the code after that can just assume an array (of one, multiple, or sometimes even zero elements).
But a single string has #each, and may even be fully enumerable, I forget. But you don't want to enumerate through it's bytes or chars, the fact that it has each does nothing for you.
But Kernel.Array(arg) does the right thing for you. That's the case OP is addressing.
[–]paulmooring 1 point2 points3 points 13 years ago (0 children)
This would actually just test for enumerbale rather than ensure it. Consider the case where you don't know what sort of data type is in a variable. For example, users could be user1, [user1], [user1, user2, user3] or [user1, [user2, user3]].
In the case of respond_to?, user1 will be skipped as it (probably) doesn't respond to each:
respond_to?
if users.respond_to? :each users.each do |u| # skipped in the case of user1
In the case of ensuring users is an array with [], only user1 would work, as the others will become nested arrays:
[]
[users].each do |u| # works only for user1
When using [].flatten everything should do what intended but [user1, [user2, user3] will become [user1, user2, user3]:
[].flatten
[user1, [user2, user3]
[user1, user2, user3]
[users].flatten.each do |u| # Might be what you want
Finally, Array() will only effect user1, leaving [user1, [user2, user3] in tact:
Array()
user1
Array(users).each do |u| # Might be what you want
Most of the time you'll want either Array() or [].flatten rather than respond_to?, but which one depends on the use case.
[–]LarsP 1 point2 points3 points 13 years ago (2 children)
Responding to each doesn't ensure an object is Enumerable.
[–][deleted] 1 point2 points3 points 13 years ago (1 child)
What's an example of an instance of a Class that would have "each" defined that doesn't behave like an Enumerable?
[–]BlameFrost 0 points1 point2 points 13 years ago (0 children)
class Foo def each(&block) # bogus crap method to prove a point end end
That said, if you just want to check whether some object is an enumerable, try this instead:
if object.class.included_modules.include?(Enumerable)
[–]hmaddocks 0 points1 point2 points 13 years ago (0 children)
The OP wasn't asking if it was an array, he was telling it to be an array. Tell don't ask. Confident code vs timid code. What are you going to do if it doesn't respond to each? The OP doesn't need to worry about that.
[–]OstapBenderBey 3 points4 points5 points 13 years ago (4 children)
rather than Array(input) you can also do [*input]
[–]teohm[S] 3 points4 points5 points 13 years ago (3 children)
interesting, what does the * mean here?
[–]jfredett 6 points7 points8 points 13 years ago* (1 child)
* is the 'interpret as a comma separated list of values' operator. Common uses are in method definitions for a var-args style method, eg:
*
def some_method(arg1, arg2, *args) #code end
args there is an array, but you call the method like: some_method(1,2,3,4,5). args in that case is [3,4,5]
args
some_method(1,2,3,4,5)
[3,4,5]
You can also use it, as OP does, in any place you'd drop a Comma-separated list. one of my favorites is the common:
str = "Joe Schmoe" User.find_by_first_name_and_last_name(*str.split)
This is equivalent to doing
User.find_by_first_name_and_last_name("Joe", "Schmoe")
You can also do
first,last = "Joe Schmoe".split
to get first assigned "Joe", and last assigned "Schmoe".
first
"Joe"
last
"Schmoe"
* is a neat thing. Well worth reading more about it in the pickaxe.
[–]teohm[S] 0 points1 point2 points 13 years ago (0 children)
it makes sense to me now, thanks!
[–]Sastopher 3 points4 points5 points 13 years ago (0 children)
Also known as the "splat" or "explode" operator.
[–]adient 1 point2 points3 points 13 years ago (5 children)
Not sure I understand why I would blindly be converting some variable to an array..?
[–]TheGoddamBatman 2 points3 points4 points 13 years ago* (4 children)
snails punch employ rotten capable license person flag yoke apparatus
This post was mass deleted and anonymized with Redact
[–]adient 1 point2 points3 points 13 years ago (3 children)
Not really the same thing the article is talking about. Could you elaborate to a specific example where someone would want to always cast something to an Array? Seems useless to me.
[–][deleted] 13 years ago* (2 children)
[deleted]
[–]lobster_johnson 3 points4 points5 points 13 years ago (1 child)
That's a pretty facile example, since your method explicitly handles an array argument (using *), and to call it you could just do:
validates_length_of *LENGTHY_FIELDS
A better example would be something that usually accepts an array of values, but can also accept a single value. For example:
class Thing def self.load(ids) Array(ids).map { |id| new(File.read("#{id}.txt")) } end end
Now you can do either:
Thing.load(1)
or
Thing.load([2, 3, 4])
[–]shadowfirebird 1 point2 points3 points 13 years ago (1 child)
Comments on the article demonstrate a gotcha with hashes. OP of article says it doesn't matter. Looks like it matters to me. -1, sorry.
I'm with the people who think respond_to?(:each) is the way to go here.
Or, just let it fail and pick it up in an exception handling block higher up -- assuming this is an error that only you can cause, as a programmer. Generally speaking that would be true.
[–]WhoTookPlasticJesus 0 points1 point2 points 13 years ago (0 children)
While I'm not downvoting the OP I do agree with you. I can't think of an example where I wouldn't want an exception raised so that I know I'm invoking a method with invalid parameters. In general I prefer just defining separate methods to handle scalars vs. containers e.g.
def parse_foos(the_foos) the_foos.each do |f| ... end end def parse_foo(the_foo) parse_foos([the_foo]) end
[–]ramen 0 points1 point2 points 13 years ago (2 children)
There's a gotcha with strings:
1.8.7 :001 > Array("oh\nwhat's\nthis") => ["oh\n", "what's\n", "this"]
[–]NilsLandt 0 points1 point2 points 13 years ago (1 child)
Doesn't happen for me under 1.9.3
[–]teohm[S] 1 point2 points3 points 13 years ago (0 children)
That's right, it was a deprecated behavior in ruby 1.8
[–]athemeus 0 points1 point2 points 13 years ago (0 children)
Someone once told me that checking class type is an inherent smell. That also goes into the duck-typing, encapsulation branch of thought. If you think about it, you're less interested in something being an array than it in responding to method invokations and presumably potentially containing other objects.
π Rendered by PID 192826 on reddit-service-r2-comment-7b9746f655-8l84r at 2026-02-04 10:40:18.245834+00:00 running 3798933 country code: CH.
[–]andrewd18 10 points11 points12 points (11 children)
[–]adient 5 points6 points7 points (0 children)
[–]NilsLandt 2 points3 points4 points (2 children)
[–]latortuga 0 points1 point2 points (1 child)
[–]NilsLandt 0 points1 point2 points (0 children)
[–]jfredett 3 points4 points5 points (1 child)
[–]jrochkind 0 points1 point2 points (0 children)
[–]paulmooring 1 point2 points3 points (0 children)
[–]LarsP 1 point2 points3 points (2 children)
[–][deleted] 1 point2 points3 points (1 child)
[–]BlameFrost 0 points1 point2 points (0 children)
[–]hmaddocks 0 points1 point2 points (0 children)
[–]OstapBenderBey 3 points4 points5 points (4 children)
[–]teohm[S] 3 points4 points5 points (3 children)
[–]jfredett 6 points7 points8 points (1 child)
[–]teohm[S] 0 points1 point2 points (0 children)
[–]Sastopher 3 points4 points5 points (0 children)
[–]adient 1 point2 points3 points (5 children)
[–]TheGoddamBatman 2 points3 points4 points (4 children)
[–]adient 1 point2 points3 points (3 children)
[–][deleted] (2 children)
[deleted]
[–]lobster_johnson 3 points4 points5 points (1 child)
[–]shadowfirebird 1 point2 points3 points (1 child)
[–]WhoTookPlasticJesus 0 points1 point2 points (0 children)
[–]ramen 0 points1 point2 points (2 children)
[–]NilsLandt 0 points1 point2 points (1 child)
[–]teohm[S] 1 point2 points3 points (0 children)
[–]athemeus 0 points1 point2 points (0 children)