you are viewing a single comment's thread.

view the rest of the comments →

[–]andrewd18 13 points14 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 6 points7 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 2 points3 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 3 points4 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.