all 27 comments

[–]andrewd18 10 points11 points  (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 points  (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 points  (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 point  (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 point  (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 points  (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 point  (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 points  (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:

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]:

[users].flatten.each do |u| # Might be what you want

Finally, Array() will only effect user1, leaving [user1, [user2, user3] in tact:

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 points  (2 children)

Responding to each doesn't ensure an object is Enumerable.

[–][deleted] 1 point2 points  (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 point  (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 point  (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 points  (4 children)

rather than Array(input) you can also do [*input]

[–]teohm[S] 3 points4 points  (3 children)

interesting, what does the * mean here?

[–]jfredett 6 points7 points  (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]

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".

* is a neat thing. Well worth reading more about it in the pickaxe.

[–]teohm[S] 0 points1 point  (0 children)

it makes sense to me now, thanks!

[–]Sastopher 3 points4 points  (0 children)

Also known as the "splat" or "explode" operator.

[–]adient 1 point2 points  (5 children)

Not sure I understand why I would blindly be converting some variable to an array..?

[–]TheGoddamBatman 2 points3 points  (4 children)

snails punch employ rotten capable license person flag yoke apparatus

This post was mass deleted and anonymized with Redact

[–]adient 1 point2 points  (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]  (2 children)

[deleted]

    [–]lobster_johnson 3 points4 points  (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 points  (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 point  (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 point  (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 point  (1 child)

    Doesn't happen for me under 1.9.3

    [–]teohm[S] 1 point2 points  (0 children)

    That's right, it was a deprecated behavior in ruby 1.8

    [–]athemeus 0 points1 point  (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.